Repository navigation
feat(inflight): report when an oversized class is cheap to decompose, and what is blocking it - #412
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
✅ Duplicate Code ReportTwo engines run in parallel for cross-validation. Each has its own thresholds tuned to its baseline - the real safety net is the per-engine "max increase vs base" check. ✅ PMD CPD
No new clones introduced by this PR. ✅ jscpd (language-agnostic)
No new clones introduced by this PR. Powered by astubbs/duplicate-code-cross-check |
|
[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its Superseded by a newer quarantine lane report. |
🟢 Throughput — OKThis branch measured about 16% slower than master, on the one test this measures. That is INSIDE this test's own run-to-run spread of about 17%, so read it as a reading and not as a result - re-running the same commit moves it by about as much.
Allowable range 🟢 ≥ 0.70 · 🟡 0.50–0.70 (about a 30% loss) · 🔴 < 0.50 (about a 50% loss) What the numbers mean, and what they cannot tell youThe one that gets misread. Why a shape and not a rate. A rate depends on which runner you drew. A shape does not: every test here processes a fixed number of records, so a runner twice as slow doubles the subject and the controls together and leaves their ratio alone. That is the whole trick, and it is why the reported rate is shown last and labelled as this machine only. Reading the comparison. By conservation, not by correction. Every test in this lane processes a fixed number of records, so within one run the ratio of one test's time to another's is invariant under machine speed — a runner twice as slow doubles both terms and leaves the ratio alone. There is no machine-index correction to be wrong, because nothing needed correcting. Per-method times, not class times. A class time is Reference is the median of 5 recent What this still cannot do. It removes machine-to-machine variance. It does not remove this test's own run-to-run variance, measured at about 30% on a single unchanged commit while its controls stayed within 5%. That is a property of the test, not of the comparison, and no arithmetic here can touch it — which is why the reference is a median and the bounds are deliberately coarse. 🟡 means look at this; only 🔴 is outside the measured spread. Since the previous push: ratio 0.962 -> 0.838, share 1.646 -> 1.92, rate 63188 -> 64250 (+1.7%). One push of difference sits inside this test's measured spread - read it as movement, not as a result. Updated for |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #412 +/- ##
============================================
- Coverage 81.45% 81.15% -0.31%
- Complexity 0 1367 +1367
============================================
Files 83 90 +7
Lines 4448 4833 +385
Branches 456 464 +8
============================================
+ Hits 3623 3922 +299
- Misses 644 727 +83
- Partials 181 184 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@claude review this Steer, because a local multi-agent review already ran and the useful thing now is a fresh pair of eyes on what it changed rather than a repeat of what it found. Already found and fixed on this branch, so please do not re-report unless you disagree on the merits:
Where a fresh look is most likely to pay:
Known and deliberately deferred, listed in the PR description: the injected hook text is not truncated or labelled as repository data; the new |
|
Claude finished @astubbs's task in 6m 54s —— View job Review completeWent through the four flagged areas directly (not re-checking the already-fixed list). Two real findings, both fresh; the rest of each area held up.
1.
|
…nd cover the branch nothing patched Both from the requested review on #412. THE DISCARDED RESULT. `measure()` checked `ok` on every git call except the baseline blob lookup, which is precisely the omission AGENTS.md names ("a bare call reads identically whether ignoring the result was a decision or an oversight") and which read as an oversight sitting three lines above this file's own "COUNTED, NEVER JUST SKIPPED" comment. The reviewer was right that no wrong verdict follows from it - a failed lookup leaves baseBlob undefined, the identical-to-baseline fast path stops firing, and every ref falls through to a real diff that is still correct - but "correct by accident" is the state the rule exists to make visible. It now fails the candidate the way its neighbour already did. THE UNTESTED BRANCH. The rewrite of `addedSinceMergeBase` onto the shared primitives added `if (!mb.ok) return null`, which is the line separating "absent at the merge-base" from "git could not be asked" - the old `rev-parse` conflated them, so a failed query made `note drift` report an existing note as created by the branch. Every mutation check for that rewrite patched refactor-window.mjs and none patched notes.mjs, so a future edit could have restored the conflation silently. The reviewer offered a tracking note as the alternative; a real test turned out to be reachable instead. A path containing a newline injects an extra line into `cat-file --batch-check`'s input, so git returns fewer answers than refs and `blobsForPath` reports ok:false - a genuine input rather than a stub, and the mutant that removes the guard goes red on it. The review's other two areas came back clean and are recorded here because a negative result is worth as much as a finding: no fourth silent-open path exists in the measurement, and the fixtures are properly isolated from this repository's branch shape.
3ff153c to
3e983b1
Compare
…se right now docs/refactoring.md says its entries are to be picked up "when things are quiet", and nothing in the repository could evaluate that. So the entries aged: its entry for AbstractParallelEoSStreamProcessor records 1533 lines, and the file is now 2406. Every author who touched it faced the same undecidable question and made the same locally correct choice to add rather than extract. `inflight refactor-window` answers it for the files listed in bin/refactor-candidates.json, names the branch and pull request standing in the way when the answer is no, ranks the candidates by distance to open, and says whether each is still growing. THE SIGNAL IS THE LARGEST SINGLE DIVERGENCE any live branch holds against the mainline - not the number of branches touching the file. Chosen against a measurement rather than a hunch: PartitionState had dozens of live branches with an open pull request diverging from it and the largest of those divergences was EIGHT LINES. A count calls that file blocked with nothing in its way. The maximum is also what gets reported, because the operator's alternative to waiting is to go and land that one branch. THE MEASUREMENT WAS WRONG ON THREE OF FOUR CANDIDATES BEFORE IT WAS RIGHT, and that is the part worth keeping. A candidate lists two paths because the package rename is in flight; they are two NAMES FOR ONE FILE. The first implementation resolved the merge-base version under only the path the tip happened to use, so a branch that had DONE the rename found nothing at its merge-base, fell through to "this branch created the file", and scored the ENTIRE file as added. The largest candidate reported +1609 on a rename branch whose real divergence is +92, and named that branch as the one to land - while the true blocker, +1047 on another branch with an open PR, went unreported. Same shape on ParallelConsumerOptions (+598 reported, +393 real) and WorkManager (+392, +190). The inflation is systematically upward, so it pins windows shut rather than opening them falsely, which is exactly why nothing looked broken and no test caught it. It was found by a reviewer re-deriving the measurement independently. A MEASUREMENT THAT NEVER RAN IS NOT A QUIET TREE. Four ways that could have been reported as "go ahead and refactor" are closed: a null baseline in a shallow or single-ref clone (baseline() documents that returning null forces the caller to say so, and the caller did not); a ref whose merge-base cannot be resolved, which was dropped from both the maximum and the count meant to make absences visible; a candidate no live ref carries under any configured path, which is what a retired path spelling will look like once the rename finishes; and a failed blob lookup read as an absence, which appeared in three separate places and rendered variously as a fabricated whole-file divergence, a note created by this branch, and a file that "did not exist" six months ago. THE GROWTH LINE IS DERIVED, NEVER STORED. Recording a snapshot per run was considered and rejected: the writer would be a hook firing on every session start and every push, mutating a tracked file across a dozen worktrees, and git already holds every past length exactly. The anchor is a rolling window rather than a recorded date, so there is nothing to keep current - which is the whole point, since the number it replaces is the one in docs/refactoring.md that went stale by 873 lines with nothing to say so. THRESHOLDS ARE A JUDGEMENT, and the config says so along with the two values that were wrong: a tenth of file length was a guess dressed as a rule, and a half read OPEN on the largest candidate while a branch held +1047 against it. Scaling to file length is a weak proxy in any case - conflict pain comes from OVERLAP between the extraction and the branch's changes - so the first window that actually opens will be the first real evidence about it. Cost is 2.3s for four candidates over 437 live refs, after three rounds of arrangement that changed no answers: hoisting merge-base out of the per-path loop, batching ~3,500 rev-parse calls into one cat-file per path, and memoising the line count on blob identity. Measuring against origin/master directly would be faster and wrong - it counts what master gained since a branch forked as though the branch wrote it. mergeBases and mergeBaseBlobs live in git.mjs rather than here because they are pure plumbing with no knowledge of what a refactor candidate is, and that file's header is written about exactly this: "source this, do not copy from it". notes.mjs's addedSinceMergeBase is rewritten onto them, so `note drift` gets the batching for free and stops conflating "absent at the merge-base" with "git could not be asked". Every self-test here is negative-controlled, and the behavioural ones drive the measurement against purpose-built git fixtures - a package rename in miniature, an orphan history, a repository with no master. Nothing drove it against a real repository before, which is why all of the above shipped. It sits in the front door's registry beside `codecov`, which master landed while this was in review; both were added to the same table in the same window, and both survive.
… the edit A rule in a file fires only if somebody opens the file, and nobody opens a backlog speculatively to ask whether today is the day. These two hooks are the half that fires on its own. remind-refactor-window.sh is ONE script registered TWICE. SessionStart reaches whoever is choosing what to work on; the pre-push arm reaches the moment a piece of work finishes and the next one gets chosen. Neither covers the other - an agent that never starts a fresh session still pushes, and a person who never pushes still opens sessions. check-branch-behind-its-own-remote.sh is the precedent for the double registration and for reading hook_event_name to tell the arms apart. It does NOT reach a Task-spawned subagent on the SessionStart arm; inject-branch-context.sh records that measurement, and the other surfaces cover that case. nudge-refactor-candidate.sh reaches the moment that actually grew the class: somebody with a change to make, for whom adding is the locally correct move and extracting is a decision nobody prompted them to consider. It is advisory and cannot block, ask, or delay - CONCEPTS.md sets that failure budget, and a false positive must never cost anybody a turn. It is the first hook here to match anything other than Bash or *, so the matcher is driven by the self-test with real payload shapes rather than assumed. SILENCE IS EARNED, NEVER ASSUMED, and this is why the scripts are shaped the way they are. A hook whose correct output is silence is byte-identical to a hook that is broken - the rule inject-branch-context.sh states as "DEGRADED READS ARE LOUD, NEVER SHORT". Three states, three outputs, each pinned by a self-test: nothing open is empty, a failed measurement is loud, and a hook that could not start is empty again, that last being the one silence an advisory hook is allowed. Getting there closed three ways the second collapsed into the first: an unusable TMPDIR turned the failure report into silence, the push arm replaced the whole report with its loud line and threw away the candidates that had answered, and the command's own invalidating warnings were suppressed in exactly the mode both hooks use. REPEATING IS THROTTLED ON CONTENT, NOT ON TIME. The report is meant to keep saying so until the work is done, but the identical paragraph at every session start for days is how CONCEPTS.md says a reminder trains its reader to skip it. The stamp holds a digest of what was last said: unchanged, quiet for twelve hours; changed, it speaks at once. A plain timer was the obvious implementation and is wrong - it would delay a newly opened window by up to the whole window. The edit nudge is throttled per candidate for the same reason. hook_project_root had its derivation order backwards in its first form, preferring $CLAUDE_PROJECT_DIR - which describes the SESSION, not the command. That is the defect docs/solutions/workflow-issues/a-hook-processes-own-directory-describes-the-session-not-the-command-2026-08-31.md records and #382 fixed for two other guards, and putting it in a shared helper would have institutionalised it. The payload's cwd now wins, and both hooks invoke the tool by absolute path - which also closes a hole where, with the variable unset, the payload chose which program ran. Registering a hook here is a documentation change too, and that is gated: bin/test-check-agent-hooks.sh asserts docs/agent-harness.md's prose hook counts against settings.json and that every registered hook is named in a self-test.
…can answer docs/refactoring.md has always said its entries are to be picked up "when things are quiet", and nothing could evaluate that - which is most of why its entry for AbstractParallelEoSStreamProcessor still records 1533 lines against a file that is now 2406. Now that the tool answers it, the documents that own the surrounding territory need to say who owns which part. docs/inflight-tool.md gets a worked example in that document's own shape: the command, what the answer looks like on a real question, and why the working-tree version is wrong. Here the working-tree version cannot be given at all - the question is about other branches by definition, and 160 of the refs carrying the largest candidate carry it only under its pre-rename path. docs/refactoring.md gets the pointer and the ownership split stated explicitly, because both files can hold "what to pull out" and would otherwise drift: this document keeps the full case for each candidate, the config keeps the one line an agent is shown mid-edit. Adding a candidate to the config without an entry here would leave a machine that knows to nag and a reader who cannot find out what for. CONCEPTS.md gains "refactor window". A refactor candidate is a file the project has already DECIDED to decompose - the decision is the entry condition, so a large file nobody has committed to breaking up is not one - and its window is the period in which no large in-flight change stands on it. The entry records why the measure is a largest-single-divergence rather than a count of branches, because that distinction is what the whole mechanism turns on and it is not recoverable from the code. No AGENTS.md change: its table already routes docs/inflight-tool.md, and the rule is situational rather than binding on every session. The plan is recorded as written, including where implementation contradicted it - the predicted cost was out by a factor of seven, and its acceptance example for the unmatched-path count understated the number. THE WORKED EXAMPLE SHOWS REAL CURRENT OUTPUT, and getting there is worth recording: an earlier draft of it quoted the numbers from before the measurement was fixed - +1609 against a threshold of 240, naming a rename branch as the blocker. Those were the artefact this work exists to have removed, sitting in the document that teaches the command, and nothing would have gone red about it. It carries an `issue-refs: exempt` marker because real output legitimately contains a bare pull-request number that the tool prints verbatim.
ffaf1a8 to
b5cae03
Compare
[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its Updated for Superseded by a newer quarantine lane report. |
|
@codex review Fresh eyes wanted, and worth saying what has already been looked at so the effort goes somewhere new. A local multi-agent review and a Where a genuinely independent look is most likely to pay:
Known and deliberately deferred, so please do not spend time re-deriving them: the injected hook text is not truncated or labelled as repository data; the new |
[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its Since the previous push: Updated for Superseded by a newer quarantine lane report. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b5cae03151
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…pen over a ref nobody measured Eight of ten findings from the Codex review on #412. One is rejected below with evidence and one is left open as a decision. THE FOURTH ROUTE TO THE FALSE PASS, and the one three earlier reviews walked past. `out.open` consulted `unanswerableRefs` only when NOTHING was measurable, so one connected branch at +1 alongside one unrelated branch produced `open: true` over a ref nobody had looked at. Any unanswerable ref now blocks an open verdict, and a candidate where EVERY diverging ref was unmeasurable is a failed candidate rather than a quiet one - left as `ok: true` it rendered as neither open nor failed, so `--if-open` emitted the empty string, which is the silence reserved for a completed measurement over a quiet tree. THE SIGNAL IS NOW WHAT A BRANCH TOUCHES, added plus removed, not what it adds. Counting additions alone scored a branch that deletes or moves a large block at nearly zero - and that is exactly the branch a decomposition collides with, since both sides are rewriting the same region. This is not cosmetic: it inverts the ranking on this repository. work-manager was reported nearest to workable at 1.9x over; counting removals it is the FURTHEST at 4.7x, and the largest candidate moves from second to nearest. The old figure was answering a different question from the one the tool asks. A REPEATED FAILURE IS NO LONGER THROTTLED INTO SILENCE. The twelve-hour repeat window keyed on content alone, so an identically-failing measurement was suppressed and the next session received the exact silence that means "nothing to say" - the collapse this whole feature exists to prevent, reintroduced in the mechanism added to stop the nagging. Only a successful report is now eligible, and only a successful report writes the stamp. The pre-push preamble likewise follows the outcome: every non-empty report used to get the success wording, so a failure-only report told the model a file "is currently cheap to decompose" when no open candidate had been established at all. A FAILED HISTORICAL LOOKUP IS NO LONGER A HISTORICAL FACT. `firstBlobAtAnyPath` returned `null` both when a query failed and when the file was genuinely absent, and the view rendered the latter as "did not exist then" - so the `found.ok` check added earlier was an incomplete fix. Failure is now a distinct sentinel and drops the growth line rather than fabricating one. The growth cutoff also follows the baseline's first-parent history: unrestricted `rev-list --before` could land on a merged side branch whose tree was never the mainline's state. And the silent form now writes zero bytes. `emit('')` is `console.log('')`, which writes a newline, so the documented silence produced two bytes. The self-test that asserted it used command substitution, which strips trailing newlines and could not see it. REJECTED, with evidence: "validate merge bases before the identical-blob shortcut". The shortcut compares a ref's blob to the BASELINE'S CURRENT blob, so byte-identical content is zero divergence in that file whatever the merge-base is - a refactor cannot conflict with an identical copy. Counting those refs as unanswerable would manufacture failures. Its sibling finding about the verdict is correct and is fixed above; same reviewer, one holds and one does not. Tests: four new checks, each negative-controlled, covering the deleting branch, the one-unmeasurable-ref verdict, the throttled failure and the failure-only preamble. Fixing them exposed that the hook cases were coupled through a stamp file in TMPDIR rather than through code - the push-detection case was being silenced by a report an earlier case had written, and now clears it first.
🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its No quarantined test changed outcome since the previous push. Updated for |
…ion sits in `git -C /other/worktree push` publishes THAT repository, and the refactor-window hook measured the session's instead. Both failure directions are silent: the hook stays quiet because this tree has no open window while the pushed one does, or it injects this tree's advice into a push that has nothing to do with it. In a repo whose own rules say every task gets its own worktree, pushing from one directory while sitting in another is the ordinary case, not the exotic one. The fix that was NOT available was widening `hook_git_invocations`: `-C` is a git GLOBAL option, so it precedes the subcommand and that helper - which emits the subcommand and its arguments - parses it and drops it. Three other hooks read that output, so changing its shape to answer a fourth hook's question puts the blast radius on the wrong side of the ledger. `hook_git_dash_c` is a companion instead. It reuses the identical shlex configuration - the same `punctuation_chars`, the same `whitespace_split` - so the two helpers cannot disagree about where a command ends, walks tokens for a `git` basename, and prints the `-C` target only when the subcommand matches the one asked for. `git -C /a/b status` yields nothing when you ask about `push`, and a chained `git add -A && git -C /x push` still yields `/x`. Two negative-controlled tests, each against a real second repository whose `bin/inflight.mjs` stub prints a different marker, so the assertion is which repo was measured rather than whether the hook ran: a `-C` push measures the pushed repo, and a bare push still falls back to the session's. Closes the last open thread on #412.
One conflict, and it was two correct sentences disagreeing: both sides added a hook and both updated the count in docs/agent-harness.md. This branch had made it sixteen scripts across nineteen registrations; master's #413 had made it fifteen across seventeen. Neither number is right after the merge, so taking either side would have shipped a stale count into the one paragraph that says "the count is stated because it drifted". Resolved by counting the merged `.claude/settings.json` rather than by picking: seventeen hook scripts across twenty registrations. Seventeen and not sixteen because master's addition is a `.mjs` - `inject-solutions-for-named-components.mjs` is a hook script that the habitual `*.sh` sweep does not see. Audited both files the conflict list named. All four new registrations survived in settings.json - this branch's two `remind-refactor-window.sh` entries and one `nudge-refactor-candidate.sh`, plus master's `.mjs` - and the only line the merge removes from agent-harness.md is the count sentence it replaces. Master brings #262's transactional-guarantee work, which touches `AbstractParallelEoSStreamProcessor.java` - a refactor candidate this branch tracks - so the tool's reported figures for it move with this merge.
Brought in so a flake sighting from this PR's own CI can be recorded where its ledger lives: #262 added docs/inflight/test-mdc-empty-context-precondition-is-order-dependent.md and the matching row in docs/inflight/test-untracked-ci-flakes.md on master, and neither existed on this branch. Recording the sighting anywhere else would have been a third copy. Inherited commit bodies read before merging: #412 (refactor-window in bin/inflight.mjs and a pre-push hook arm, not the invoked-directly guard), #413 (a Write/Edit PreToolUse hook), #262 (transactional guarantees register, produce-lock guards, the strategy claim, and the findings note that carries the MDC flake), #417 (retires the #262 inflight note). None of them touches the decisions on this branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UH2q829FDKQcqQseE8Cp7q
Master gained #262 (the transactional battle test), #400, #409, #412 and #413 since this branch last merged it. This branch had carried #262's commits unsquashed, so 16 files conflicted, and all but three were that PR's own earlier text against its landed text. Resolutions, so the next reader does not re-audit them: - Whole file from master, verified byte-identical afterwards: PollContextInternal.java (the produce-lock one-release guard, final form), TransactionalBatchVisibilityIT, TransactionalCrashReplayIT, TransactionalClaim, TransactionalBulkCommitTest. Only #262's own commits had touched any of them on this branch. - Hunk-level, master's side of every hunk: AbstractParallelEoSStreamProcessor (cleanUpContext's catch, final form - this branch's own changes to the file auto-merged), README.adoc, README_TEMPLATE.adoc, STRATEGY.md, docs/quarantined-tests.md, the three docs/inflight notes and the 2026-08-07 battle-test plan. - ProducerManagerTest: master's imports and the removal of buildModule, master's three produce-lock release tests folded in from the deleted ProduceLockReleaseTest, and this branch's five revocation tests appended after them. The auto-merge also left two copies each of acquireProduceLockInto and assertProduceLockStillOwnedByContext; the older pair is deleted. Found by the compiler this time, not by a count. - ProduceLockReleaseTest deleted, as master did: #262 folded it into ProducerManagerTest. This branch's edits to it were #262's own review commits. ProducerManagerTest, PollContextInternalTest, ArchitectureTest and the TransactionalClaim register pass on the merged tree. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVL2FEJ645T76PbybEBUZ6
Description
docs/refactoring.mdsays its entries are to be picked up "when things are quiet", and nothing in the repo could evaluate that. So the entries aged: its God-class entry recordsAbstractParallelEoSStreamProcessorat 1533 lines, and it is now 2405. Every author who touched it faced the same undecidable question and made the same locally correct choice to add rather than extract.This adds
node bin/inflight.mjs refactor-window, which answers it for the files listed inbin/refactor-candidates.json, and two hooks that deliver the answer without being asked.The signal is the largest single divergence any live branch holds, not the number of branches touching the file. That was chosen against a measurement:
PartitionStatehad dozens of live branches with an open PR diverging from it and the largest of those divergences was eight lines. A count calls that file blocked with nothing in its way. The report names the branch and PR holding that largest divergence, because the operator's alternative to waiting is to go and land it.What it says on this repo today:
Shape, not a transcript: every figure there moves with the refs.
node bin/inflight.mjs refactor-windowprints the current ones.Three surfaces: the command; a hook on session start and pre-push that prints nothing unless a window is open; and an advisory nudge when an agent edits one of the candidates, throttled per candidate.
The part worth reviewing hardest
A multi-agent review found that the first working version reported confident, plausible, wrong numbers on three of the four candidates. A candidate lists two paths because the package rename is in flight - they are two names for one file - and the merge-base version was resolved under only the path the tip used. A branch that had done the rename therefore found nothing at its merge-base, fell through to "this branch created the file", and scored the whole file as added. The God class reported +1609 on a rename branch whose real divergence is +92, while the true blocker went unreported.
Nothing was red. It surfaced only because a reviewer re-derived the measurement instead of trusting the output.
Three further paths to a false pass were closed with it, all of the same shape - a measurement that could not run reporting as a quiet tree: a null baseline (shallow or single-ref clone) made every candidate report OPEN; a ref whose merge-base cannot be resolved was dropped from both the maximum and the count meant to make absences visible; and a candidate that no live ref carries reported OPEN forever, which is what a retired path spelling will look like once the rename finishes.
Added since this description was first written
node bin/inflight.mjs refactor-windowrather than from here.docs/refactoring.md's own "1533 lines" did.docs/refactoring.mdplus six in-flight notes — that heading is cited by name from two other documents, so the sweep is a follow-up of its own.Deferred, and why
Edit|Write|NotebookEditmatcher is verified live but not recorded as version-verified indocs/agent-harness.mdthe way theTask/Agentdivergence is.loadCandidatesaccepts a path containing whitespace, which then resolves to nothing. The underlyingblobsForPathsplitting behaviour is pre-existing; this documents it rather than widening scope.Checklist
docs/inflight-tool.md(worked example),docs/refactoring.md(pointer + ownership split),docs/agent-harness.md(hook counts),CONCEPTS.md(refactor window)docs/features/- N/A - internal tooling, not a library featurebin/test-inflight.mjsis negative-controlleddocs/inflight/working note (pr-/branch-) started at the PR's first commit - N/A - none was started, and none is needed now: the work is complete on the branch,docs/plans/2026-09-02-002-feat-refactor-window-signal-plan.mdcarries the decisions and the deferred items, and the rest is whatghshowsce-simplifyandce-code-reviewlocally - both; the code review is what found the measurement defect above