feat(ai-review): lock Claude→Cursor→free model cascade - #52
feat(ai-review): lock Claude→Cursor→free model cascade#52Hussam Aldarwish (eduly-haldarwish) wants to merge 10 commits into
Conversation
Prefer the execution-log model when present; otherwise use steps.models.outputs.primary instead of a second Sonnet literal. Refs #51
claude/cursor/claude-* hangs Claude Code CLI (aborted_streaming) even when /v1/messages succeeds, so a Claude-off cascade never reached free. Cap the chain at three entries to match the CLI limit. Refs #51
Repair was skipped when claude-code-action failed on missing structured_output without exporting session_id. Recover it from the execution snapshot. Schema stages fall back to structured_output free models only — Cursor has no SO and can retract schema mid-stream. Refs #51
There was a problem hiding this comment.
✅ PASS
1 P2 / 1 P3 finding(s) noted — non-blocking.
Confidence: 95 · Merge risk: low
P0: 0 · P1: 0 · P2: 1 · P3: 1
Model: claude/claude-sonnet-5
Re-run this job if you need another review pass.
P0 — Blockers
None.
P1 — Should Fix
None.
P2 — Nice to Have
- Duplicated locked-model/cascade strings between
ai-review/action.ymlandai-qa/action.yml(ai-review/action.yml:437-440,ai-qa/action.yml:299-301). This is an explicitly accepted tradeoff per the design doc ("no shared package," with a "keep in sync" comment present in the shipped code), so it's not a defect — just a spot future edits could drift on since nothing automated enforces the two lists staying aligned.
P3 — Nits
- The free-tier fallback lists (
oc/nemotron-3.5-lightning-free,oc/deepseek-v4-flash-free,auto/best-free) are repeated as literal strings acrossHAIKU_FALLBACK,SONNET_FALLBACK/FREE_SO, andOPUS_FALLBACKinside the same file (ai-review/action.yml:437-440) — cosmetic only,FREE_SOis already reused for two of the three.
Strengths
- Model-cascade lock is complete and consistent. Directly verified
ai-review/action.yml'sinputs:block (lines 16-231) no longer definessonnet-model/opus-model/haiku-model, andai-qa/action.ymlno longer definesqa-model(confirmed viagit diff— 3 removal points: input def, a stale comment, and the--modelflag usage). No remaining workflow/README references to any of the four removed inputs. - Cascade ordering matches issue #51 and the design doc exactly: Claude primary → Cursor
composer-2.5(excluded from every--json-schema/structured-output stage, matching the documented "no Cursor model advertisesstructured_output" constraint) → pinned OpenCode free models →auto/best-freeas last hedge.auto/*never appears as a primary. All fallback lists stay within the CLI's 3-entry--fallback-modelcap. session_idrecovery bugfix is correctly wired end-to-end.retry_budget's new jq-based recovery (falling back from env, to snapshotinitentries, toresultentries) andreview_repair'sif:/--resumewere both switched consistently tosteps.retry_budget.outputs.session_id— no drift between the two references.- Model attribution ("what actually ran") is correctly threaded through Publish in both actions.
resolveModelUsed(ai-review) checks retry → repair → review logs in priority order before falling back to the routed primary;modelFromExecutionLog(ai-qa) reads the review log and only falls back to the primary whenQA_OUTPUTis truthy (avoiding false attribution when review never produced output). Both are covered by direct unit tests (publish.test.js,metrics.test.js,report-footer.test.js). unescapeLiteralNewlinesfix is narrowly scoped and tested — only triggers when the literal-\ncount meets/exceeds the real-newline count, avoiding accidental mangling of legitimate backslash-n sequences in review text.- Docs (
docs/plan.md, the cascade design doc, both READMEs) are fully scrubbed of stale model-routing/model-input descriptions and now accurately describe the locked cascade, matching the shipped code. - Test coverage for all new/changed logic is present:
report-footer.js(new),metrics.js'sresolveModelUsed, andpublish.js's footer/unescape helpers all have accompanying passing-shaped unit tests; this is CI/config wiring rather than a critical runtime path, so the existing coverage is adequate.
Intent Alignment
[Intent] ✅ Aligned — All 5 tasks from linked issue #51 are directly verified in code (not just inferred from docs):
- Publish banner with model + re-run hint — shipped in both actions, tested.
- Resolve actual model from execution logs into Publish — shipped in both actions, tested.
- Remove
sonnet-model/opus-model/haiku-modelinputs, hardcode the Claude→Cursor→free cascade — confirmed via direct read of theinputs:block. - Remove
qa-model, apply the same cascade+footer pattern toai-qa— confirmed viagit diff. - Scrub stale docs — confirmed via full diffs of
docs/plan.md, the design doc, and both READMEs.
No scope creep beyond the issue's task list; no missing requirement.
Testing
Test execution was skipped per review policy (static review only). The PR's checked test-plan item (node --test ... 285 passing) is unverifiable from this session but is structurally plausible — direct counting of test() blocks across the referenced suites (including the two new ai-qa/lib/report-footer.test.js cases) is consistent with that scale. The two unchecked live-gateway verification items are inherently unverifiable via static review and correctly left unchecked by the author.
Model: claude-sonnet-5
Re-run this job if you need another review pass.
Summary
ai-reviewandai-qa(remove model inputs): Claude primary → Cursor if Claude is blocked → pinned OpenCode free models →auto/best-free.Test plan
node --test ai-review/lib/*.test.js ai-qa/lib/*.test.js(285 passing)Made with Cursor
🤖 AI Review — checklist verification
Last updated by ai-review · verdict: pass.