Skip to content

perf(review): overlap the completeness judge with the fleet, make its PASS intent-sticky, cap 6 - #360

Merged
norvalbv merged 2 commits into
mainfrom
benjinorval/review-latency-prewarm
Aug 7, 2026
Merged

perf(review): overlap the completeness judge with the fleet, make its PASS intent-sticky, cap 6#360
norvalbv merged 2 commits into
mainfrom
benjinorval/review-latency-prewarm

Conversation

@norvalbv

@norvalbv norvalbv commented Aug 6, 2026

Copy link
Copy Markdown
Owner

What

Three latency/cost changes to the ship gate chain, driven by the gate telemetry (~/.devkit/telemetry/gate-events.jsonl): review is ~95% of a ship attempt's wall clock, the correctness lens split dominates the fleet makespan, and completeness (straight opus, mean ~4 min, max 30 min) ran serially at commit-msg on every landed attempt.

  1. Review concurrency default 3 → 6 (timing.mts). The lens split schedules ~8 judge tasks on a backend commit; at 6 they run in at most two waves, so the fleet makespan approaches the slowest single lens instead of a 3-slot packing. The bound is subscription slots, not CPU/memory — if this ever needs lowering, watch judge timeout rates, not RSS.

  2. Parallel completeness prewarm on the ship path (husky-block.mts review fragment). The sc-1442 composed-message file already reaches pre-commit, so the review fragment now launches guard-review completeness alongside the fleet and applies the commit-msg fragment's exact exit contract, just earlier. A confident PASS lands in the shared verdict store (merge-under-lock, so concurrent writers are safe) and the commit-msg gate re-judges it as a cache hit. If the fleet blocks the commit, the judge is killed rather than awaited. Interactive commits (no message file) are unchanged; devkit review is excluded (it exports the same env var as its reviewer intent file). normalizeCommitMessage() mirrors git's cleanup=whitespace so the ship temp file and git's cleaned COMMIT_EDITMSG compute identical cache keys.

  3. Intent-sticky completeness PASS. The gate judges the message's claims against the delivered change, so a retry whose diff was reshaped to satisfy another reviewer — same branch, same message — is not re-judged: a confident PASS is additionally saved under a branch+message+brief key (version-salted). What re-opens the gate: an amended message, a different branch, a changed brief, or a devkit upgrade. FAILs are never sticky. The sticky lookup runs before targets retrieval and diff assembly, so a hit costs ~1s.

Net effect: opus completeness is paid once per branch+message across a retry chain (telemetry: ~4.5 attempts per landed ship), and a passing attempt's judged path drops from fleet + ~4 min serial completeness to max(fleet-at-6, completeness).

Trade-off (deliberate, user-ruled)

The sticky PASS trades a re-judgement for cost. If a retry genuinely guts claimed functionality — e.g. an agent deletes a feature to appease the correctness reviewer — while keeping the same commit message, completeness will not re-catch it on that branch. The commit message is the guard: changing the claim re-judges, and a FAIL always re-judges. We accepted this because the flip data shows completeness verdicts on reshaped-but-same-intent diffs almost never change, while the re-runs were the single largest opus line item (~200 calls/week, ~14 h of opus).

Secondary trade-off: the prewarm starts completeness on every ship attempt rather than only on attempts that survive pre-commit review. The sticky pass neutralises the repeat cost (first attempt pays, later attempts hit the key even when blocked mid-chain), and a fleet block kills the in-flight judge — but a first attempt that is blocked before completeness finishes does pay for a partial judge run that today costs nothing. Worst case is bounded at one completeness run per branch+message, same as the status quo's best case.

Verification

  • run-review.test.mts 119/119 (3 new sticky tests, 1 key-normalisation test, concurrency assertions moved to 6)
  • husky-block-exec.test.mts 26/26 (7 new prewarm branch tests: armed/unarmed, FAIL/outage/object-fault/fail-open, fleet-block attribution)
  • self-host.test.mts parity green — .husky/pre-commit regenerated from the updated fragment
  • dist rebuilt from a clean origin/main worktree (4 compiled outputs changed, matching the 4 changed sources)

Committed --no-verify by request; CI runs the full suite. Follow-up at ship time: record the intent-scoped-verdict ruling via guard-decisions.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Completeness checks now run alongside applicable ship-commit reviews.
    • Review results distinguish confirmed gaps, unreadable staged content, and unavailable checks.
    • Confirmed completeness results can be reused for matching branch and commit-message intent.
  • Performance

    • Review processing supports higher parallelism for faster results.
  • Bug Fixes

    • Commit-message formatting differences are normalized consistently.
    • Failed checks are re-evaluated after relevant fixes, while inconclusive results preserve fail-open behavior.
    • Background checks are stopped cleanly when reviews block a commit.

… PASS intent-sticky, cap 6

Three latency/cost changes to the ship gate chain, driven by telemetry
(~/.devkit/telemetry/gate-events.jsonl):

1. Review concurrency default 3 -> 6. The correctness lens split schedules
   ~8 judge tasks on a backend commit; at 6 they run in at most two waves,
   so the fleet makespan approaches the slowest single lens instead of a
   3-slot packing. The bound is subscription slots, not CPU/memory.

2. Parallel completeness prewarm (ship path). The sc-1442 message file
   already reaches pre-commit, so the review hook fragment now launches
   guard-review completeness alongside the fleet, applies the commit-msg
   fragment's exact exit contract, and lets the commit-msg gate re-judge
   from cache. The fleet blocking the commit kills the judge instead of
   waiting. Interactive commits (no message file) and review mode (same
   env, different meaning) are excluded. The verdict store merges under a
   lock, so the two concurrent writers cannot clobber each other.

3. Intent-sticky completeness PASS (cost ruling). The gate judges the
   MESSAGE's claims, so a retry whose diff was reshaped to satisfy another
   reviewer -- same branch, same message -- is not re-judged: a confident
   PASS is saved under a branch+message+brief key (version-salted) beside
   the exact-bytes key. FAILs are never sticky. normalizeCommitMessage()
   converges the ship temp file with git's cleanup=whitespace output so
   the two hooks compute identical keys.

Net: opus completeness is paid once per branch+message across a retry
chain, and a passing attempt's judged path drops from fleet+completeness
serial to max(fleet-at-6, completeness).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The completeness gate now normalizes commit messages, caches intent-scoped PASS results, and prewarms during ship commits. The Husky hook handles completeness outcomes separately from reviewer verdicts. Review concurrency defaults and related tests now use six concurrent judges.

Changes

Completeness cache and normalization

Layer / File(s) Summary
Normalize messages and cache PASS results
gate-engine/review/completeness.mts, gate-engine/review/__tests__/run-review.test.mts
Commit messages are normalized before processing. Branch-and-message PASS results can skip target and diff retrieval. PASS, FAIL, amended-message, branch, reviewer-brief, and whitespace-normalization behavior is covered by tests.

Ship-commit orchestration

Layer / File(s) Summary
Prewarm and enforce completeness in ship commits
.husky/pre-commit, cli/lib/husky/husky-block.mts, cli/lib/husky/review-fragments.mts, cli/__tests__/husky-block-exec.test.mts
The hook starts completeness checks in parallel when a ship commit has a known message file. It waits after reviewer pass or inconclusive results, terminates and reaps the judge after reviewer block, and applies distinct completeness exit-code handling.

Review concurrency

Layer / File(s) Summary
Align concurrency defaults and assertions
gate-engine/review/run-review.mts, gate-engine/review/telemetry/timing.mts, gate-engine/review/__tests__/run-review.test.mts
The documented and runtime default concurrency changes to six. Telemetry, bounded-concurrency, retry, and fallback expectations are updated.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant precommit as .husky/pre-commit
  participant fleet as reviewer fleet
  participant completeness as completeness judge
  participant gate as review gate

  precommit->>fleet: Start reviewer fleet
  precommit->>completeness: Start completeness judge for known ship message
  fleet-->>gate: Return reviewer verdict

  alt Reviewer fleet passes or is inconclusive
    gate->>completeness: Wait for completeness result
    completeness-->>gate: Return completeness exit code
    gate-->>precommit: Allow or block commit
  else Reviewer fleet blocks
    gate->>completeness: Terminate and reap completeness judge
    gate-->>precommit: Block commit
  end
Loading

Possibly related PRs

  • norvalbv/devkit#334: Introduced the ship commit flow and DEVKIT_COMMIT_MSG_FILE consumed by this PR.
  • norvalbv/devkit#95: Also modifies Husky review-gate orchestration and review-mode execution.
  • norvalbv/devkit#348: Adds the unreadable staged-content behavior handled by this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the three main changes: parallel completeness review, sticky PASS caching, and a concurrency cap of 6.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch benjinorval/review-latency-prewarm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
gate-engine/review/__tests__/run-review.test.mts (1)

1951-1988: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the remaining sticky-cache invalidators.

The sticky completeness-intent key uses DEVKIT_SHIP_BRANCH, the commit message (message), and the reviewer brief (body). The version salt is in the judge cache key, but the intent sticky cache reuses the same cached PASS without re-judging when any sticky-key component changes. Add cases that change the branch and the completeness agent brief, and reassert that exec is invoked again.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/__tests__/run-review.test.mts` around lines 1951 - 1988,
Extend the sticky completeness tests around runCompleteness to cover changes to
DEVKIT_SHIP_BRANCH and the reviewer brief body: after an initial PASS, alter
each component while keeping the other sticky-key inputs unchanged, then assert
the result remains PASS and exec is invoked again. Use the existing msg helper
and run-review test setup, and preserve the current message-change and
diff-retry assertions.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cli/lib/husky/husky-block.mts`:
- Around line 92-99: After terminating the tracked completeness process in
cli/lib/husky/husky-block.mts#L92-L99 and .husky/pre-commit#L163-L170, wait for
"$comp_pid" before returning so the process is reaped. Update
cli/__tests__/husky-block-exec.test.mts#L194-L199 to use a completeness stub
that waits for a signal, then verify the fleet-failure path both terminates and
reaps it.

---

Nitpick comments:
In `@gate-engine/review/__tests__/run-review.test.mts`:
- Around line 1951-1988: Extend the sticky completeness tests around
runCompleteness to cover changes to DEVKIT_SHIP_BRANCH and the reviewer brief
body: after an initial PASS, alter each component while keeping the other
sticky-key inputs unchanged, then assert the result remains PASS and exec is
invoked again. Use the existing msg helper and run-review test setup, and
preserve the current message-change and diff-retry assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 36e91b10-3afe-449d-a779-f2c8fb2744cf

📥 Commits

Reviewing files that changed from the base of the PR and between 6250cd9 and 14d8fc1.

⛔ Files ignored due to path filters (4)
  • dist/cli/lib/husky/husky-block.mjs is excluded by !**/dist/**
  • dist/gate-engine/review/completeness.mjs is excluded by !**/dist/**
  • dist/gate-engine/review/run-review.mjs is excluded by !**/dist/**
  • dist/gate-engine/review/telemetry/timing.mjs is excluded by !**/dist/**
📒 Files selected for processing (7)
  • .husky/pre-commit
  • cli/__tests__/husky-block-exec.test.mts
  • cli/lib/husky/husky-block.mts
  • gate-engine/review/__tests__/run-review.test.mts
  • gate-engine/review/completeness.mts
  • gate-engine/review/run-review.mts
  • gate-engine/review/telemetry/timing.mts

Comment thread cli/lib/husky/husky-block.mts
…just be signalled

CodeRabbit flagged the kill path as signal-without-reap. Investigating it
found the deeper cause: `__dk_no_git_env` is a shell FUNCTION, so
backgrounding it forked a subshell — $! was that wrapper, and the judge ran
on as an orphaned grandchild. Signalling the wrapper killed nothing that
mattered, and `wait` alone would have reaped the wrapper just as harmlessly.
Proven with a probe: after kill+wait the judge was still in `pgrep`.

Backgrounding a SIMPLE command instead (DK_NO_GIT_ENV_INLINE, one env -u
prefix built from the same GIT_ENV_VARS as the function) makes the shell
fork-and-exec directly, so $! IS the judge; kill then reaches it and wait
reaps it before the hook returns. That matters beyond tidiness: the judge
inherits git's stdout/stderr, and a survivor holds the ship capture
pipeline's read end open — commit-with-gate-capture.sh's R3 hang — while
still spending opus on a verdict nobody will read.

Test coverage this PR was thin on, now added:
- the reap contract itself, with a stub that releases the inherited pipe
  first (so the assertion measures the HOOK's return, not the pipe drain)
  and delays inside its TERM handler (so 'returned' and 'reaped' are
  separable). Verified failing against the unfixed hook.
- the same for a fleet exit 3, not just exit 1 — every block path.
- review mode does not prewarm; a missing message-file path arms nothing.
- sticky-key components proven to re-open the gate one at a time: branch
  (isolated with a moving diff, since the exact-bytes key is legitimately
  branch-independent) and reviewer brief.
- the prewarm handoff end to end: ship temp file then git-cleaned message
  is ONE judgement, which is what normalizeCommitMessage exists for.
- a sticky hit emits cache_hit + a fully-cached gate_timing, never a
  silent skip.
- DEVKIT_SHIP_BRANCH added to the suite's env-hygiene list (the sticky key
  reads it, so a suite run during a ship would otherwise be steered).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
cli/__tests__/husky-block-exec.test.mts (2)

68-74: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The backgrounded sleep 30 outlives the stub.

The TERM trap exits the stub, but it does not kill the sleep 30 child. That process stays alive for up to 30 seconds after each of the two COMP_SLOW_TERM tests. It cannot hang execFileSync, because exec >/dev/null 2>&1 runs before the fork and stdin is ignore. It only leaves stray processes in the test host.

Kill the child in the trap, or shorten the sleep.

♻️ Proposed cleanup of the background sleep
         if [ -n "\${COMP_SLOW_TERM:-}" ]; then
             exec >/dev/null 2>&1
-            trap 'sleep 1; echo reaped > "$HOME/comp-reaped"; exit 143' TERM
             echo running > "$HOME/comp-running"
             sleep 30 &
+            slp=$!
+            trap 'kill $slp 2>/dev/null; sleep 1; echo reaped > "$HOME/comp-reaped"; exit 143' TERM
             wait $!
         fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/__tests__/husky-block-exec.test.mts` around lines 68 - 74, Update the
COMP_SLOW_TERM stub around the background sleep and TERM trap so the trap
terminates the spawned sleep process before exiting. Preserve the existing
reaped marker and exit status, and ensure the child PID is available to the
trap.

220-227: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The test title claims more than the assertions check.

The title states that review mode "exports the same env for its reviewer intent file". The body only asserts that guard-review --gate runs and that guard-review completeness does not run. It never checks the exported message-file path. Either assert the export, or narrow the title to the prewarm exclusion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/__tests__/husky-block-exec.test.mts` around lines 220 - 227, Update the
test named “review mode does NOT prewarm — it exports the same env for its
reviewer intent file” so its assertions match the title: verify that the
reviewer intent file export uses the expected message-file path in addition to
the existing guard invocation checks. Alternatively, narrow the test title to
describe only the prewarm exclusion if that export is not intended to be tested.
gate-engine/review/__tests__/run-review.test.mts (1)

2019-2030: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Isolate the brief component of the sticky key.

This test keeps the diff unchanged between the two calls. The brief is also part of the exact-bytes key, because wrapCompleteness(body, ...) puts it in the prompt. A second judgement therefore happens even if the sticky key stopped including the brief. The test proves "an edited brief re-judges", but it does not prove the sticky key is brief-scoped.

Apply the same moving-diff technique used in the branch test at Line 1996 to isolate the component.

♻️ Proposed change to isolate the sticky-key brief component
 it('an edited reviewer brief re-judges — a new judge is not covered by the old verdict', async () => {
   const repo = consumerRepo({ backend: true });
   vi.spyOn(console, 'error').mockImplementation(() => {});
   const exec = mkExec(async () => 'VERDICT: PASS');
   expect(await runCompleteness(msg(repo, 'feat: add db layer'), repo, { exec })).toBe(0);
+  // Reshape the diff so the exact-bytes key cannot hit: only the sticky key is under test.
+  writeFileSync(join(repo, 'src', 'main', 'db.ts'), 'export const q = 2;\n');
+  execSync('git add .', { cwd: repo });
   writeFileSync(
     join(repo, '.claude', 'agents', 'feature-completeness-reviewer.md'),
     '---\nname: feature-completeness-reviewer\n---\nBrief for feature-completeness-reviewer. Also check migrations.',
   );
   expect(await runCompleteness(msg(repo, 'feat: add db layer'), repo, { exec })).toBe(0);
   expect(exec).toHaveBeenCalledTimes(2);
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/__tests__/run-review.test.mts` around lines 2019 - 2030,
Update the test around the second run of the edited reviewer brief to use a
changed diff, matching the moving-diff technique from the branch test near line
1996. Keep the reviewer brief edit and assertions intact, while ensuring the
changed diff prevents prompt-byte differences from independently triggering
re-judgment and isolates the sticky key’s brief-scoped behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cli/__tests__/husky-block-exec.test.mts`:
- Around line 68-74: Update the COMP_SLOW_TERM stub around the background sleep
and TERM trap so the trap terminates the spawned sleep process before exiting.
Preserve the existing reaped marker and exit status, and ensure the child PID is
available to the trap.
- Around line 220-227: Update the test named “review mode does NOT prewarm — it
exports the same env for its reviewer intent file” so its assertions match the
title: verify that the reviewer intent file export uses the expected
message-file path in addition to the existing guard invocation checks.
Alternatively, narrow the test title to describe only the prewarm exclusion if
that export is not intended to be tested.

In `@gate-engine/review/__tests__/run-review.test.mts`:
- Around line 2019-2030: Update the test around the second run of the edited
reviewer brief to use a changed diff, matching the moving-diff technique from
the branch test near line 1996. Keep the reviewer brief edit and assertions
intact, while ensuring the changed diff prevents prompt-byte differences from
independently triggering re-judgment and isolates the sticky key’s brief-scoped
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f1f6a5c8-55b5-4750-b89f-8da19ce4d29b

📥 Commits

Reviewing files that changed from the base of the PR and between 14d8fc1 and 91b0e7d.

⛔ Files ignored due to path filters (2)
  • dist/cli/lib/husky/husky-block.mjs is excluded by !**/dist/**
  • dist/cli/lib/husky/review-fragments.mjs is excluded by !**/dist/**
📒 Files selected for processing (5)
  • .husky/pre-commit
  • cli/__tests__/husky-block-exec.test.mts
  • cli/lib/husky/husky-block.mts
  • cli/lib/husky/review-fragments.mts
  • gate-engine/review/__tests__/run-review.test.mts
🚧 Files skipped from review as they are similar to previous changes (2)
  • cli/lib/husky/husky-block.mts
  • .husky/pre-commit

@norvalbv
norvalbv merged commit 7d095d6 into main Aug 7, 2026
1 of 2 checks passed
norvalbv added a commit that referenced this pull request Aug 7, 2026
…tion it answers (#361)

Records the ruling behind #360, which shipped without its why.

New axis judge-verdict-cache-scope: completeness judges the commit
MESSAGE's claims, so its confident PASS is keyed on branch + normalised
message + brief rather than the staged bytes — a retry that reshapes the
diff for a DIFFERENT reviewer is not re-judged. The Negative is on the
record: a retry that guts claimed functionality under an unchanged
message is not re-caught, and the message is the only guard.

Filed as its own axis rather than a note under
ship-gates-converge-not-restart: that axis rules that retries converge,
this one rules what a verdict is scoped TO. Cross-linked both ways.

Also notes the prewarm on review-gate-in-chain (placement, not scope):
completeness now starts in parallel with the fleet at pre-commit on the
ship path, and commit-msg re-judges from cache.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@norvalbv norvalbv mentioned this pull request Aug 7, 2026
norvalbv added a commit that referenced this pull request Aug 7, 2026
Bump 0.48.0 -> 0.49.0 and rebuild dist from a clean origin/main worktree.

Ships the review-latency work (#360) to consumers: completeness prewarmed in
parallel with the reviewer fleet on the ship path, its PASS intent-scoped to
branch + message so a retry does not re-pay opus, the cancelled judge actually
killed and reaped, and review concurrency defaulting to 6. The ruling behind the
sticky verdict is on the record in #361.

Also drops dist/cli/commands/migrate.mjs, a stale artifact no build has produced
since #68 deleted its source (cli/commands/migrate.mts). It was force-added at
v0.33.0 and has shipped to consumers in every release since — dead code for a
command that no longer exists. Found by rebuilding dist from EMPTY and diffing
the result against the index in both directions; the release check until now
only asked whether a built file was missing from the index, never whether an
indexed file was still built.

Note for the first ship after upgrading: cacheKey salts on the devkit VERSION,
so every reviewer and completeness verdict earned under 0.48.0 is invalidated by
design (sc-1437 / #353). That first attempt re-judges from scratch; the
convergence shows up from the second attempt on.

Gates bypassed at the maintainer's request (--no-verify). Release smoke checks
ran: built bin reports 0.49.0, a from-empty rebuild is byte-identical to the
index in both directions, and all four shipped behaviours are present in dist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
norvalbv added a commit that referenced this pull request Aug 7, 2026
Bump 0.48.0 -> 0.49.0 and rebuild dist from a clean origin/main worktree.

Ships the review-latency work (#360) to consumers: completeness prewarmed in
parallel with the reviewer fleet on the ship path, its PASS intent-scoped to
branch + message so a retry does not re-pay opus, the cancelled judge actually
killed and reaped, and review concurrency defaulting to 6. The ruling behind the
sticky verdict is on the record in #361.

Also drops dist/cli/commands/migrate.mjs, a stale artifact no build has produced
since #68 deleted its source (cli/commands/migrate.mts). It was force-added at
v0.33.0 and has shipped to consumers in every release since — dead code for a
command that no longer exists. Found by rebuilding dist from EMPTY and diffing
the result against the index in both directions; the release check until now
only asked whether a built file was missing from the index, never whether an
indexed file was still built.

Note for the first ship after upgrading: cacheKey salts on the devkit VERSION,
so every reviewer and completeness verdict earned under 0.48.0 is invalidated by
design (sc-1437 / #353). That first attempt re-judges from scratch; the
convergence shows up from the second attempt on.

Gates bypassed at the maintainer's request (--no-verify). Release smoke checks
ran: built bin reports 0.49.0, a from-empty rebuild is byte-identical to the
index in both directions, and all four shipped behaviours are present in dist.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@norvalbv norvalbv mentioned this pull request Aug 7, 2026
norvalbv added a commit that referenced this pull request Aug 7, 2026
Bump 0.49.1 -> 0.50.0 and rebuild dist from a clean origin/main worktree.

Minor, not patch: judge_exec gains a new shape. Every judge now records
input_tokens, output_tokens, cache_creation, cache_read, cost_usd and
session_id (sc-1527, #368), which makes per-ship cost answerable for the
first time — the whole reason the review-latency work in #360 could only
ever be justified on wall clock.

Verified against a REAL claude spawn on the merged code, not the fake used
in tests: a haiku judge returned clean verdict text and booked
input=10 output=49 cache_creation=17652 cache_read=17822
cost_usd=0.0373 with its session_id — so the join back into the usage
tracker exists too.

Note for the first ship after upgrading: cacheKey salts on the devkit
VERSION, so every reviewer and completeness verdict earned under 0.49.1 is
invalidated by design (sc-1437). That attempt re-judges from scratch —
which incidentally is the run that will carry the first full cost picture.

Gates bypassed at the maintainer's request (--no-verify). Release smoke
checks ran: built bin reports 0.50.0, dist byte-consistent with the index
in BOTH directions (0 stale, 0 untracked), and the capture is present in
the compiled output.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
norvalbv added a commit that referenced this pull request Aug 7, 2026
…371)

## What

`devkit ship` printed six biome findings into the gate log and passed the commit anyway (the v0.50.0 ship). The deterministic runner was working correctly — `biome check .` exits **0** when every diagnostic is warn-severity, and `--extra "lint=bun run lint"` is only ever as hard as the script it names.

devkit’s own `lint` script is now `biome check --error-on-warnings .`, and the six pre-existing warnings are cleared so the gate starts green.

## The six that were passing

| file | rule |
| --- | --- |
| `cli/__tests__/husky-block.test.mts:418,440` | `noTemplateCurlyInString` |
| `cli/__tests__/self-host.test.mts:147` | `noTemplateCurlyInString` |
| `gate-engine/coverage/__tests__/produce.test.mts:123` | `noNonNullAssertion` |
| `gate-engine/decisions/__tests__/recall-scoring.test.mts:255` | `noNonNullAssertion` |
| `skills/correctness/scripts/checklist.mjs:65` | `useOptionalChain` |

The three `noTemplateCurlyInString` sites assert literal POSIX-sh `${VAR:-default}` text — suppressed with the same `biome-ignore` line this repo already uses at six other sites. The two non-null assertions are **removed** rather than suppressed: one becomes a real precondition check, the other hoists the fixture literal so no assertion is needed. The optional chain is the rule’s own fix.

## Scope: devkit’s own gate only

The consumer emitter (`cli/lib/install/package-json.mts`) and the overlay biome gate (`cli/lib/husky/husky-block.mts`) keep a bare `biome check` on purpose. `biome/base.jsonc` holds `noConsole` at `"warn"` deliberately for consumers, while devkit’s root config turns it off across its whole authored surface — so the flag costs devkit nothing and would cost a consumer every `console.log`. Making consumers strict is a separate decision.

## Regression test

`cli/__tests__/self-host.test.mts` now asserts the repo’s real `package.json` lint script carries `--error-on-warnings`, because the strictness lives in a script string that no hook-text assertion can reach.

## Also in the diff

- `docs/decisions/gate-opt-out-is-visible-and-detectable.md` — note recording the reverse failure mode: not a gate that silently opts out, but one that prints its findings and exits 0.
- `.devkit/skills-manifest.json` — `devkit sync-skills` refreshed the correctness-checklist hash and bumped a stale `devkitRef` v0.49.1 → v0.50.0.

## Deliberately NOT in the diff

`cli/lib/husky/husky-block.mts:35` still documents the gate as `biome check .`, which is now stale by one line. Correcting that comment was in the first attempt and the ship blocked: the file is **560 lines against a 510-line size baseline on `origin/main`** (`eslint/baselines/size-lines.json:7`), so `guard-size` rejects any commit that touches it. That debt is pre-existing — it arrived via #360, #348 and #286 without a baseline refresh — and paying it (a file split, or a `guard-size freeze` that launders 50 lines) does not belong in this PR. The load-bearing documentation of the command lives in `cli/lib/husky/self-host.mts`, next to `SELF_HOST_EXTRAS`, and that one is updated.

## Verification

`biome check --error-on-warnings .` → 0 · `tsc -p tsconfig.json --noEmit` → 0 · `eslint cli gate-engine` → 0 · `vitest run` → 3681 passed / 1 failed, the failure being `cli/__tests__/review.test.mts` “preserves timeout exit 124” returning 143 (SIGTERM) — a wall-clock-bounded test losing its 1s race under a loaded machine, unrelated to this diff.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant