Skip to content

fix(pm): dispatch-gates names the type-check lanes it cannot measure - #19636

Merged
huangyiirene merged 4 commits into
mainfrom
claude/issue-19172-typecheck-lanes-not-measured
Sep 22, 2026
Merged

huangyiirene merged 4 commits into
mainfrom
claude/issue-19172-typecheck-lanes-not-measured

Conversation

@huangyiirene

@huangyiirene huangyiirene commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes #19172

Clause-②: no

The defect, restated from the measurement

scripts/pm/dispatch-gates.mjs --commands is what a dispatch order is built from. For a TypeScript-touching path it emits pnpm check:type-check-coverage and pnpm check:type-check-debt — and nothing else carrying the word. Those two ratchet a LEDGER. The required TypeScript Type Check context goes red on four CI jobs running per-package tsc programs, and the four steps that run them had no NAME anywhere in the output and no disclosure at all on --commands — they were already rows of the always-runs tail, 4 of its 33, printed unnamed and unclassified on every plain run. So a reader who greps the output for the one word they would grep for FINDS something and can reasonably conclude the surface is accounted for. That is a false reassurance, not a silence they could notice.

Re-measured on the base of this branch (744a0a3) with the filer's own paths, packages/spec/scripts/sharded-artifacts.test.ts scripts/git-env.d.mts:

node scripts/pm/dispatch-gates.mjs --commands THOSE_TWO_PATHS
  ... 72 commands on stdout; grep -icE 'typecheck|type-check|tsc' over the WHOLE
     captured output (stdout + stderr) = 2, both of them the two ledger families.
  ... the closing disclaimer named its buckets and no type-check surface.

Premise live on this base, three file moves after the card was graded.

What landed

A type-check lane block, derived from the same parsed workflow entries the always-runs tail and the path-scheduled job block already read — never a hardcoded roster:

  • isTypeCheckInvocation(command) recognises a type-check program on argv tokens, never substrings: tsc as a token together with --noEmit / -p / --project, or run followed by typecheck / type-check. The substring reading over-matches on this very tree (an echo about a tsc-built package, the lane aggregator's own console.log about a type-check lane), and both over-matches are pinned as negatives. check:type-check-coverage and check:type-check-debt match neither, deliberately.
  • typeCheckLaneSteps(entries) walks every pull-request workflow's jobs and steps. A job or step carrying an if: is kept and marked, never excluded — the two neighbouring blocks drop a conditional because each claims CI definitely runs the step, and this one claims only that the lane exists.
  • The walk is SIZED. counts returns the command-carrying steps and the spliced run: lines examined, and both rendered lanes print that denominator, so a recogniser that stops spelling a lane shows as a dip rather than as silence.
  • Line continuations are spliced with joinLineContinuations before the token split, the reading jobFilteredSteps takes; an unspliced split drops a continued invocation silently.
  • typeCheckLaneLines(rows, counts) renders it on every plain run, directly above the always-runs tail, with the prescription a card owes: pnpm --filter PKG run typecheck per package whose TypeScript the diff changes what a program can see.
  • Absence is loud. An empty walk prints THE SOURCE OF TRUTH CAME BACK EMPTY with the workflow count it read, rather than dropping the heading — a missing block looks exactly like a covered surface.
  • --commands / --json print the rows on stderr as ⊘ NOT MEASURED lines, plus one line saying the two ledger families are not these. --json carries a typeCheckLanes key. The block is named unconditionally in outsideBlockNames, so an empty walk cannot hide it from the closing disclaimer on any of the three lanes that read that list.

After, same paths, at 872e04b:

stdout: 72 commands, byte-identical to before
stderr: + 4 CI step(s) run a TYPE-CHECK PROGRAM and are NOT above - ...
          ⊘ NOT MEASURED - [lint.yml · Type Check · source gates] pnpm --filter @objectstack/spec exec tsc --noEmit
          ⊘ NOT MEASURED - [lint.yml · Type Check · workspace] pnpm exec turbo run typecheck --filter='./packages/*' ...
          ⊘ NOT MEASURED - [lint.yml · Type Check · consumer gates] pnpm --filter './examples/*' run typecheck
          ⊘ NOT MEASURED - [lint.yml · Type Check · consumer gates] pnpm --filter @objectstack/downstream-contract run typecheck
          ⛔ pnpm check:type-check-coverage and pnpm check:type-check-debt are NOT these ...
closing disclaimer: "... the 5 path-scheduled CI job(s), the type-check lanes and the always-runs tail ..."

The derivation for this PR now names its own former blind spot.

Reverse verification — measured, not designed

The first full battery run with the block wired in went RED: 6 of 1903 cases failed. Four of those six are pre-existing pins that spell the outside-blocks enumeration verbatim — the human reconciliation lane's phrase pin, the --ran lane's phrase pin, and both zero-row negatives ("while still naming the two that print unconditionally"). That red is the proof the wiring is observable rather than decorative: adding one unconditional name to outsideBlockNames reddened every lane that reads that list. The other two were my own fixture anchoring on a literal that only exists inside a YAML block scalar. Both halves repaired in the second commit; the re-run is green below.

Second, for the by-name control (patch round 1): ablating one spelling out of the recogniser (joinLineContinuations untouched) drops the live rows 4 to 1 — the old rows.length greater than zero assertion stays GREEN while the by-name assertion goes RED. On-disk proof: anchor 1 to 0, blob 9c8d18aa52d8 to c3acc8e3a937; restored blob equals HEAD and git diff HEAD is empty.

Tests and gates, at 3b533c1 (patch round 1; the round-0 rows held at 872e04b)

command exit verdict line
pnpm check:pm-dispatch-gates (detached, 655.0 s) 0 ✓ dispatch-gates self-test: 1904 cases pass. and ✓ check:pm-dispatch-gates --self-test: the exit contract holds in all three directions.
the other 28 derived families 0 each recorded as :: exit 0 in the run record
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran 0 ✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 29 run, 0 NOT-MEASURED
pnpm lint (repo-wide, eslint . --no-inline-config) 0 clean; not a narrowed run, the whole scan
control-byte self-scan over the changed file no hits

Ten new self-test cases, all green: the two positive spellings, the two ledger families as negatives, both live over-matches as negatives, the conditional-kept-and-marked row, the non-pull-request workflow sized rather than dropped, the heading and prescription, the loud-empty rendering, the unconditional enumeration name, and a LIVE control asserting the per-package tsc lanes are found on this tree with no ledger family mistaken for one.

No build or per-package typecheck is owed by this diff and none is claimed: it touches one .mjs file and no workspace package, and the tool's own change-kind derivation scores it 0 by change KIND — a .mjs is outside the root tsc program, the root config setting no allowJs.

Line budget: 208 insertions / 8 deletions = net +200, self-test cases included (patch round 1 was +76 / −76: the new checks paid for line-for-line out of the restated prose).

Acceptance notes

  • The filer's candidate 2 (emit pnpm --filter PKG run typecheck as real commands) is measured NOT cheap, and is not taken here. Two readings: the tool has no path-to-package map at all (the only package resolution in it runs the other way, from a --filter name to a directory); and a lane is not a discovered check family, so putting lane commands into the runnable union would break familyReconciliation's own arithmetic invariant, which asserts matched + convention − both + alwaysRunsOnly === total and throws otherwise. The prescription is therefore carried as text in the block rather than as stdout commands. Candidate 3's prohibition is honoured: neither ledger family is renamed or dropped.
  • Type Check · debt ledger runs pnpm check:type-check-debt, which IS a discovered family, so it is correctly absent from the lane rows and present in the matched list — the two blocks stay disjoint.
  • .github/workflows/** is read and never edited, as the claim's file surface declares.
  • The recogniser vocabulary has silent misses by construction (pnpm typecheck, pnpm -r typecheck, node --run typecheck, tsc --build, tsc -b, vue-tsc, tsgo). They are declared in the predicate header, the walk is sized so a miss is visible as a dip, and the live control pins the required aggregate lanes by name so a miss on THOSE reds.

Generated by Claude Code

`dispatch-gates --commands` emitted `check:type-check-coverage` and
`check:type-check-debt` for a TypeScript-touching path and nothing else
carrying the word, so a reader who grepped the output for `typecheck`
found something and could conclude the surface was covered. Those two
ratchet a ledger; the required `TypeScript Type Check` context goes red
on per-package `tsc` programs that sat in no bucket at all.

Add a type-check lane block, derived from the same parsed workflow
entries the always-runs tail and the path-scheduled job block read:
every pull-request-workflow step whose `run:` invokes a type-check
program, recognised on argv tokens rather than substrings. It renders on
every plain run above the tail, prints its rows as `⊘ NOT MEASURED` on
the `--commands`/`--json` stderr accounting, carries a `typeCheckLanes`
key in `--json`, and is named unconditionally in `outsideBlockNames` so
an empty walk cannot hide it. An empty walk renders loud instead.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>
The lane fixture replaced a literal that only exists inside a block
scalar, so its unconditional row never appeared. Anchor it on the step
whose `run:` is a compact one-liner instead.

The four verbatim pins on the outside-blocks enumeration are doing their
job: they spell the whole phrase in print order, so the new
unconditional name belongs in each. Updated in place, including the two
negatives that assert the names printed at zero rows.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>
@huangyiirene huangyiirene added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 22, 2026 — with Claude

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 872e04bd19c0f5957f671790a260f6a40619e6aa

① Derived judgments

Diff line numbers below are lines of git diff origin/main...872e04bd -- scripts/pm/dispatch-gates.mjs (207/-7, one file; merge-base is 744a0a3f1, an ancestor of current origin/main, so the count is not inflated).

(a) isTypeCheckInvocation (diff 25-30) — right today, silently wrong on rot. I ran the head's own exported predicate over the head's 38 workflows: 26 PR workflows, 4 rows, all lint.ymlType Check · source gates / Type check (@objectstack/spec) (exec tsc --noEmit), Type Check · workspace / Type check workspace packages (turbo run typecheck), Type Check · consumer gates ×2 (--filter './examples/*' run typecheck, @objectstack/downstream-contract run typecheck). That is 3 of the 4 jobs behind the required TypeScript Type Check; Type Check · debt ledger yields 0 rows and that is correct — its only run: is the ledger family. A permissive sweep of every run: line in every workflow carrying typecheck|type-check|tsc|vue-tsc finds 0 lanes the predicate misses on this tree; its 6 rejects are the 2 ledger families plus 4 prose lines. ⛔ But the misses it would make are silent, every one — measured: pnpm typecheck, pnpm -r typecheck, pnpm turbo typecheck, node --run typecheck (no run token); tsc --build, tsc -b; vue-tsc --noEmit, tsgo --noEmit (token is not tsc). So is a backslash-continued exec tsc \ + --noEmit: unlike jobFilteredSteps, which splices joinLineContinuations before the split and says in its own comment why, this block copies the older unspliced reading (diff 77-80). A miss costs one row and nothing else: counts (diff 68) carries prWorkflows / nonPullRequestWorkflows / conditional and never sizes the steps or run: lines it walked past, so there is no denominator for a shrinking numerator to show against — both neighbours return their exclusion sizes precisely because an exclusion nobody can size is one nobody can weigh. The loud-empty path covers total rot only, and the live control (diff 285-288) is pinned to rows.length > 0, so it stays green if 3 of the 4 rows disappear. That is this card's own defect one layer up: a confident heading over a set nobody can audit.

(b) Same entries — HOLDS. typeCheckLaneSteps(workflowEntries) sits at head line 14905, on the array jobFilteredSteps reads at 14901 and alwaysRunSteps at 15280, all from the one discoverFamilies({ tree }) at 14779. The claim (diff 34-36, 222-225) is exact. Residual, unclaimed: composite-action steps (entry.composites) are not walked — same blind spot as the neighbours, no live lane in .github/actions.

(c) outsideBlockNames (diff 149) — right, pins only half-updated. Unconditional is correct: the block renders on every run, so a counted name would vanish on exactly the empty walk; print order matches derive (diff 246-247 renders between jobFilteredOut and the tail). No consumer anywhere in the repo parses that sentence — grep for always-runs tail outside this file returns nothing. Four pins updated (diff 296-298, 305-307, 315-316, 324-325). ⛔ Three more sites that spell the same enumeration were left un-updated, all on the human lane: the name-by-name battery (head 27277-27284) and the zero-branch battery (head 27327-27333) still list five names without the new one, and head 27307 still reads '...while still naming the two blocks that print unconditionally' asserting only two names — while the --ran and --commands equivalents were both moved to three. Those batteries are the strong shape #16398 installed because containment pins let a sentence name three of five and stay green. Dropping the new name still reds the two phrase pins, so this is coverage drift, not a hole — but it is drift away from the file's own rule.

(d) Empty walk (diff 111-117, 211-213) — unreachable today, correctly loud. 4 rows on this tree, so the branch is dead until the recogniser or lint.yml moves. Both lanes name it NOT MEASURED, say the source came back empty, carry the workflow count, and refuse the "tree without type checking" reading. Right shape.

(e) stdout — confirmed unchanged. No console.log is added anywhere in machineReadableOutput; the only added console.log is the human lane (diff 246-247). commandsFor is untouched, so --commands is byte-identical. derivationJson gains typeCheckLanes (diff 171) after jobFilteredSteps and before counts — purely additive, no key renamed or dropped. Right. Nit: the stderr row prints row.commands[0] only (diff 205) with no elision marker, while the human lane caps at ALWAYS_RUN_COMMAND_CAP and says how many it ate (diff 131-133) — silent truncation if a step ever carries two lanes. Nit: typeCheckLaneSteps is computed at head 14905 before the mode === 'ran' return and is unused on that lane.

(f) Prohibition honoured. Neither check:type-check-coverage nor check:type-check-debt is renamed or dropped; both are pinned as negatives (diff 262-263), both remain discovered families, and Type Check · debt ledger is green at this head. Correct.

(g) Budget. 207 insertions, ~95 of them prose. "Two families carry the word, neither is a lane" is restated in six comment sites (diff 21-23, 45-53, 99-107, 166-168, 195-198, 243-245) and twice more in rendered output (diff 121-123, 207-210). That matches this file's convention of a why-here note per call site, so I do not refuse it — but it is the first thing a budget would cut.

⛔ The one factual error. Diff 43-44 states the lanes "were in NO bucket this tool had." Measured on this head: all four rows are already rows of the always-runs tail — same workflow, job, step and command, 4 of its 33 rows, printed on every plain run. The dev's own derive-site comment (diff 242-244) says exactly that ("the tail is where these steps otherwise dissolve… one row among thirty-three"), so the diff contradicts itself about the tool's prior behaviour. The real defect is narrower and still real: no name, and no disclosure at all on --commands. A header in this file is the record; this one is wrong as written, in a card about prose that reassures falsely.

② Semver level

None owed. scripts/pm/** is shipped by nothing — root package.json is private: true with no files, and no workspace package lists scripts/pm. One .mjs, no published schema, no docs surface, no CHANGELOG prose. skip-changeset is this repo's correct form (the label, never an empty changeset), it is on the PR, and Check Changeset reads skipped at this head. Consistent — correct.

③ Boundary flags

  1. Base moved 1c16889a61744a0a3f1. Answered, accepted. Both are ancestors of current origin/main; git merge-base origin/main 872e04bd returns 744a0a3f1 exactly, so the three-dot diff is against the true fork point and the premise re-measurement on 744a0a3 is on the branch's own base.
  2. Detached batteries, one 600 s call cap re-entry. Accepted as an honest reading, not as a clearance. The gate's own header prescribes the detached pattern; the local exit 0 at 624.1 s is recorded with both verdict lines and pinned to a clean tree at this head. The authoritative reading is the head's check-runs, and at 872e04bd Lint & Repo Gates is in_progress — that is the job that runs pnpm check:pm-dispatch-gates, i.e. this block's own battery has not converged in CI. Test Core (1/6) is also in_progress. Everything else completed is success or skipped; TypeScript Type Check and all four Type Check · lanes are success. Recorded as in-flight, not counted either way.
  3. Reverse verification red on 4 pre-existing pins + 2 own fixtures, repaired in 872e04b. Half-answered. The red is genuine proof the wiring is observable, and the four repairs are in the head and correct. But "every lane that reads that list reddened" is not the same claim as "every pin that spells the list was updated": the three human-lane sites named in ①(c) stayed green by containment and were not extended. Flagged, not escalated.

What must change

  1. Make partial recogniser rot loud. Pin the live control (diff 285-288) to the lanes behind the required aggregate — assert a row is found for Type Check · source gates, Type Check · workspace and Type Check · consumer gates by name, instead of rows.length > 0. Two lines, and it turns every silent spelling miss on the lanes that matter into a red check:pm-dispatch-gates.
  2. Size the walk. Return in counts (diff 68) the number of steps / run: lines examined, and render it in both lanes, so a shrinking numerator is visible against a stable denominator — the discipline both neighbouring blocks already keep.
  3. Splice continuations before the split (diff 77-80), as jobFilteredSteps does, or state the continued invocation as a known silent miss in the header.
  4. Correct diff 43-44. The four rows were in the always-runs tail; say what was actually absent (a name, and any --commands disclosure at all), so the header does not misstate the tool's own prior behaviour.
  5. Extend the three human-lane enumeration pins (head 27277-27284, 27307, 27327-27333) to the new name, matching what the --ran and --commands lanes received.

Implemented-by: claude/issue-19172-typecheck-lanes-not-measured
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: FAIL


Generated by Claude Code

Review of record on PR #19636 returned FAIL with five owed changes.

- The live control is pinned to the lanes behind the required aggregate
  BY NAME (`Type Check · source gates` / `· workspace` / `· consumer
  gates`) instead of a bare row count, so a partial recogniser miss reds
  `check:pm-dispatch-gates` rather than dipping silently. `Type Check ·
  debt ledger` stays absent on purpose: its only `run:` IS the ledger
  family.
- `counts` now sizes the walk — command-carrying steps and spliced
  `run:` lines — and both rendered lanes print that denominator, the
  discipline the two neighbouring blocks already keep.
- Line continuations are spliced with `joinLineContinuations` before the
  token split, as `jobFilteredSteps` does; an unspliced split drops a
  continued invocation silently.
- The header no longer claims the lanes "were in NO bucket this tool
  had". Measured: all four rows were already rows of the always-runs
  tail. What was absent is a NAME and any `--commands` disclosure.
- The three human-lane enumeration pins are extended to the new name,
  matching what the `--ran` and `--commands` lanes already carry.

Two nits from the record: the stderr row now elides rather than
truncating silently, and the lane walk moved below the `--ran` return,
which renders no block of it. Paid for inside the +200 budget by cutting
the restated "neither of those two is a lane" note down to the header
and one line in each rendered lane.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 3b533c17154347988307f6a0de87660755a96805

① Derived judgments

Line numbers are lines of git show refs/pm/pr19636:scripts/pm/dispatch-gates.mjs. Merge-base is 34545d6 = current origin/main, so the three-dot count 208/−8 on one file is against the tip, not inflated. Round-2 delta is 872e04b..9cd7a88, +76/−76.

The five owed changes of record 5770500900 — all five MET.

(1) Live control pinned BY NAME — MET. Head 27118-27125: the control now asserts ['Type Check · source gates', 'Type Check · workspace', 'Type Check · consumer gates'].every((j) => liveLaneJobs.includes(j)), with the ledger negative kept. Measured by me over the head's 38 workflows: 26 PR workflows, 4 rows, jobs exactly those three (consumer gates twice); Type Check · debt ledger yields 0 and the comment at 27118-27120 says why.

(2) Walk sized, both lanes render it — MET. counts gains steps / runLines (2923, incremented 2937-2938 only for steps that carry a non-empty spliced run: line, 2932-2936). Human lane 11981 builds walked and prints it at 11992 (rows) and 11985 (empty walk); --commands / --json print it at 14718 (rows) and 14727 (empty). Live reading: 415 command-carrying step(s) / 1722 spliced run: line(s) across 26 pull-request workflow(s) against a numerator of 4.

(3) Continuations spliced — MET. 2932-2935 is byte-identical to jobFilteredSteps at 2839-2842 (joinLineContinuations(c).split('\n'), trim, drop empties), and the header at 2909-2912 names the reading and the miss it closes. Row count on this tree is unchanged at 4, so the splice neither widened nor narrowed here.

(4) Header corrected — MET, and the replacement is true. 2892-2899 now reads "What was missing was NOT the steps… all four rows this walk returns were already rows of the always-runs tail — same workflow, job, step and command, 4 of its 33. Missing was a NAME for them and any disclosure at all on --commands". Verified: alwaysRunSteps returns 33 rows on this tree and all four lane rows match one by workflow, job, step and command. The self-contradiction with the derive-site comment (now 15261-15264) is gone. The one surviving in NO bucket at 2632 is pre-existing prose from #13333 about the undetermined residue bucket, identical in origin/main — a leftover of a different block, not of this one.

(5) Three human-lane pins extended — MET. Name-by-name battery 27281, the phrase pin 27306 ("naming the three blocks that print unconditionally", over three names), zero-total battery 27327. The round-0 repairs at 27292 / 28370 / 28383 / 28460 are untouched and still correct; occurrences of the name rise 6 → 9. Case count 1903 → 1904 is consistent: only the name-by-name loop adds a t().

Both nits — MET. Stderr row elides with (+N more lane line(s) in this step) at 14721-14722 instead of printing commands[0] silently; typeCheckLaneSteps moved to 14939, below the mode === 'ran' return — every use (14939, 14952, 15266) is after it, so no TDZ.

Reverse verification holds as a claim about the code as landed. I ablated one spelling from the recogniser (2885) and re-walked the live workflows: rows 4 → 1, the old rows.length > 0 assertion GREEN, the by-name assertion RED, denominator unchanged at 415/1722. Ablating the tsc branch (2884) instead: 4 → 3, old pin GREEN, by-name RED. Exactly the failure the record named.

Nothing narrowed or widened beyond round 0. Predicate 2881-2886 is unchanged; the only behavioural changes are the splice, the two counters and the empty-line filter (inert — the empty string never matched). --commands stdout, commandsFor and the derivationJson key order are untouched.

Prose cuts removed nothing the code relies on. The cut lines are restatements: "neither of those two is a lane" survives in the header (2896-2899), the human block (11993-11995) and the stderr line (14724-14725); "running every command on stdout does NOT cover it" is carried by "OUTSIDE the runnable total" at 11995. No self-test pin greps a cut phrase — the loud-empty pin at 27113-27114 still matches (across 7 pull-request workflow(s)), and the heading/prescription pin at 27110-27112 still matches pnpm --filter PKG run typecheck.

The merge brings nothing into this file. git diff 9cd7a88 3b533c1 -- scripts/pm/dispatch-gates.mjs is empty; the merge carries 8 .changeset/*.md edits from #19627 only, and the PR's file surface is still one file.

Residuals, not defects. (i) The denominator it installed is itself unpinned — no case asserts the Walked … line renders in either lane; the only mention of counts.steps in selfTest is the LIVE control's detail string at 27125, which is a message, not an assertion, while the neighbour block pins its own sizing at 26990 and 26994. Deletable without reddening. (ii) Composite-action steps (entry.composites) still unwalked — unchanged blind spot shared with both neighbours, no live lane in .github/actions.

② Semver level

None owed, unchanged from round 0. scripts/pm/** is shipped by nothing — root package.json is private: true with no files, no workspace package lists it. One .mjs, no published schema, no docs or CHANGELOG surface. skip-changeset is this repo's correct form and is on the PR; Check Changeset reads skipped at this head. Consistent.

③ Boundary flags

  1. Net +200 held (208 / −8). Answered, accepted. Verified against merge-base 34545d6; PR files API agrees (+208 / −8, one file). The round-2 delta is +76 / −76, so the new checks were paid line-for-line out of restated prose, exactly as reported.
  2. Worktree recreated at 872e04b. Answered, accepted. The delta is a clean patch on that base: every round-0 site outside it (27292, 28370, 28383, 28460, the predicate, commandsFor) is still at its round-0 value on the head, so nothing was lost or re-reverted in the recreation.
  3. PR body left to the seat. Accepted as a seat act; judged the diff only. Flagged for the seat before landing: the body still says "at 872e04b", "207 insertions / 7 deletions", and still carries "those jobs were in no bucket the tool had" — the sentence this round corrected in the code. The dev's pr_body_corrections list is the replacement text and its six items match what landed.
  4. Dev's named deviation (no PATCH of the body, per os-dev.md outranking the dispatch wording). Answered: correctly named rather than silently resolved; no escalation.
  5. No open_questions. Nothing to escalate.
  6. CI convergence — in-flight, counted neither way. At this head 31 check names: 22 success, 8 skipped, and Lint & Repo Gates is in_progress — the job that runs pnpm check:pm-dispatch-gates, i.e. this block's own battery. TypeScript Type Check and all four Type Check · lanes are success. An honest reading at report time; the seat verifies both required jobs success before flipping ready or queueing.

Implemented-by: claude/issue-19172-typecheck-lanes-not-measured
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: PASS


Generated by Claude Code

@huangyiirene
huangyiirene marked this pull request as ready for review September 22, 2026 03:23
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 22, 2026
Merged via the queue into main with commit 80ca0b1 Sep 22, 2026
40 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-19172-typecheck-lanes-not-measured branch September 22, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants