Skip to content

ci: announce a quarantined test that started passing, and share the sticky-comment machinery - #409

Merged
astubbs merged 69 commits into
masterfrom
ci/quarantine-report-status-changes
Sep 2, 2026
Merged

astubbs merged 69 commits into
masterfrom
ci/quarantine-report-status-changes

Conversation

@astubbs

@astubbs astubbs commented Sep 2, 2026 •

Copy link
Copy Markdown
Owner

Was stacked on #400 for the front door its Codecov commands are a row in;
that has now merged, so this targets master again and the dependency is discharged.


Follow-up to #407, which did this for the throughput comment. The quarantine lane report had the same problem and the fix is now shared rather than copied.

The problem

A quarantined test that starts passing means its fix landed and its @Quarantined annotation plus registry entry should be deleted. That transition was announced by silently editing a comment far up the page — no notification, no visible change to anyone reading the PR top to bottom. The most consequential thing this report can say was the thing least likely to be seen.

What changed

The report now embeds a machine-readable payload, renders a delta naming which test moved, posts a fresh comment when any row's outcome changes (retiring the old one first, linking forward), and stamps every comment with the 7-char head sha, a PR-context commit link, the run and the time — so an in-place update is visibly fresh rather than looking dead.

Shared, not copied

All five mechanics moved to .github/scripts/sticky-report-comment.js: paginated Bot-only lookup, payload read-back, update-vs-post-fresh, retire-before-create, and the stamp. github-script require()s it the way pr-checklist.yml already loads four gate modules.

Three consumers. throughput-report-comment.js and quarantine-report-comment.js hold each report's own vocabulary. The throughput step in maven.yml went from ~186 lines of inline JS to two. The SpotBugs step uses the shared lookup and stamp but keeps its own update-or-create — whether a clean→dirty SpotBugs transition deserves a new comment is a judgement nobody has made, and this PR does not make it unasked.

#407's reasoning comments moved verbatim rather than being summarised — including the whole measured argument for why the stamp cannot link to a #commits-pushed anchor.

Two live defects found on the way

  • The shell reporter's own gh api upsert had no author filter — the same class that let the SpotBugs step overwrite a human's comment.
  • A failed write under set -e took the whole step down, so a rate limit while commenting could red a job whose lane ran fine. Posting is now a separate continue-on-error step; the classifying step stays able to fail, because its lane-leak self-check must not be swallowed. That step now also runs on push-to-master, where the leak check never ran.

How the tests were proved able to fail

Nine defects reintroduced into the shared module one at a time — missing Bot filter, unpaginated lookup, prev && cur &&, create-before-retire, 9-char sha, bare commit URL, no marker rename, retirement note naming a place, rethrown forward-link failure — each flipping only its own assertions. Four more in the shell script.

One caught a repeat of #407's own mistake. The first "payload did not land inside the prose" test split on the marker and asserted the word was absent above it — and stayed green when the payload was spliced into the heading line, because with the marker on line one there is no prose above it. It now asserts position.

The end-to-end case matters most: the marker name and outcome vocabulary are a shell↔JS contract with no gate, and every hand-written payload agrees with itself by construction. quarantine-report-comment.test.js runs the real reporter three times against a fixture whose outcome flips and feeds its actual output through the reader. Renaming PASSED_ACTION or the payload marker in the shell script fails it and nothing else.

pr-checklist.yml's four named self-test steps became a glob over .github/scripts/*.test.js — this module would otherwise have been the fifth thing nobody wired up.

Recorded, not fixed

  • docs/inflight/ci-quarantine-lane-not-run-cannot-name-a-broken-build.md — NOT_RUN cannot distinguish "not selected" from "the build broke before integration tests", and steps.run.outcome cannot help because it is failure on almost every run by design. A design decision, so it is written down rather than guessed at.
  • The stale PIT and Codecov comments on open PRs: archaeology done first. PIT's comment-posting was removed deliberately, so resurrecting it would reverse a decision — the stale comments need deleting by hand and no code will ever touch them. Codecov needs someone with app.codecov.io access; uploads demonstrably still work.

Both notes record where the search looked and what each place returned, including the several that returned nothing.

Left alone deliberately

claude.yml and claude-code-review-dispatch.yml grant the reviewer two .test.js files by name. Broadening that to the glob would let a reviewer run PR-authored JavaScript — a security-shaped decision, not made unasked.

Checklist

  • Docs updated — two inflight notes, and the reasoning lives in the module headers
  • User-facing feature documentation data added under docs/features/ — N/A - CI reporting, not a library feature
  • Tests added/updated — a shared-module suite, an end-to-end shell↔JS contract test, and the self-test glob
  • docs/inflight/ working note started — ci-quarantine-lane-not-run-cannot-name-a-broken-build.md plus the PIT/Codecov note
  • Title & body reflect the final content of this PR
  • Ran ce-simplify and ce-code-review locally — N/A - asking for @claude review this on the PR instead

Added since the original description

Codecov, reachable from the front door. bin/inflight.mjs codecov exposes the per-test outcome
and wall-clock history Codecov keeps per commit - codecov test <name> for "which commit did this
change at" (a bisect that costs one HTTP request), codecov flaky for the sighting evidence a
quarantine entry needs, codecov slow for what owns the CI wall-clock. Unauthenticated, because this
repo is public, so it works from a fresh sandbox with no setup step. Four self-tests with negative
controls, driving the real logic on fixture rows rather than the network. The post-push hook now
names these commands, since the moment an agent has a red check is when they are worth reaching for.

The test-results upload was ignoring its own configuration. The files: globs were passed
literally to a CLI that does not expand **, matched nothing, and the CLI fell back to a tree-wide
*test*.xml search - ingesting 18 logback config files alongside 39 genuine reports, entirely
green. Now expanded with find, with disable_search: true as a second lock, and moved off the
deprecated test-results-action onto codecov-action@v5.

CodeQL HIGH (js/bad-tag-filter) fixed rather than dismissed. The superseded marker was derived
by regex-parsing an HTML comment terminator. Not exploitable - the marker is a literal we own - but a
marker and its superseded twin are two constants of a report, not one computed from the other, so the
regex is deleted rather than hardened and the whole class is gone from the tree.

Known-failing, and NOT introduced here

bin/test-inflight.mjs reports mutant of explicit-help-succeeds goes red as FAILED. This
pre-exists on the parent PR
- confirmed by a control: stashing every change on this branch and
re-running against that tip fails identically.

Root cause: import.meta.url resolves to the realpath while process.argv[1] does not, and macOS
os.tmpdir() is a symlink (/var/folders/... -> /private/var/folders/...). So inside the
self-test's temp dir the "invoked directly" guard at the bottom of bin/inflight.mjs is false, the
CLI body never runs, and inflight help prints nothing and exits 0. Changing only the path form
flips it - /var/... gives exit 0, the realpath gives exit 2.

Worth more than the one red line: on macOS the other invoke()-driven mutants pass for the wrong
reason
, since a CLI that never ran returns exit 0 and empty output, which differs from what they
expect and so reads as "red" without the mutation being exercised. Invisible in CI because Linux
/tmp is not symlinked. It belongs on the parent PR and is deliberately not patched from here.


Added since the description above was written

A shared test harness. The three report-comment suites arrived carrying three copies of the same
runner and fake GitHub client, two of them byte-identical. report-comment-test-harness.js collapses
them — deliberately keeping the RICHEST version rather than the smallest common one, because sticky's
copy recorded call ORDER (retire-before-create is invisible to any assertion about final state) and
rejected an async body handed to the sync test(), which can never fail. Sharing down would have
deleted both; instead the other two suites gained them.

Codecov is documented (docs/ci.md). It had zero mentions of a service carrying coverage uploads,
a separate test-results upload, four status checks, codecov.yml, and an API client. CODECOV_TOKEN
was documented in AGENTS.md and lost in the restructure.

Review fixes. Seven local reviewer lenses, plus an independent cross-model pass on Codex and a
Claude review. Codex found seven defects the local lenses all missed, every one a variant of the same
failure: codecov flaky could exit 0 with an apparently complete "no flakes" — in the exact situation
a reader uses it to justify REMOVING a quarantine. All fixed, each with a red-proofed test.

Known-failing, diagnosed, and deliberately not fixed here

codecov/project/unit (−4.46%) and codecov/project/integration (−15.32%) fail on branches that
change no Java. Cause proven from the upload logs: the uploader's CLI does not expand **, so
files: matches nothing and it falls back to a tree-wide search. On master's full build that finds
every jacoco report — both halves — so unit and integration each receive the whole tree and report
an identical number. On a PR each suite job has produced only its own half. The delta measures that,
not the branch.

It is the same inert-files: defect this PR fixes for the test-results upload; four instances
remain, all coverage uploads. Not fixed here because it changes what a REQUIRED merge gate measures,
which should be the only thing in its own diff. Written up in docs/ci.md and
docs/inflight/ci-the-coverage-uploads-still-use-the-inert-glob.md.

Also filed rather than fixed: docs/inflight/ci-emptying-the-quarantine-lane-leaves-its-last-report-live.md
— deleting the LAST quarantine entry leaves the previous report live saying ACTION REQUIRED, because
every step after the empty check is gated on the lane being non-empty.

bin/test-inflight.mjs fails two mutation checks inherited from #400 (a
realpath/symlink guard; macOS-only, invisible in CI). Not introduced here — confirmed by control.

astubbs and others added 30 commits August 19, 2026 06:40
…ule, like every other collaborator

PCModule already provides workManager, brokerPoller, producerManager and consumerManager; its own
javadoc calls it the DI system and PCModuleTestEnv is the established seam for substituting a
collaborator in tests. ShardManager was the exception, constructed inline by WorkManager, so it was
the one collaborator no test could substitute.

This completes that pattern rather than adding a hook for the test that needs it: no test-only
branch, no flag, no latch in production. The provider takes the owning WorkManager as a parameter
because the two are mutually dependent and WorkManager's constructor is mid-flight when it resolves
this - calling workManager() would recurse.

The caching is a plain null-check rather than anything thread-safe, which is sufficient because
WorkManager has exactly one construction site and construction is single-threaded. Worth knowing
before a second call site is added.

Enables the reproduction in the next commit, which must swap in a ShardManager that can park the
control thread mid-insert. Verified not to disturb anything: full core unit suite green.
…n, and the third precondition it uncovered

#31 shipped with unit-level proof and an openly declared gap: no load-level evidence. This
closes it with something stronger than a soak - a broker-real reproduction that discriminates every
run, with no seed and no sleeps.

Building it uncovered why nothing had reproduced the defect before. The loss needs THREE
coincidences, not the two previously recorded: a rebalance inside the registration insert loop, the
partition returning so the uncommitted tail re-delivers, and - the missing one - NO take-scan of the
shard in between. ProcessingShard.getWorkIfAvailable lazily evicts stale containers as it scans, so
an ordinary take-scan HEALS the collision before a fresh arrival can hit it. Only a consumer whose
pipeline is at target keeps residents alive long enough to eat a fresh record.

That explains the standing puzzle: the Chaos Pain Suite is green on defective master not through an
unlucky seed but because its own churn is the healing mechanism. Random reproduction was fighting
itself. It also narrows the production risk - this bites BUSY consumers, not idle ones.

The test drives all three deliberately: a gated user function saturates the pool so the control
loop's delta stays at or below zero, a fixed message-buffer size pins the load factor so the target
cannot step mid-experiment, a second group member on another topic forces an eager revoke/re-assign,
and the insert is parked mid-loop until re-delivery has fully registered. Every await is event-gated
- latches, listener callbacks, instrument counters - so a failure is loud rather than slow.

Proven both ways. With addWorkContainer's staleness check neutralised it fails naming the exact lost
records - 25 of them, matching 25 "already exists in shard queue, dropping record" lines at the
defect site. With the fix it passes in ~15s.

Untagged, so it runs in the ordinary failsafe lane: it is deterministic, not chaos. NOT yet hardened
- four green runs total. Wants ~10 CI repetitions before anyone treats it as settled.
…k in flight

The mistake this exists to stop, in full. On 2026-08-19 #31 was merged about ten minutes
before a spawned agent finished building the broker-level reproduction of confluentinc#909 - the
exact gap that PR's own description declared open under "Known gap". The maintainer asked "are the
commits ready?", the answer was given from the PR's state alone, and the background task nobody
mentioned was never weighed. The fix and the evidence proving it now live in two PRs, and an
inflight note went stale on master the moment it landed.

Why a hook and not a line in a document: docs/merge-checklist.md already lists what to confirm
before merging, and it WAS loaded - inject-merge-checklist.sh put it in that very turn. It did not
help, because the question being asked was "is this PR ready?" while the thing that needed
remembering was a task started an hour earlier. A checklist cannot prompt for what you have
forgotten you are waiting on.

The signal is Claude Code's own task output files under $CLAUDE_CODE_SESSION_ID, which are appended
to while a task runs; a file touched inside the window means something is still producing output.
Scoped by session id, so a sibling session never blocks this one. Also refuses while a maven build
is live under .claude/worktrees.

Registered with NO `if` matcher, for the reason docs/agent-harness.md already gives about the squash
guard: a prefix `if` never sees `echo ready && gh pr merge`. It tokenises with shlex instead of
grepping, so `gh pr comment --body "run gh pr merge later"` is not mistaken for a merge - the first
draft did exactly that, which is why the case is now a test.

Its limit is stated in the header and repeated in the denial the model reads: a STALLED agent writes
nothing and is NOT detected, so this is evidence of live work, never proof of quiescence. Two agents
stalled during the session that produced it. MERGE_DESPITE_OUTSTANDING_WORK=1 is the deliberate,
loud override for when the outstanding work genuinely belongs elsewhere.

Nine cases added to bin/test-check-agent-hooks.sh, negative controls included: a stale task file
must NOT block, no session id fails open, an unparseable payload fails open, and the override
releases it. Fails open on its own bugs throughout - a hook that jams the tool call shut is worse
than the mistake it prevents.
… it settles, and file the follow-ups

docs/solutions/ gains a dated companion to the reachability proof. That proof established the race
was reachable but left open why nothing reproduced it; this names the missing precondition - no
take-scan between the stale inserts and the fresh arrival, because getWorkIfAvailable lazily evicts
stale containers as it scans - and draws the method lesson: a soak that does not reproduce is telling
you something about the mechanism, not about your luck. It sits BESIDE the 2026-08-18 record rather
than over it, per the dated-record rule.

docs/inflight/test-909-not-reproducible-by-existing-chaos-scenario.md is DELETED, not annotated. Its
question is answered and its advice ("do not reroll seeds") is now explained rather than observed;
docs/inflight/AGENTS.md's first rule is that a satisfied note kept as a narrative is worse than no
note.

Two follow-ups gain proper inflight homes rather than sitting in the deferred backlog, because both
are known defects an agent should see when scanning that directory:

- bug-retry-queue-orphaned-by-inline-stale-removal.md - getWorkIfAvailable's inline stale eviction
  calls iterator.remove() alone while the sweep cleans both structures and documents that it does.
  The call itself is one line; the reason it is not done here is that RetryQueue has three tests and
  all three cover ordering, so the fix would land into a class with no test that could catch its
  successor. Coverage first, then the fix, in one PR.
- parked-stale-arrival-guard-needs-a-null-safety-decision.md - parked on a judgement, not on more
  investigation: whether an absent PartitionState is not-stale or an error, and whether three
  PartitionStateCommittedOffsetTest fixtures encode a real production shape or a shortcut.

The arrival-guard entry is REMOVED from docs/refactoring.md in the same commit - it now has one
owner, and the same fact in two places is how the two drift apart.
Conflict in bin/test-check-agent-hooks.sh: both sides APPENDED a self-test section - master's
inject-recorded-knowledge.sh (04b23d0) and this branch's check-merge-outstanding-work.sh. Neither
side removed anything, so both are kept, master's first. Checked deliberately against
docs/solutions/workflow-issues/keeping-both-sides-of-a-merge-conflict-resurrects-a-deleted-abstraction.md:
that trap is a deletion on one side being undone by the other, which is not this shape.

Running the merged suite immediately caught a real defect in this branch's own hook, which is what
the negative control existed for. `pgrep -f MavenWrapperMain` also matches every SHELL whose command
line mentions it - the hook's own subshell, and any wait-loop polling for a build. It counted six
"live builds" with none running, which would have denied every merge forever. Now requires argv[0]
to be java.

Worth recording beyond the fix: that self-match is the same bug that made this session's wait-loops
never terminate, since each `until ! pgrep -f <pattern>` matched its own command line. A process
table filtered by pattern alone will find the watcher.
…not fail

From a ce-simplify-code pass over this PR's own changes.

The consequential one is not a simplification. bin/test-check-agent-hooks.sh kept the squash
section's failures in a `fails` counter that the final exit check never read, so the script printed
"FAIL: ..." for a real failure and then exited 0 saying "All .claude/hooks self-tests passed". A
self-test suite that cannot fail is worse than none, because its green is load-bearing in review.
Proven by negative control: with `fails=1` injected, origin/master's script exits 0 and the patched
one exits 1. Folded into the `failures` counter the rest of the file already uses.

check-merge-outstanding-work.sh gains a cheap `*merge*` substring pre-filter ahead of the python3
spawn. The hook has no `if` matcher, so it fired an interpreter on every Bash call for the life of
every session; any command the tokeniser would accept necessarily contains the substring, so the
filter can only skip work it was going to skip anyway - it cannot decide. All self-test cases
unchanged.

PausableInsertShardManager imports TimeUnit rather than fully qualifying one parameter type.
docs/agent-harness.md's row for the new hook is re-worded, because "tokenises rather than grepping"
became half-false once the pre-filter existed - the grep is now a pre-filter and the decision is
still tokenised, and the row has to say so or it teaches the wrong thing.

Deliberately NOT done: extracting a shared merge-detector for the two hooks. check-squash-subject.sh
is untouched by this PR, and the two algorithms differ on purpose - one is a policy check on an
override, the other a state check on the session.
…d inject the plans index

Two things aimed at the same failure: knowledge the agent already has, that does not survive the
turn it was formed in.

next-agent-self-review-as-blocking-pr-comments.md records the owner's proposal - the agent posts its
own pre-merge must-dos as review comments, which GitHub blocks the merge on until each is explicitly
resolved, mirroring how he reviews his own PRs. The admission test is the design, and it is recorded
with the correction that produced it: "something I know must happen before this merges, that I am
not WORKING ON RIGHT NOW". An earlier draft said "not doing in this PR", which is self-contradictory
- work that is not in the PR cannot block the PR. Applied to #31 the rule admits exactly one
comment, the outstanding load-level evidence, which is the thing that was lost.

It also records the noise argument, because that is what kills such mechanisms: a blocking comment
is only worth anything while resolving one is a considered act, so the agent proposes candidates and
the maintainer approves before any are posted. And the prerequisite to check before building any of
it - unresolved-conversation blocking is a repo setting, and if this repo lacks it the comments are
advisory and the whole thing has no teeth.

inject-recorded-knowledge.sh now NAMES the dated plans instead of counting them, and lists the
test-hardening audits. A count tells you a shelf exists without naming anything on it, so an agent
still had to know to run `ls` - which is the exact "never learns the document exists" failure the
hook was written for. Measured while deciding scope: every path under docs/ is ~2.8k tokens, less
than this hook's solutions block alone, so the constraint is signal rather than budget -
docs/features/ and docs/data/ stay out as generated catalogues of shipped surface.

Fixes a delimiter bug found reading that output: `paste -sd', '` treats ", " as a CYCLING delimiter
list, joining items alternately by comma then space, so pairs of entries rendered welded together
("bug-857-family,bug-batch-quantity-over-request bug-max-failure-history-is-inert"). Anyone scanning
for the second name of a pair would not find it.
…not know

The session-start index listed docs/inflight/ as a flat comma-separated run of FILENAMES. Two
problems with that, both measured rather than assumed.

TITLES, NOT SLUGS. Across the whole corpus, 66 of 70 titles carry information the filename does not
- and the information is STATUS and NEXT ACTION, which a filename structurally cannot hold: "done,
no PR", "one result worth keeping, then delete", "may have a contaminated control arm". Knowing a
note exists is not the same as knowing whether it is still live, and the flat list only ever told
you the former. An earlier reading of this said titles mostly restate the slug; that came from a
six-file sample which happened to land on all four of the exceptions.

GROUPED BY COST OF NOT KNOWING, not alphabetically. The prefix IS the type field - docs/inflight/
AGENTS.md says the prefix is the point - so listing titles alone would have thrown it away; the
group heading restores it. Order is bug, ci, test, branch, pr, release, deps, perf, static, web,
next, parked: a known defect in shipped code outranks a candidate idea, and parked work is last
because it is deferred BY DECISION. Alphabetical put `branch-` above `bug-`, which is backwards.

The high-priority notes keep a block of their own above the groups AND stay marked `!!` in place.
Both, deliberately: a `parked-` note that still binds every agent is exactly the one a
category-ordered list buries, and a reader scanning by category should still see which entries are
the urgent ones. The reason is printed once, in the top block.

Seeds the marker on three notes, because a mechanism with one user does not discriminate:
bug-857-family (the fork's longest-running defect, new sightings still landing on unrelated
branches), branch-package-rename (binds ANY agent merging ANY branch - a clean merge without the
rename is silent corruption), and ci-mutation-lane-skip-reads-as-a-pass (a green tick that scored
zero mutants is indistinguishable from a real pass).

Cost is 3.9k tokens, up from 3.3k. Not the constraint: every path under docs/ is ~2.8k, less than
the solutions block alone. The constraint is whether the block stays readable, which is why the
anti-inflation rule on the priority marker matters more than the token count.
…r than a flag

The marker was binary `high`, which meant 66 of 70 notes carried no ranking at all. Group order
already ranks CATEGORIES by what it costs you to not know, but inside a group the entries sat in
filename order - and `next-` has twenty-one of them, which tells a reader nothing about which to
open.

So: three levels, and only `high` has to justify itself. `high` still REQUIRES a reason and stays
rare - that is the anti-inflation rule, and a marker on everything is a marker on nothing. `medium`
and `low` take no reason; they are ordering hints and nothing more. An unmarked note sorts as `low`,
so forgetting the marker is quiet rather than promoting the note.

Two notes join the four already marked high. bug-poisoned-transaction-not-aborted-while-running is
EOS correctness - a terminally failed send leaves the transaction abortable but unaborted.
release-0600-blockers gates the correctness of artefacts we are about to publish. Six of seventy is
a proportion the block survives.

**The medium/low assignments are a first-pass triage and the docs say so.** They were set from
titles rather than from reading all seventy notes, so they are defaults to correct on contact, not
considered judgements - recorded in docs/inflight/AGENTS.md so nobody mistakes the confidence.
Something imperfect beats filename order, and correcting one costs a line. The six `high` entries
are the exception: each was set deliberately, with its reason.
… the code does

The comment on KeyOrderedKey claimed "we use just the topic name here, and not the partition", and
the field was named topicName to match. Both were wrong: the constructor passes
`new TopicPartition(rec.topic(), rec.partition())`, so the shard has always been identified by
(topic, partition, key).

The CODE is right and the doc was the lie, which is the dangerous direction - anyone acting on the
comment would "fix" the constructor to drop the partition and introduce a silent data-loss bug.
ProcessingShard's `entries` map is keyed by OFFSET, and offsets are only unique within a partition,
so two records sharing a key on different partitions would land in one map under one key and one
would be lost without a trace.

The javadoc now says why partition scoping is load-bearing rather than merely asserting it, and the
field is renamed to topicPartition. Safe: the Lombok-generated getTopicName() had no callers
anywhere in the tree, and the only reference was the constructor's own assignment. All modules
compile and the core unit suite is green (387 tests).

Found while reviewing #31; pre-existing and independent of it. Removes the
corresponding entry from docs/refactoring.md.
The backlog entry described a doc that contradicted its constructor. That is corrected in the
previous commit, so the entry is deleted rather than left to describe work already done - a stale
backlog item reads as open.
…ion index by it

Replaces the high/medium/low scale added an hour earlier. A number was my opinion and undefendable;
a class is an observation about what it costs someone to not know, which the next reader can check
and correct. The classes were derived by reading the corpus, not chosen in advance - the set is in
docs/inflight/AGENTS.md with the instruction to add one rather than force a poor fit.

THE ORDER IS THE POINT, AND IT IS NOT SEVERITY. Signal integrity comes first: you cannot judge the
state of the code through instruments that lie, and being misdirected is worse than being blind. So
`misdirection` (a green that asserted nothing, a hidden flake, a contaminated control arm, a scanner
returning 401 while appearing to scan) outranks `blind-spot` (no signal at all), and both outrank
any product defect - including `data-loss`. That is the principle the fork was built on: the tests
were fixed first, because everything else is measured through them.

Grouping by consequence also does something the filename prefix cannot: a class spans prefixes.
`misdirection` currently covers notes filed under ci-, test-, branch-, deps- AND static-, which
prefix-grouping scattered across five headings. The prefix says what kind of file it is; the class
says what it costs you.

An unclassified note is still listed, under its own heading. A marker someone forgot must be VISIBLE
rather than a way for a note to vanish from the index - that silent-drop is the exact failure the
hook exists to prevent.

Self-tests updated and shown to fail on purpose: reversing the class order trips "signal integrity
is listed before product defects", and deleting the fallback trips "an unclassified note is still
listed". The old assertion tracked the priority block that no longer exists.

ALREADY-RUNNING AGENTS WILL NOT SEE THIS. SessionStart has fired for them, so they keep working from
the old index. Run `.claude/hooks/inject-recorded-knowledge.sh` once, by hand, to pick it up
mid-session - a one-off, and only for sessions that predate this commit.
… subagent tiering

Three notes, none of them work items yet - each carries a decision that has to be
made before anyone writes a hook, which is what makes them notes rather than lines
in docs/refactoring.md.

ci-issue-index-has-no-edges: issue-index.md put the tracker inside git grep's reach
and stopped at one issue. Nodes are materialised; edges - what closes what, what
references what - are not, and the edges are where the reasoning lives. Records the
constraints a poke hook has to respect (verified additionalContext channel, no `if`
prefix match, cache-first, rows not prose) so they are not re-derived, and gates the
build on the external-harness survey that process-adopt-external-harness.md defers:
Beads advertises dependency graphs and context injection, so this may be an adoption
decision rather than a construction one.

ci-nothing-asks-what-a-pr-closes: push and merge both have hooks and neither asks
whether the PR resolves something already on the tracker. Two halves - saying nothing,
and saying `Fixes #167`, which closes nothing while reading as correct. Needs
no new data, so it is the first slice.

ci-subagent-model-defaults-to-inherit: omitting `model` on a dispatch silently runs
the subagent on the foreground tier. A nudge cannot fix it - the context arrives after
the launch - so only a deny changes the outcome.

Gates run locally: check-inflight-tags, check-issue-refs, check-file-refs,
check-copyright-headers, check-branch-self-reference.
…han with the decision

process-adopt-external-harness.md defers the adopt-or-build DECISION until after v6.
It also, by implication, deferred the measurement the decision will be made on - which
is the expensive half, and the half that does not have to wait. Whoever picks this up
after v6 would otherwise start from the same README everyone starts from.

Leads with the question that probably settles it and is not a feature question: where
the state lives, and what happens when two branches both change it. docs/inflight/ was
one file until 2026-08-04 and became a directory because every PR edited it; a tracker
storing state outside the tree hands that property back, and no query power pays for
reintroducing the conflict that split the directory. Measured on a real two-branch
merge, before anything else is looked at.

Then four more probes in order - lossless round-trip of the three tag axes, whether its
graph caches GITHUB's edges or only its own, whether its injection can order by impact
with signal integrity first, and what it does that we have no answer to at all (memory
decay, where the case FOR adoption is strongest and should be recorded as carefully as
the cases against).

The parent note's two claims to superiority - conventions not in anybody's tooling,
per-gate self-test density - are marked as claims to TEST here rather than repeat.

Links both ways: the verdict returns to the parent, which stays the only place the
decision is stated, so the two cannot grow separate copies of it.

Gates run locally: check-inflight-tags, check-file-refs, check-issue-refs. The
`file-refs: N/A` marker first used for the cross-branch citation was removed after a
negative control showed the gate does not flag a bare filename in prose - it silenced
nothing and implied otherwise; the reason is now stated as prose.
…n, before any spike

The adopt-or-build decision is deferred until after v6; the measurement it will be
made on does not have to be. Instructed to answer the three questions and the storage
question from the documentation first, and not to install or run anything - which is
the right order, because the desk pass is cheap and may be decisive on its own.

THE FINDING THAT MAY SETTLE IT. Beads has had two architectures, and the third-party
writeups that dominate search results describe the older one. Generation 1 committed
.beads/issues.jsonl and travelled with branches - "git IS the database" - and Yegge
calls that design "a crummy architecture (by pre-AI standards) that requires AI in
order to work around all its edge cases where it breaks". Generation 2, the current
one, moves the store to embedded Dolt and syncs it through refs/dolt/data on the git
remote, which takes the state OUT of the working tree. docs/inflight/AGENTS.md is
built on the opposite property - a note travels with the branch that produced it and
stops being true when that branch does. Gen 1 fails loudly, as the merge conflict that
split this directory on 2026-08-04; gen 2 fails silently, which this repo's whole
doctrine says costs more.

WHAT IT REPLACES: the tracker core, and nothing else. Every hook and gate here is
about delivery rather than storage, and the GitHub surface - issue-index.sh,
inject-branch-context.sh, the upstream-mirror pairing - is not addressed at all. No
GitHub Issues import is documented in either generation, which provisionally un-gates
the link-graph work on docs/inflight-gh-link-graph.

WHAT WE HAVE BEYOND IT: a consequence vocabulary where Beads has a ranking. P1 is a
position in a queue; misdirection is a statement about what is wrong with your
instruments, and the ordering that puts signal integrity first has nowhere to live in
an ordinal scale. What Beads has and we do not is compaction - we have no answer to
index inflation beyond a person noticing.

Also records two hazards found in the docs: `bd init` generates or updates AGENTS.md
by default, and `bd hooks install` is a second writer to a settings file whose
silent-overwrite behaviour docs/agent-harness.md already documents.

Written as a dated investigation document rather than into the inflight note, because
docs/plans/ owns those and the session index injects every open note. The note now
points at it and keeps only what still needs `bd` to have run - seven probes, plus the
install decision (isolated `go install`, not `curl | bash`) so a later session does not
re-litigate it. No verdict is written into process-adopt-external-harness.md, which
owns the decision; a documentary pass has not earned one.

Every claim is marked documentation-derived at the top, with sources and their dates,
because docs/agent-harness.md's standing rule is the opposite and the exception has to
be visible where nobody can miss it.

Gates run locally: check-inflight-tags, check-file-refs, check-issue-refs,
check-copyright-headers.
…ping product contradicts it

The claim was written on 2026-08-19 from a survey that named
karanb192/claude-code-hooks and then did not check its test story. That project
ships tests for every hook, run in CI across three Node versions, plus a benchmark
harness. The research literature asks for more than we do as well: GENESIS makes a
negative control an admission rule with three gates - fail on the unsolved state,
pass on the reference solution, fail again on a known-bad mutation - where ours has
one.

The discipline itself is kept and restated, because it is worth having on its own
merits. What is removed is the comparative claim, which was the load-bearing half:
it was one of two reasons this note gave for building rather than adopting, and a
comfortable claim about our own superiority is exactly the kind that goes three
weeks unchecked.

Struck through rather than deleted, and dated, so a reader who remembers the old
sentence can see it was retracted rather than quietly edited away.
…rest neighbour stacks

"Is Beads the right tracker" turned out to be the wrong question. Widening the survey
to the trackers, the first-party features that have since shipped, and the harness
frameworks changes what the post-v6 decision is about.

CORRECTS HOW THE FIRST PASS WAS CONDUCTED. The 2026-08-19 survey established that the
IDEAS here are published - a named field, arXiv papers, guides from LangChain, Addy
Osmani, HumanLayer, Software Mansion. That is evidence about a literature, and it was
used to conclude something about a market: whether anything installable already does
this. Different claims, different instruments; docs/solutions/workflow-issues/
negative-results-need-an-instrument-that-could-have-said-yes.md owns why that
substitution is a reporting error. The product survey is the instrument that could
have said yes, and it did - sd0xdev/sd0x-dev-flow ships commit-msg-guard and
pre-push-gate, which contradicts a claim made an hour earlier in this same
investigation that nobody ships push-time reminders.

THE FINDING THAT OUTLIVES THE PROBES. Beads was built for the "50 First Dates" problem
across a fleet of twenty or thirty parallel agents, which is what Gas Town
orchestrates. A fleet needs one shared store that no single branch owns; we have one
repository whose state is the thing being tracked. Beads is correctly designed for its
problem and its problem is not ours - a better reason to decline than any feature
comparison, and it holds whatever §6's probes return.

THE AXIS THAT IS ACTUALLY DISTINCTIVE, once the field is surveyed: everyone else's
memory lives beside the AGENT - ~/.claude/tasks, ~/.openclaw/workspace,
refs/dolt/data - and follows it. Ours lives beside the CODE and follows the branch,
which is why a note can stop being true when its branch does. The injection itself is
commodity, with better implementations shipping today.

DOES THE NEAREST NEIGHBOUR STACK: yes at the Claude layer, no at the git layer. Hooks
merge across user, project, local, plugin and managed settings and all run in
parallel, with conflicts resolved deny-first - so stacking buys the union of everyone's
blocks, and agent-harness.md records what one misconfigured blocking hook did here.
But core.hooksPath takes exactly one path and this repo sets it to .githooks, so a
git-level guard installed the usual way either never fires or silently disables ours.
Both halves of that collision are silent, which is the failure class this harness
exists to remove.

Gates run locally: check-inflight-tags, check-file-refs, check-issue-refs. The
file-refs gate earned its keep mid-write, flagging a path-shaped string in prose about
another project's layout.
…ross branches

Answers "is there a git-backed tracker that keeps state in the working tree rather
than a detached ref". Yes, and there is exactly one: Backlog.md. Markdown task files
committed to the repo, travelling with the branch. Every other candidate stores
outside the tree - ticgit in a separate branch, git-bug in its own refs, Beads gen 2
in refs/dolt/data, Claude Code Tasks and OpenClaw outside the repo entirely, and
dspinellis/git-issue's own docs say to gitignore its .issues directory when it
coexists with another project.

AND IT ALREADY DOES THE CROSS-BRANCH PART. checkActiveBranches, remoteOperations and
activeBranchDays reconcile task state across branches and optionally check remote
branches for newer states. That is drift detection, shipping, at ~6.6k stars.

This corrects the sharpest claim this investigation has made. Section 8 concluded the
distinctive axis is state beside the CODE rather than beside the AGENT, and that still
holds against Beads, Tasks and OpenClaw - but it is not unique to us, because
Backlog.md is on the same side of that line and further along it. The survey confirmed
what the other architectures cannot do and never asked what the neighbouring one
already does; Backlog.md was named in section 8 and dismissed in half a sentence as
this directory "with a UI bolted on".

WHAT SURVIVES IS NARROWER AND MORE USEFUL: no tracker surveyed integrates with GitHub
Issues, Backlog.md included, and the two that bridge import GitHub into a parallel
tracker rather than making GitHub's own graph queryable from the repo. That is where
issue-index.sh, the upstream-mirror pairing and inject-branch-context.sh already sit,
and it is unoccupied ground.

Also files the Node-client proposal as a note, narrowed by the above: build the GitHub
tunnel, treat the cross-branch reader as adopt-or-justify. It records the decisions the
proposal still needs - what "drift" means, caching on ref SHA rather than time, the
GitHub cache inheriting issue-index.md's structure-not-status discipline, no daemon or
lockfile - and the one the wording hides: a CLI cannot "forcibly" do anything, so the
forcing function is a PreToolUse hook and the client is only the good path it redirects
to. It also names its overlap with ci-issue-index-has-no-edges.md on the other branch,
so the two cannot both be built.

Gates run locally: check-inflight-tags, check-file-refs, check-issue-refs.
…inage beside it

"Adopt-or-justify" was invented one section after process-adopt-external-harness.md's
own "adopt-or-build decision" was cited, for the same idea. Two names for one decision
is how two vocabularies start, and the second one has no owner - a reader meeting it
cannot tell whether it names something new.

Both uses now route to the note that owns the decision, and say the thing the coinage
was reaching for in plain words instead: the default is on adopt because the capability
ships, rebuilding is still open, and the argument for rebuilding belongs in that note
rather than being assumed in this one.
…y declined as out of scope

Checked their tracker before proposing a contribution, because effort spent on a patch
the project does not want is effort spent twice. GitHub Issues/Projects sync and a
comprehensive lifecycle hook system are both closed NOT_PLANNED by the maintainer -
the first as "a major integration and source-of-truth design problem, outside the
current project scope", the second as "broad extensibility surface, not work planned
for the current project scope" despite being reviewed as implementation-ready. A Beads
integration request was closed as too open-ended.

The door left open is narrow and is a design question rather than a PR: a one-way,
narrowly scoped sync, needing their owner's decision first.

THE MORE USEFUL CONSEQUENCE: Backlog.md is not going to grow the injection layer. So
adopting it would replace the note store and the cross-branch reader and nothing else -
the hooks, the gates and the GitHub surface stay ours either way. That is the same
conclusion section 3 reached about Beads, one layer down, and belongs in the
adopt-or-build decision rather than being rediscovered there.

And the ordering point that governs the question: contributing to a tool is downstream
of adopting it. Upstreaming first would mean maintaining a feature in someone else's
codebase, for a system this repo does not run, whose design the maintainer says is
unresolved on their side too.

Gate interaction worth knowing for anyone citing that project again: the issue-ref gate
demands a qualified `owner/repo#NN`, and the qualified form trips the file-ref gate,
because Backlog.md's repository name ends in `.md` and so reads as a path. Resolved by
naming the project in prose and linking the bare number, which satisfies both without
needing either gate's opt-out marker.
Every command in AGENTS.md's "Before you investigate anything" table read the
WORKING TREE - `ls docs/plans/`, `grep -rl <mechanism> docs/solutions/`,
`ls docs/inflight/` - and so did every enumeration in the session-start index
hook. In this repo that is the wrong corpus by a factor of three: measured
across 443 refs, 580 of the 901 documents under docs/ exist ONLY on branches
that have not merged. Notes, plans and write-ups are authored on the branch
that produced them and stay there; many of those branches never land.

So a session on master runs all six checks, gets hits, and is still reasoning
from a false negative - which is worse than not looking, because a completed
check carries authority a skipped one does not.

WHAT PROMPTED IT. A session investigating #44 (confluentinc#803) ran the
full table and missed the confluentinc#857 revoke-path cluster decomposition
plan, the architecture write-up on the exact seam under investigation, and a
sibling RetryQueue defect note. One `git grep` across every ref returned all
three. The failure mode was already on the record - a handoff describes a note
that "appeared in no session index for anyone not on that branch" - but it was
written as a fact about one document rather than as a property of the retrieval
layer, so no rule came of it.

- bin/prior-art.mjs searches every ref in one command, groups hits by path
  rather than by ref, and flags each path missing from origin/master. It prints
  the size of the corpus it searched, so "nothing, across 443 refs" is a result
  and not a blank line, and it exits 2 rather than 0 when it cannot search -
  the failure named in a-check-that-reports-success-without-having-run.md.
  It also warns from the main checkout, on a stale fetch, on a shallow clone,
  and when HEAD is behind the baseline: the same incident also investigated a
  151-commit-stale snapshot in the shared main checkout, which is a second way
  to get "nothing found" from the same commands.

- The session-start index now states the gap in the same breath as the list -
  the branch-only count and the command that closes it - instead of implying it
  is the whole corpus. AGENTS.md's claim that "I did not know it existed is not
  available as an excuse" is corrected accordingly. Cost: one git grep, ~0.5s.

- Node rather than shell, per bin/AGENTS.md's 2026-09-01 operator ruling; the
  first draft was shell and check-source-patterns.mjs caught it once staged.

The write-up's own citations are branch-only, so check-file-refs.sh failed the
first commit attempt on all four - the defect reproducing one layer up, since a
citation gate is a working-tree search too. They carry the paragraph markers it
prescribes, and the document says so rather than hiding it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PVL2FEJ645T76PbybEBUZ6
Brings bin/prior-art.mjs - the cross-ref prior-art search - alongside the survey
that decides whether to build or adopt this class of tooling. The branch had no
PR and was never pushed, so the script existed on exactly one machine and was
invisible to every gh-based check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
Brings the three harness-gap notes - the issue index carrying nodes but no edges,
nothing asking a PR what it closes, and the subagent model defaulting to inherit.
The first two are the GitHub-tunnel half that the Backlog.md survey concluded
nobody ships, so they belong beside the survey that reached that conclusion.

ci-issue-index-has-no-edges.md and ci-node-query-client.md must not both be
built; landing them on one branch is what makes that choice visible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
… only, no content

Records that this branch's work has already landed on master by other routes, so
that it can be deleted and so a later merge attempt is a no-op rather than the 74
conflicts a content merge raises. `-s ours` deliberately: every one of its twelve
commits was audited against origin/master and nothing survives the audit.

WHY IT LOOKED LIKE STRANDED WORK. It was branched 2026-08-19 from a46b7df, a
mid-stack commit on fix/909-load-reproduction - which became #322 and merged
2026-08-26 as cf2741c. A child cut from a PR that merged a week later and was
never rebased, so it carries its parent's confluentinc#909 ancestry as a fossil while
the real version went in through #322. That is the whole explanation for both
the 114-commit lag and the conflict count.

WHERE EACH COMMIT ACTUALLY LIVES NOW:

  2e22e83 ShardManager through the module   identical patch-id on master
  30c09a3 broker-level reproduction         via #322 - RegistrationRaceStaleResidentIT
                                              and PausableInsertShardManager are on master,
                                              which is why they conflicted add/add
  6982b9f the third precondition            relocated to docs/solutions/logic-errors/
                                              909-needs-a-saturated-pipeline-...-2026-08-19.md
  92c9a73 refuse a merge with work in flight .claude/hooks/check-merge-outstanding-work.sh
  b1b7a47 hooks simplify + self-test fix    both halves on master: the `*merge*` pre-filter,
                                              and the `fails`->`failures` fold whose absence
                                              made the suite unable to fail
  e8db3c5 ShardKey javadoc contradiction    master's KeyOrderedKey carries the correction
  9013c47 drop the ShardKey refactoring row  followed it
  a46b7df agent self-review as PR comments  renamed next- -> ci-agent-self-review-...
  f7f558a index open work by cost           superseded - inject-recorded-knowledge.sh does
                                              this from bin/lib/inflight-tags.sh
  2db9a51 give every note a priority        superseded by the impact axis
  ddf6465 classify by consequence           superseded - see below

THE ONE GENUINELY UNIQUE THING, AND THE DECISION IT SETTLES. The branch proposed a
single consequence axis, `<!-- inflight-class: X -->`. Master went the other way and
shipped three - inflight-type, inflight-impact, inflight-labels, plus inflight-state
for disposition - with bin/lib/inflight-tags.sh as their single source, a gate in
bin/check-inflight-tags.sh, and the session index consuming the same lib. The
successor even carries the branch's own sentence forward verbatim, "Classify by
CONSEQUENCE, not by what kind of file it is", and then splits it. Three axes win: a
single axis cannot say that a feature addressing a crash belongs beside the crashes,
which is the case bin/lib/inflight-tags.sh's header calls out by name.

Sixty-five of the seventy-four conflicts were nothing but those two schemas meeting.
Resolving them would have meant re-deciding the taxonomy sixty-five times.

NOTHING IS LOST BY TAKING NO CONTENT. Thirty-six files exist on the branch and not on
master; all are stale names from master's later prefix rework (next- and parked- into
core-, ci-, release-, upstream-), two are the sigpipe scripts #403 deliberately
folded into a rule row, one is a workflow 025d0b7 deleted on purpose, and three are
Java classes present on master at other paths. The single note that is real open work
and genuinely absent from master -
bug-retry-queue-orphaned-by-inline-stale-removal.md, a distinct defect from master's
bug-stale-sweep-iterator-evicts-fresh-replacement.md - is carried on fifty-six other
refs, so it lands with whichever of those merges and cannot be stranded by this.

Merged here rather than to master because #400 is where the harness-survey
context lives, and the taxonomy question is the same question that PR is answering.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
The three-axis vocabulary - inflight-type, inflight-impact, inflight-labels, plus
inflight-state - has a predecessor that no reader of this file could see: a single
`<!-- inflight-class: X -->` field on ci/inflight-index-by-type-and-priority, which
carried the same "classify by consequence" argument and stopped one axis short.

Without the record, the branch reads as unmerged work rather than as a settled
question, and the next session proposing a session index by consequence proposes it
again. That is not hypothetical: the branch's sentence survives verbatim in this file,
so someone re-deriving the idea arrives at exactly the wording already here.

Why three beat one, stated where the decision is looked up rather than in a commit
nobody greps: a single field cannot express that a feature addressing a crash belongs
beside the crashes. bin/lib/inflight-tags.sh's header already owns that reasoning -
it is why INFLIGHT_BUG_IMPACTS and INFLIGHT_TASK_IMPACTS are separate sets - so this
cites it rather than restating it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
…ndings rather than a page

bin/inflight.mjs is the single command an agent has to know. It hosts prior-art
search today and is the table any later tool joins by adding a row.

WHY A FRONT DOOR. The failure is not that the tools are missing - it is that an
agent that cannot name one reaches for `git grep` on the working tree and gets a
false negative carrying the authority of a completed check. Measured 2026-09-01,
580 of the 901 documents under docs/ exist ONLY on unmerged branches, so the
habitual search sees a third of the corpus. A tool nobody reaches for is
indistinguishable from a tool that does not exist, and bin/prior-art.mjs had been
sitting on an unpushed local branch, invisible to every gh-based check, since the
day it was written.

WHY BUILD RATHER THAN ADOPT, for this surface. Backlog.md is the one surveyed
tracker that keeps state in the tree and reconciles it across branches, so the
cross-branch reader genuinely ships and #400's survey says do not rebuild
it. What ships nowhere is the rest: GitHub Issues sync and a lifecycle hook
system were both proposed to Backlog.md and both closed NOT_PLANNED by its
maintainer as out of scope. This is not a tracker. It is the layer above one.

IT RETURNS FINDINGS, NOT AN EXIT CODE, AND NOT TEXT. The first cut of the split
returned 0 or 2 and printed as it went - a shell script in Node's clothing, and
Antony called it. priorArt() now returns a PriorArtResult; format(), formatByRef()
and refClusters() are views over it; bin/inflight.mjs is the only file that may
call process.exit, once, at the bottom. Three things follow, and the third is the
reason: the process boundary exists in one place; the self-test asserts on
structure instead of scraping stdout; and every feature queued behind this one -
comparing a note across branch tips, heading-only search, ref clustering - is a
different view over the same hits, and none is reachable from a formatted page.

--by-ref IS THE FIRST THING THE RESULT OBJECT BOUGHT, and it exists because
dogfooding found the gap. Searching a term that survived only on one abandoned
branch printed sixty-five lines, one per note, each saying NOT ON origin/master;
the finding - "this is one dead branch" - had to be recovered by comparing
sixty-five ref names by eye. Identical ref-sets are one event in the repository's
history, and the per-path view cannot say so.

TWO BUGS FOUND BY USING IT, both now regression-tested:

  - Section 4, headed "Everything else under docs/", was pathspec `docs/*.md`.
    `*` crosses `/` in git's wildmatch, so it silently re-listed every plan,
    solution and note that sections 1-3 had already reported. It now excludes
    them explicitly. Present since the section was written; invisible in the
    per-path view because a duplicate under a different heading reads as a
    second finding.
  - refClusters() counted the consequence twice, reporting one note as two paths
    in a single cluster. Deduplicated, and the guard stays after the pathspec fix
    because a future section may overlap deliberately.

THE SELF-TEST'S OWN CONTROLS WERE WRONG TWICE, which is the argument for having
them. `library-never-exits-the-process` failed on the real tree by matching the
comment that states the rule - it now strips comments before asserting on code.
And two mutations silently matched nothing: one anchored on text spanning a line
break, one on a path whose first occurrence is in a header comment, so both
controls passed while proving nothing. Mutations now go through patch(), which
throws when its anchor is absent, and a mutant that cannot be built is reported
as a suite failure rather than a pass.

Citations repaired in the same commit: AGENTS.md's prior-art row and the
session-start index hook now name `bin/inflight.mjs prior-art`. The dated
solutions write-up gets an address repair only, per docs/citations.md - its
claims are untouched and it says where the code went.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
…nd stranded, over one pass

Three commands answering the questions an agent currently answers by hand, over a
single fan-out of docs/inflight/ across every ref. The working tree is not the
corpus: 570 note paths exist across the refs and 165 are on origin/master, so
`ls docs/inflight/` shows under a third of what is open.

  note find <fuzzy>   which note is this, and which branches carry it
  note drift <path>   what other branches have that master does not
  stranded            notes that have never reached master at all

DELEGATED TO GIT THROUGHOUT. bin/lib/git.mjs is the single home of plumbing -
for-each-ref, cat-file --batch-check, ls-tree, merge-base, diff --numstat - and the
only work done in JavaScript is grouping output into a Map. Where git offers an exact
answer and a heuristic one it takes the exact one: blob identity over --find-renames,
`merge-base --is-ancestor` over `branch --merged`. Extracted rather than copied
because bin/lib/inflight-tags.sh's header already records what private copies of a
shared primitive cost here.

BLOB SHA IS THE IDENTITY, and it is what makes this cheap. `cat-file --batch-check`
fed `<ref>:<path>` resolves one path across 436 refs in ONE process - measured 27ms,
against 436 forks for the obvious loop. Two refs sharing a blob are byte-identical, so
the diff runs once per distinct VERSION: 37 diffs rather than 274 for the fork's
most-edited note. Each query carries its own ref as a `%(rest)` token so every output
line says what it answers for, rather than being paired to input by position.

ONLY DIVERGENCE IS REPORTED, and this was the correction that mattered. Diffing branch
versions against master's CURRENT version answers the wrong question twice over. A
branch that has not merged recently is "different" and gets more different every time
anyone edits the note, without anyone doing anything - 198 of 274 carrying refs, pure
noise. And the size is wrong in the same direction: one cluster read `+22 -1150`,
where 22 is the finding and 1150 is what master added afterwards.

  - PURELY BEHIND is detected exactly, from master's history of the path: a blob master
    itself once held. Two batched calls, 17ms - no per-ref merge-base.
  - SIZES ARE AGAINST THE MERGE-BASE, so a number says what the branch added since the
    common ancestor. Affordable only because the filter already cut the set to 23.

A WRONG PREDICTION, STATED RATHER THAN QUIETLY DELETED. Rename subtraction was expected
to explain most notes absent from master and explained ONE of 405: blob equality proves
a rename only when the content was byte-identical at both paths, and a rename almost
always carries an edit. What works is master's HISTORY - this repo `git rm`s a note when
its work lands, so a path master once had and no longer has is CLOSED, not stranded.
That removed 40 more. The blob check stays because when it does fire it is exact.

364 survive, so they are CLUSTERED BY REF-SET: 141 clusters, the largest 42 notes from
one language-proxy workstream on 50 refs. An identical ref-set is one event in the
repository's history, and listing its members separately buries the finding under its
own volume - the lesson prior-art --by-ref already paid for.

FACTS, NOT THEMES. Each branch is named by its PR title, else the title of a note it
carries that master does not, else its own name. A summarised theme was the first design
and was dropped: it is the one field that cannot be cached, cannot be reproduced, and has
to be verified by the reader - which defeats a command whose purpose is to stop an agent
guessing.

CACHED ON REF TIPS, NEVER ON TIME. A ref that has not moved cannot have changed its
notes, so the set of tip SHAs is an exact key. docs/inflight/ci-node-query-client.md
records this as a decision to be made; this is it made. The GitHub half is the one
exception - PR state moves without any ref moving - and it is bounded at 30 minutes and
says so.

THE SUITE CAUGHT ITSELF THREE MORE TIMES, which is the case for negative controls. Two
mutation anchors went stale when this change moved the code and failed LOUDLY, because
patch() throws when its anchor is absent. The third was the last raw `.replace` left in
the file: its regex matched nothing, so the mutant was identical to the original and the
control reported a pass having tested nothing at all. There are now no raw replaces.

FOUND BY RUNNING IT: docs/inflight/bug-857-family.md is 1816 lines and 128KB on master.
That is the index inflation the harness survey said we had no detector for beyond a
person noticing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
…ding it settled

Two notes, because the survey and the proposal it answers are different items and one
of them is nearly done.

ci-inflight-absorbs-the-query-half.md is new: the dividing line is that a hook or gate
decides POLICY and must not know how to READ THE CORPUS, and seven scripts currently do
both in bash, each with its own copy of the reading. It names each one, the query inside
it, and what does NOT migrate - the gates over the working tree that read no corpus,
because a survey that only lists candidates reads as a plan to rewrite everything.

The session index is named as the first migration, and for a reason other than its line
count: it is branch-scoped and says so apologetically, so moving it onto the corpus index
gains a capability rather than only shedding bash. `stranded` at session start would put
a 42-note workstream in front of an agent that currently cannot learn it exists.

ci-node-query-client.md is updated rather than deleted. Its cross-branch reader landed,
so the note would otherwise read as unstarted work - the stale-note failure its own
directory's rules exist to prevent - but its GitHub tunnel is still unbuilt, which is
what it stays open for. Two of its decisions were settled by building it and are recorded
where they were settled: drift means DIVERGENCE rather than difference, and the ref-SHA
cache is now the whole corpus index rather than one query.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
… the coverage gap that hid itself

Review pass over the tooling this PR adds - ce-simplify with three dispatched
reviewers, then Antony on the cache. Six findings applied; two of them were the
kind that a green run cannot show you.

THE FIVE SELF-TEST CHECKS COVERING git.mjs AND notes.mjs WERE NEVER IN THE FILE.
They were written, the edit reported success, and its anchor had not matched - so
the suite ran eleven checks, printed "All 22 self-test(s) passed", and three
commands shipped with ZERO coverage while their author believed otherwise. That is
the precise failure this repository organises itself against, committed inside the
file whose purpose is preventing it, and nothing went red. It surfaced only because
a reviewer noticed two module loaders that nothing called.

Now seventeen checks, thirty-four assertions, each with a negative control that must
go red - including six new ones over the corpus queries. The insertion is verified
after writing rather than trusted, and the file says why.

THERE IS NO LONGER A DISK CACHE FOR GIT DATA. Antony's question was whether it
earned its place, and it did not: git is already the cache. It cost a read/write
layer, a 2.5MB file, a staleness class, and it had already shipped one real bug -
orphaned snapshots, 7.4MB in a single session.

It was also concealing a design mistake, which is the more useful half. `note drift`
asks about ONE path and was building the WHOLE corpus to answer it - 436 `ls-tree`
forks - then caching that to make it affordable. Asking git the narrow question
directly is 60ms cold, which is the CACHED path's speed with none of the machinery.
`drift` now takes a path rather than an index, and `branchFacts` reads a ref's notes
lazily, only for branches actually displayed. 0.7s end to end.

`corpusIndex` survives for the two questions that genuinely span every note - `find`
and `stranded` - and simply pays its 1.3s. The one cache left is the PR list, which
crosses the network and shares a rate limit with every parallel session here.

prior-art.mjs WAS THE SECOND COPY git.mjs's OWN HEADER FORBIDS, on the branch that
wrote the rule. Two reviewers found it independently. It carried private `exec` and
`lines`, its own ref listing, its own baseline resolution, and a near-verbatim
freshness-warning block - and had ALREADY drifted: `maxBuffer` 64MiB against
256MiB, plus reworded warnings. It now sources all five from git.mjs.
notes-views.mjs becomes views.mjs and owns the one warning renderer both callers
had derived separately, down to the same eleven-space indent.

ALSO APPLIED, from the efficiency pass, each measured rather than assumed:
`branchFacts` rescanned the whole corpus per branch (~3ms over 26,539 rows, up to a
hundred times per drift); `corpusIndex` re-forked `ls-tree` for a baseline already
present in its own results; `blobTitle` re-forked per blob and is now memoised on a
key that names its own content.

`isContainedIn` and `renames` are removed unused. What `isContainedIn` knew is real
and is now in docs/inflight/ci-inflight-absorbs-the-query-half.md, where the next
parcel of work is described: `git branch -d` measures containment against the
CURRENT HEAD, and `git cherry` compares patch-ids that a squash-merge invalidates.
Both cost time on this PR. A function nobody calls is documentation with a
maintenance cost.

Six self-references are now confirmed post-merge-correct, including a note asserting
this branch is "docs-only - four markdown files", which the tooling made false. The
measurement it supports still stands and says so; only the address was repaired.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
…arch that never ran

ce-code-review with four dispatched reviewers - correctness, testing, adversarial,
project-standards. Standards came back clean. The other three found, between them,
the exact failure this tool was built to remove, twice, inside the tool.

P0, REPRODUCED OUTSIDE A GIT REPOSITORY. `stranded`, `note find` and `note drift`
printed a clean empty result and exited 0. `note find` said "no note matching /q/ on
any of 0 refs. Nothing is a result here: this searched every branch tip" - a false
claim about work never done, in the sentence written to reassure the reader. The
cause: `refTips()` returned `[]` for both "no refs" and "git failed", and only
prior-art guarded it. It now returns `{ok, tips}`, every command distinguishes the
two, and all four exit 2. Regression-tested by running each command in a directory
that is not a repository.

P0, SECOND SHAPE. Section 5 of prior-art searched commit history with `git log -S`
and never checked `res.ok`, so a git that died - a pathological repack, an OOM kill -
produced no entries and rendered as "nothing", byte-identical to a real zero-hit
search. That is the contract the same file's header states one section above. A
failed term is now carried and rendered as UNKNOWN, the way the GitHub section
already did.

P1, REPRODUCED WITH A PURPOSE-BUILT REPOSITORY. `stranded` dropped a genuinely
stranded note whose filename had been used before. The path filter asked only whether
the baseline's history ever held the PATH, so once master had closed and `git rm`d
`ci-flaky.md`, an unrelated `ci-flaky.md` created later on a branch was silently
excluded - the tool that exists to surface work that will be lost, losing it. Short
`<category>-<slug>` names make that ordinary rather than exotic. The filter is now
blob-aware: a recycled name with content the baseline never held is still stranded.

P1, THE PROMISE A SHAPE COULD NOT KEEP. `prsByBranch` returned a bare Map with the
comment "unavailable is not 'no PR'; callers show it as unknown" - which no caller
could do, because the shape carried no difference. An unauthenticated `gh` rendered
as "this branch has no PR". It returns `{ok, reason, map}` and `note drift` says so.

ALSO: `baseline()` fell back to the literal string 'master' unverified, and in a
shallow or single-ref clone every downstream answer became confidently wrong with no
warning anywhere - including the one warning that could have caught it. A path
containing a space truncated the `cat-file` object spec at that space and reported the
note missing on every ref. A failed `git diff` rendered as `+null -null`. Two
freshness probes read a failed git as a negative answer, one of them printing a
confident "this is the MAIN CHECKOUT" when git could not answer at all.
`baselineHistoryBlobs` used plain `rev-list`, which prunes a merged-away side, while
its doc claimed "EVER held".

THE SUITE GREW BY THE GAP THAT LET BOTH P0s THROUGH: no check ever invoked the CLI
with a working command, so argv parsing, `--all`, `emit`, and the entire views layer
were unexercised - every command was tested only through its library. Now 19 checks
and 38 assertions, including one that runs every command end to end and one that runs
each of them outside a repository.

Two checks were pinned to `docs/inflight/bug-857-family.md`, a note this repo's own
contract guarantees will be deleted; on that day they would have gone red for a reason
unrelated to any code change. They now choose a qualifying note from the corpus.
`registeredNames` scraped the source with a regex anchored to one indent level, so two
checks named "every registered command" covered three of five - `bin/inflight.mjs` now
exports its command paths and the suite walks the registry.

Counts that describe a list rot when the list moves: AGENTS.md said "the six commands
above" against a table this branch reduced to five, and it was wrong before that too.
Those are deleted rather than corrected, here and in two places one layer in.

docs/inflight/ci-inflight-absorbs-the-query-half.md gains the register family - four
generated registers holding edges in four formats, none queryable together - and the
finding that `.github/scripts/file-ref-gate.js` already computes the whole doc-to-file
citation graph on every PR and discards everything except the dangling entries.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTX8obQMsjs9kq2rkpU5cZ
…cov cache was an orphan

Three findings from a simplify pass, two of them silent-wrong-answer defects in
code this branch added.

THE BISECT COMMAND ANSWERED ABOUT THE BRANCH REF. `codecov test --branch <ref>
<name>` searched for <ref>, not <name>:

    $ inflight codecov test --branch ci/quarantine-report-status-changes MultiInstanceHighVolume
    no test matching /ci/quarantine-report-status-changes/ in 834 recorded tests.
    EXIT=0

`args.find(a => !a.startsWith('--'))` looks right and is wrong: `--branch` takes
a VALUE, and a branch ref does not start with `--` either, so the value was the
first thing that matched. Exit 0, plausible wording, wrong question answered -
in the one command whose entire value is being trusted mid-bisect. With the flag
written LAST it always worked, which is why it survived being used.

cvOpts now returns the positionals with flag values removed, and both `test` and
`slow` take their argument from there. The regression check asserts BOTH orders,
because a test that only tried the working order would have passed against the
bug; its mutant goes red.

THE CODECOV CACHE WAS REGISTERED NOWHERE, so three things were wrong at once and
none of them reported it:

  - `cacheRead(name, {key, maxAgeMs})` no longer honours a caller's TTL -
    #400 moved that to a POLICY table, and cache.mjs says
    why in as many words: "two read sites disagreeing about how old is too old is
    a cache with two answers". This call site kept passing one. It was silently
    dropped, and the TTL actually applied was the one-hour default rather than the
    ten minutes the module's header and the CLI help both promise.
  - Absent from POLICY means absent from knownCaches(), so `inflight cache`
    reported a 2.8M file written SECONDS earlier as "ORPHAN - nothing reads this
    any more", and recommended the command that deletes it.
  - `cache clear` with no --all deletes orphans, so routine cleanup destroyed the
    live cache and forced the next query to refetch the whole corpus.

Registering it in POLICY fixes all three, because that table is the one place
TTL and known-ness are derived from. Verified: the file now lists as live.

An empty corpus is also no longer cached. cache.mjs's `cacheEmpty: false` guard
tests `Array.isArray(value)`, and this value is an object wrapping the array, so
the guard could not see inside it - caching an empty fetch would have served "no
test history" for the whole TTL, which reads as "no flakes recorded".

Both defects arrived the same way: this branch merged a squashed #400 that had
changed the cache API underneath code written against the older one, and a clean
merge reports nothing about a call site whose argument has quietly become inert.

ONE SHARED TEST HARNESS instead of three copies. The three report-comment suites
landed together, each with its own runner and fake GitHub client; two of the
copies were byte-identical. The shared version is deliberately the RICHEST of the
three rather than the smallest common one - sticky's fake recorded call ORDER
(retire-before-create is invisible to any assertion about final state) and its
test() rejected an async body handed to the sync runner, which can never fail.
Sharing down to the simplest would have deleted both. Instead the other two
suites gained them, and all three still pass.

Reviewed by three independent passes; the reuse and efficiency reviewers found
the cache defect separately, which is why it is stated as corroborated rather
than as one reviewer's opinion.
…nd eight more from review

An eight-lens review, one of them an independent cross-model pass. The P1 is
mine, from the previous commit.

THE SENTINEL. `cvOpts` filtered positionals with `i !== branchAt + 1` to stop a
flag's VALUE being read as the query. `indexOf` returns -1 when the flag is
absent, so `branchAt + 1` is 0 and the filter dropped the FIRST POSITIONAL -
the query itself:

    $ inflight codecov test MultiInstanceHighVolume
    codecov test: give part of a test name
    $ inflight codecov slow 3        # prints 20

Every invocation this branch's own docs advertise was broken - the inflight-tool
worked examples, the AGENTS.md table row, the post-push hook. Fixing one
silent-wrong-answer bug produced a louder one.

The regression test did not catch it because it only ever exercised the
`--branch` form. It is now PURE - `cvOpts` is exported and asserted directly -
which also removes a live api.codecov.io call from a file whose header says
"Network: none" precisely so a rate limit cannot flake CI. It asserts the bare
form, the flag-first form and the flag-last form, and its mutant reverts exactly
to the sentinel.

THE REST, by lens:

reliability
  - The PR-files lookup in bin/quarantine-lane-report.sh had no `|| true`, unlike
    its sibling two lines above. Under `set -euo pipefail` a transient gh failure
    killed the run, and the step that calls it carries no continue-on-error - so
    a rate limit reddened a job whose lane had run fine. That is the defect this
    file's own header says was fixed for the path that moved out to JS; the
    neighbour still had it. The defect-class sweep should have found this when
    the first instance was fixed.
  - quarantine-lane.yml had no `concurrency` group. Two close pushes could both
    retire and both create, leaving two live markers - and the lookup takes the
    oldest, so the newer is orphaned and every later update writes where nobody
    reads. This branch changed the consequence: the race used to lose an edit.

security (all P3, none reachable by an outside contributor)
  - `prev.status` was rendered unsanitised while `cur.status` went through the
    shared helper - and prev is the half that comes from a comment body.
  - The Codecov collector wrote GITHUB_OUTPUT with `echo`; a filename may
    contain a newline, which injects a step-output line. Heredoc delimiter now.
  - The lane script spliced the marker into a jq filter while json_key()
    sanitised the same class of value for the JSON sink a few lines below.
    Passed via ENV now - sanitised on one path and raw on the other was the
    asymmetry.

correctness
  - `codecov slow` was the only analysis that dropped `truncated`, so past the
    page bound a partial list printed as complete.
  - A SKIP was counted as a failure, admitting assumption-skipped tests to the
    flake list and skewing its sort.
  - Both sides of the outcome contract said "five outcomes"; there are four.
    Nothing gates the number, which is why it drifted on both sides at once.

testing
  - The shared harness's own can-never-fail guard had no test. It has one now,
    driven in a subprocess against the real harness and red-proofed by deleting
    the guard. The first attempt re-implemented the guard inline and asserted on
    the copy, which would have passed with the real one removed - the exact shape
    the guard exists to catch.
  - slowestFrom's "latest observation carrying a duration" branch was untested
    while its three siblings each had a mutation-tested check.

agent-native
  - The `codecov` help summary described the subcommands while the bare command
    prints coverage totals - and the flattened help list is the discovery path
    this front door exists for.

Also removed a mechanical count from docs/inflight-tool.md; it is now the
command that yields the number.

FILED, NOT FIXED: docs/inflight/ci-emptying-the-quarantine-lane-leaves-its-last-
report-live.md. A PR that deletes the LAST quarantine entry leaves the previous
report live saying ACTION REQUIRED, because every step after the empty check is
gated on the lane being non-empty - so the run that should retract the report is
the one guaranteed not to speak. `renderDelta` already has a `left the lane`
branch that is unreachable for the last entry, which is how it was found. Fixing
it needs a distinct empty-lane report path and fixtures, not a line at the end of
a review.
@astubbs

astubbs commented Sep 2, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Context, so this pass spends its effort where the local one could not: an independent Codex review was
run against this branch locally and timed out before emitting findings, so the adversarial lens is the
one lens on this PR that produced nothing. Seven other lenses ran and their fixes are already pushed —
please review the current head rather than assuming it is unreviewed.

Where the risk actually is:

  1. The sticky-comment engine is a silent-pass verification surface.
    .github/scripts/sticky-report-comment.js decides whether a report updates in place or posts a
    fresh comment, retiring the old one first. The consequential direction is under-announcing: a
    deterministic quarantined test that starts passing means its fix landed and its @Quarantined
    annotation plus registry entry must be deleted, so a missed announcement leaves dead quarantine in
    the tree. Attack the status-change detection, the retire-before-create ordering, and the behaviour
    when the previous comment's embedded payload is absent, truncated, or hostile.

  2. The payload round-trip is the only state store. Each report embeds one machine-readable line
    and reads its own previous comment back to render a delta — no series is kept anywhere else.
    Consider a human-edited comment, two workflow runs racing, and a payload that parses but carries
    the wrong shape.

  3. The shell/JS seam. bin/quarantine-lane-report.sh writes the payload that
    .github/scripts/quarantine-report-comment.js parses. A classification the payload cannot express
    is a silent downgrade that neither side's own tests can see. Cross-checking the two sides is what
    found the last defect here (both claimed "five outcomes"; there are four).

  4. bin/lib/codecov.mjs reads an unauthenticated third-party API and answers flake questions. It
    is built on "found nothing" and "could not look" being different answers — a network error or an
    empty page must never surface as "no flakes recorded".

Two known items, so they are not re-reported: bin/test-inflight.mjs fails two mutation checks
(explicit-help-succeeds, freshness-reads-this-worktree-not-the-main-checkout) from a realpath /
symlink guard inherited from #400, not introduced here; and emptying the
lane leaving its last report live is filed at
docs/inflight/ci-emptying-the-quarantine-lane-leaves-its-last-report-live.md rather than fixed.

…d -A` committed

`.github/scripts/sticky-report-comment.js.bak` was byte-identical to the file it
shadows - scratch left behind when a reviewer red-proofed a test by temporarily
mutating the real file. `git add -A` swept it into the previous commit, and the
copyright gate rejected it as an unclassified file type. `repo: hygiene` failed
for the same reason: it runs bin/check-all.sh, which includes that gate.

WHY THE LOCAL RUN SAID IT WAS FINE. This worktree is a shallow clone, and
bin/check-copyright-headers.sh exits 0 without checking anything when it cannot
resolve a fork point - so `bin/check-all.sh` printed "no gate failed" while
having skipped the only gate that would have caught this. CI is not shallow, so
it did catch it. The honest local invocation is:

    COPYRIGHT_CHECK_REQUIRE_FORK_POINT=1 bin/check-copyright-headers.sh

which reproduces the failure exactly, and is what confirmed the fix.

That is the same shape as the defects this branch has been fixing all along - a
check that reports success without having run - and it was in the verification
of the fixes rather than in the fixes themselves.
@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report

Quarantined test Outcome Owner Meaning
MultiInstanceRebalanceTest.largeNumberOfInstances 🔴 failing (expected) ⚠️ unowned quarantine holding
ProducerManagerTest.producedRecordsCantBeInTransactionWithoutItsOffsetDirect 🔴 failing (expected) #262 quarantine holding

🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its @Quarantined annotation + docs/quarantined-tests.md entry (a merge-blocking review thread has been opened). Lane: non-gating; rules: see the Quarantine Audit check.

Since the previous push: MultiInstanceRebalanceTest.largeNumberOfInstances: 🟡🎲 passed (flapper) → 🔴 failing.

Updated for 770d6f4 · run 33607277026 · 2026-09-02 08:16 UTC

Superseded by a newer quarantine lane report.

…e first one carrying a rate

Integration Tests went red on this branch:
PartitionStateCommittedOffsetIT.committedOffsetRemoved(OffsetResetStrategy)[1],
`checkHowManyRecordsWithKeyPresent` expected 2 got 1 - the same assertion the
2026-09-01 row already records for this parameter. Recorded before the branch
merges, because the job log expires and the sighting would go with it.

THE PART WORTH KEEPING IS THE RATE, NOT THE SIGHTING. Every earlier row in this
ledger cites one job, because one job log was all anyone had. This test's
per-commit outcome is now queryable:

    inflight codecov test committedOffsetRemoved

    [1]  ! failure  770d6f4     <- this sighting
         pass x6 across six earlier commits on the same branch
    [2]  pass x7

One failure in seven runs, and the commit it failed at DELETES A .bak FILE and
changes nothing else - so the evidence is the run-to-run position of the failure,
not anything about that commit. A file deletion cannot alter a Kafka integration
test.

It does not settle the mechanism, and the note says so: the 2026-09-01 sighting
recorded that the record which SURVIVED was the compactor rather than the
original, which the 2026-08-05 nudge-race mechanism cannot produce. Nothing here
confirms or contradicts that. What it settles is that [1] is not deterministic on
this branch - which no single job could ever show.

Found by the two tools this branch adds, used in anger for the first time:
`inflight prior-art --headings committedOffsetRemoved` surfaced the plan, the
solved-problem write-up and this ledger across every ref; `inflight codecov test`
supplied the rate. Neither answer needed a build re-run.

No retry and no assertion touched - AGENTS.md's lever for a flake is quarantine
with evidence, and evidence is what this commit adds.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fda945efb8

ℹ️ 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".

Comment thread bin/lib/views.mjs
Comment thread bin/lib/codecov.mjs
Comment thread bin/lib/codecov.mjs
Comment thread bin/inflight.mjs
Comment thread bin/lib/views.mjs Outdated
Comment thread .github/scripts/sticky-report-comment.js.bak Outdated
Comment thread .github/scripts/sticky-report-comment.js Outdated
Comment thread bin/lib/views.mjs Outdated
@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report

Quarantined test Outcome Owner Meaning
MultiInstanceRebalanceTest.largeNumberOfInstances 🟡🎲 passed (flapper) ⚠️ unowned proves nothing - passes most runs by nature
ProducerManagerTest.producedRecordsCantBeInTransactionWithoutItsOffsetDirect 🔴 failing (expected) #262 quarantine holding

🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its @Quarantined annotation + docs/quarantined-tests.md entry (a merge-blocking review thread has been opened). Lane: non-gating; rules: see the Quarantine Audit check.

Since the previous push: MultiInstanceRebalanceTest.largeNumberOfInstances: 🔴 failing → 🟡🎲 passed (flapper).

Updated for c752f54 · run 33608579856 · 2026-09-02 08:30 UTC

Superseded by a newer quarantine lane report.

…seven ways

All seven from the independent cross-model review. Every one is a variant of the
same failure, and it is the worst one this tool could have: `codecov flaky`
exiting 0 with an apparently complete negative, in the exact situation a reader
uses it to justify REMOVING a quarantine.

Seven local reviewer lenses missed all of these. They share this model; the
cross-model pass does not, and that is the whole argument for running it.

THE FALSE-NEGATIVE FAMILY

  - An empty UNFILTERED corpus returned ok:true. A transient 200 with no rows is
    an inability to read the history, not an absence of flakes - this repo
    demonstrably has uploaded history. Now ok:false, scoped to the unfiltered
    query so a branch-scoped miss (that branch never uploaded) still answers
    honestly.
  - formatFlakes dropped the truncation warning on the NEGATIVE path. A walk that
    hit the page bound and happened to find nothing varying printed a clean
    negative with no hint that older history was never read.
  - `coverage()` turned a failed /flags/ request into an empty list and an
    overall ok, so "no flags" and "the request failed" printed identically.

THE EVIDENCE-QUALITY FAMILY

  - The sighting line showed outcome and sha but never BRANCH, while the default
    query spans every branch. One pass on master plus one failure on a PR branch
    rendered exactly like a master-state flake - defeating the rule in
    docs/quarantined-tests.md that a PR-only failure is not quarantine evidence.
  - A SKIP rendered as `X`, indistinguishable from a failure, so the line carried
    more markers than the run count above it and overstated the evidence. Skips
    now render `s`, with a legend.

THE ARGUMENT-PARSING FAMILY

  - `--branch` with no value left branch undefined and silently queried EVERY
    branch. `--branch --fresh` took the next flag as a branch name and returned a
    convincing empty result for a branch that cannot exist. An unknown flag was
    dropped by the startsWith('--') filter, so a typo ran a different query than
    the one typed. None errored - all three answered. Now refused.

AND ONE IN THE STICKY ENGINE

  - `statusChanged` compared two `undefined`s as EQUAL, so a payload that parses
    but carries no status - `{}`, or a truncation that stays valid JSON - read as
    "unchanged" and suppressed the fresh announcement the mechanism exists to
    make. It now treats an unusable payload as unknown and announces rather than
    hides.

Three new checks, each red-proofed by reverting its guard: the empty-corpus
rejection, the option-set validation (asserting the VALID forms still parse, so
the guard cannot eat the feature), and statusChanged's payload validation.

The reviewer also independently flagged the stray .bak already removed in
770d6f4 - corroboration from a model that could not see that fix.

One note on its citations: it referenced AGENTS.md by line range, which this
repo's own convention forbids (cite by anchor, never by line). The findings are
sound; the pointers into AGENTS.md will rot.
@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report

Quarantined test Outcome Owner Meaning
MultiInstanceRebalanceTest.largeNumberOfInstances 🔴 failing (expected) ⚠️ unowned quarantine holding
ProducerManagerTest.producedRecordsCantBeInTransactionWithoutItsOffsetDirect 🔴 failing (expected) #262 quarantine holding

🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its @Quarantined annotation + docs/quarantined-tests.md entry (a merge-blocking review thread has been opened). Lane: non-gating; rules: see the Quarantine Audit check.

No quarantined test changed outcome since the previous push.

Updated for f0050ab · run 33617882952 · 2026-09-02 10:12 UTC

Superseded by a newer quarantine lane report.

@astubbs astubbs left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

lgtm

…e failing on

`grep -ci codecov docs/ci.md` returned zero. 7894373 added CODECOV_TOKEN and
documented it in AGENTS.md; the restructuring lost that and never relocated it.
Meanwhile coverage uploads, a separate test-results upload, four codecov/*
statuses, codecov.yml and an unauthenticated API client were all built on a
service the CI doc had never mentioned.

WHAT THE SECTION HAS TO GET ACROSS, in order of how often it misleads someone:

  1. Two DIFFERENT Codecov products run from the same workflow - coverage
     (jacoco) and Test Analytics (the JUnit XML surefire and failsafe already
     write). A failure in one says nothing about the other.
  2. A per-suite flag reading 0% on the default branch is CORRECT. The flags
     endpoint reports default-branch coverage and only the push-only `build` job
     runs there. That has nearly been filed as an uploader bug three times.
  3. The two lanes are NOT symmetric, and this is what the gates are failing on:
     `build` uploads one jacoco file per flag; the PR-only `test` matrix uploads
     BOTH files under one flag per suite.

THE GATES, STATED AS UNSETTLED RATHER THAN EXPLAINED AWAY. codecov/project/unit
and codecov/project/integration report large negative deltas on branches that
change no Java, while the OVERALL number rises and patch coverage is unaffected -
which is not the shape of a real regression confined to two flags. They passed
before only because there was no base to compare against: they said "No coverage
information found on base report" and went green. Their first real comparison is
the one that failed.

The asymmetry above is a hypothesis with a mechanism, not a demonstrated cause,
and the doc says so. The obvious way to check it does not work: Codecov's
`report/?flag=` parameter is IGNORED - querying unit and integration on one
commit returns identical numbers, the overall total - so per-flag line counts
cannot be compared. Recorded so nobody walks that dead end twice.

The experiment that would settle it is to make the PR lane upload one file per
flag and see whether the next comparison is clean. Not done here: changing what a
required merge gate measures should be the only thing in its own diff.

The inflight note is split rather than deleted, per docs/inflight/AGENTS.md - its
subject (Codecov documented nowhere) is resolved by this commit, and what remains
is a different item, so ci-codecov-is-documented-in-no-topic-doc.md gives way to
ci-the-per-flag-coverage-gates-fail-and-the-cause-is-unsettled.md.
@astubbs

astubbs commented Sep 2, 2026

Copy link
Copy Markdown
Owner Author

@claude review this

Using the mention route deliberately: three claude-code-review-dispatch runs on this PR each
finished successfully and posted nothing (33594290780, 33595472759, and 33617907182 in
flight as I write this), so claude[bot] has never commented here and claude-review has been red
throughout. Whatever you find, your last action must be exactly one comment — a review that posts
nothing is indistinguishable from no review at all, and this route is the only one that can open
inline threads.

What already happened, so you spend effort on what is still open. An independent Codex review
found seven real defects that seven local reviewer lenses had all missed. Every one was a variant of
the same failure: bin/lib/codecov.mjs and bin/lib/views.mjs could report a confident false
negative
from codecov flaky — exiting 0 with an apparently complete "no flakes" — in the exact
situation a reader uses that command to justify removing a quarantine. All seven are fixed in
1bb8f8e68.

Look hardest at whether that class is genuinely closed, and at what sits next to it:

  1. Every path in bin/lib/codecov.mjs and bin/lib/views.mjs where an absence, an error, a
    truncated pagination walk, or an empty page could still surface as a clean negative rather than as
    "could not look". That distinction is the module's whole contract.
  2. The new option validation in cvOpts (bin/inflight.mjs). It now refuses malformed flag forms —
    check it cannot refuse a valid one, which would be the opposite defect and just as silent.
  3. statusChanged in .github/scripts/sticky-report-comment.js, which now treats an unusable
    payload as unknown and announces rather than hides. Is that direction right, and can it announce
    spuriously on every run?
  4. The seam where bin/quarantine-lane-report.sh writes a payload that
    .github/scripts/quarantine-report-comment.js parses. Cross-checking those two sides has already
    found two defects neither side's own tests could see.

Known, and not yours to re-report: bin/test-inflight.mjs fails two mutation checks
(explicit-help-succeeds, freshness-reads-this-worktree-not-the-main-checkout) from a
realpath/symlink guard inherited from #400; the emptied-lane gap is filed at
docs/inflight/ci-emptying-the-quarantine-lane-leaves-its-last-report-live.md; and the two failing
codecov/project/* flag gates are diagnosed in docs/ci.md as an inherited lane asymmetry rather
than a regression on this branch.

@claude

claude Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

Claude finished @astubbs's task in 4m 50s —— View job


Review complete

Todo

  • Confirm 1bb8f8e actually resolves the 7 Codex-flagged silent-false-negative findings in bin/lib/codecov.mjs / bin/lib/views.mjs
  • Re-audit those two files for remaining "absence/error/truncation → clean negative" paths
  • Check cvOpts (bin/inflight.mjs) doesn't reject valid flag forms while fixing malformed ones
  • Check statusChanged (.github/scripts/sticky-report-comment.js) for spurious/every-run announcements
  • Cross-check bin/quarantine-lane-report.sh ↔ .github/scripts/quarantine-report-comment.js payload contract
  • Post final review

One environment note, not a PR defect. I couldn't execute node .github/scripts/*.test.js myself — claude.yml on master only grants Bash(node .github/scripts/issue-ref-gate.test.js:*) and Bash(node .github/scripts/changelog-ref-gate.test.js:*); the new gate self-tests this PR adds aren't in that allowlist yet because I'm running master's copy of the workflow, not this branch's. pr-checklist.yml's glob step (git ls-files '.github/scripts/*.test.js') does run them in CI with full permissions, so this is a gap in my local coverage, not evidence against the PR. I read the logic by hand instead.

1. The "confident false negative" class — looks genuinely closed

Walked every finding from the earlier Codex pass against current HEAD (f0050ab):

  • Empty unfiltered corpus → ok:false, bin/lib/codecov.mjs:111-119 — good, narrowed correctly to the unscoped-query case (a --branch query legitimately returns empty).
  • /flags/ failure surfaced, not silently [] — flagsFailed threaded through coverage() (codecov.mjs:70-74) and rendered in formatCoverage (views.mjs:210-213).
  • Truncation warning on the empty-flakes path — views.mjs:254-264 now appends truncNote(v) on the zero-candidate branch too, not just the non-empty one.
  • Skip vs. failure marker — flakesFrom excludes skips from runs/failures (codecov.mjs:191-206) and formatFlakes's mark() renders ./X/s distinctly (views.mjs:274), so a skip can no longer read as a failure.
  • Branch identity in flake evidence — each observation line now prints @{branch} (views.mjs:276), so a master pass + PR-branch failure can't masquerade as a master-state flake.
  • .js.bak duplicate — confirmed gone (770d6f4); not in the tree, not in changed_files.
  • CodeQL js/bad-tag-filter — the regex-derived supersededMarker default is gone; it's now a required param, and both real consumers (throughput-report-comment.js:27, quarantine-report-comment.js:30) pass an explicit literal. The SpotBugs step in maven.yml only uses findExisting/stampFor, not postStickyReport, so it never needed the removed default.

I didn't find a remaining path where absence/error/truncation collapses into a clean negative in these two files.

2. cvOpts — rejects the bad forms, doesn't reject the good ones

Traced by hand: --branch <ref> <query>, <query> --branch <ref>, --fresh --branch <ref> (with rest empty, correct for flaky), and --branch <ref> <n> for slow all resolve correctly — the branchValueAt sentinel (-1 when absent) keeps the first positional from being eaten when --branch isn't present. --branch with nothing after it, and --branch --fresh, both now hit the branch === undefined || branch.startsWith('--') guard (inflight.mjs:91-93) and return opts.error rather than answering.

One real but low-severity gap: --branch a --branch b silently uses the first occurrence and folds b into rest as a stray query term — no validation catches a repeated flag. Not something an agent is likely to type by hand, and not in scope of what was asked to be checked, but worth a one-line note since it's adjacent to the class just fixed.

3. statusChanged — direction is right, not spurious every-run

usable(cur) gates on cur even existing before anything is compared, so a run with no payload never claims a change. When cur is usable but prev isn't (first comment ever, or a payload that parses but has no scalar .status), it now returns true — announce rather than silently edit in place, which is the correct direction per the module's own history (#407's silent-edit failure).

It does not announce on every ordinary run: status_digest in quarantine-lane-report.sh (paste -sd ';' - over a sorted key=value list) is stable across runs when nothing moved, so prev.status === cur.status holds and the normal path is updated, not superseded. Confirmed this isn't just an assumption — quarantine-report-comment.test.js's "Run 2, same outcome" case asserts action === "updated".

4. The shell↔JS payload seam

bin/quarantine-lane-report.sh writes {"status":"<digest>","outcomes":{...}}; quarantine-report-comment.js's renderDelta reads .outcomes, sticky's statusChanged reads .status — both derived from the same sorted outcome_pairs, so they can't disagree with each other. json_key() strips registry-entry names to A-Za-z0-9._$- before they become JSON keys, and those names are Java Class.method identifiers (no -/>/quotes possible), so there's no realistic injection path into the embedded HTML-comment payload via a crafted quarantine entry.

More importantly, this isn't just argued from reading — quarantine-report-comment.test.js's end-to-end block (runReporter) actually shells out to the real bin/quarantine-lane-report.sh against fixture surefire/failsafe XML, twice (failing → failing → passing), and feeds the real output through post(). It asserts created/updated/superseded in sequence and that the fresh comment names the transition (`SomeQuarantinedIT.someMethod`: 🔴 failing → 🚨✅ passed - ACTION REQUIRED). That's a genuine cross-side check, not two independently-invented fixtures agreeing by construction.

Not re-reported, per your framing

bin/test-inflight.mjs's two mutation failures (realpath/symlink guard, inherited from #400), the emptied-lane gap (docs/inflight/ci-emptying-the-quarantine-lane-leaves-its-last-report-live.md — read it, it's honestly scoped and correctly explains why it's deliberately deferred rather than fixed here), and the codecov/project/* flag asymmetry (docs/ci.md).

Verdict

No blocking findings. The false-negative class is closed with real evidence (not just assertions on invented fixtures), the option-parsing fix doesn't trade one silent-wrong-answer mode for another, and the status-change/retire-before-create machinery is sound. The one thing I'd want in a follow-up rather than this PR: validate against a repeated --branch flag in cvOpts.

…used

From the Claude review, and fixed here rather than deferred because it is a hole
in validation added an hour ago, and it is the same class that validation exists
to close.

`--branch a --branch b` took the FIRST occurrence and folded `b` into the
positionals as a stray query term, so the command answered a question nobody
asked - which is exactly what `--branch` with no value and `--branch --fresh`
were already refused for. The guard covered malformed flags and unknown flags and
not repeated ones.

Both `--branch` and `--fresh` are now refused when given more than once, with an
error that says which flag and why rather than picking a winner. The existing
option-set check gained both cases, and its mutant still goes red.

The reviewer suggested this as a follow-up. Taking it now: it is two lines, it
sits inside the function the previous commit rewrote, and shipping a known gap in
freshly-added validation is worse than the small diff.
@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report

Quarantined test Outcome Owner Meaning
MultiInstanceRebalanceTest.largeNumberOfInstances 🟡🎲 passed (flapper) ⚠️ unowned proves nothing - passes most runs by nature
ProducerManagerTest.producedRecordsCantBeInTransactionWithoutItsOffsetDirect 🔴 failing (expected) #262 quarantine holding

🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its @Quarantined annotation + docs/quarantined-tests.md entry (a merge-blocking review thread has been opened). Lane: non-gating; rules: see the Quarantine Audit check.

Since the previous push: MultiInstanceRebalanceTest.largeNumberOfInstances: 🔴 failing → 🟡🎲 passed (flapper).

Updated for b3d195f · run 33619372058 · 2026-09-02 10:30 UTC

Superseded by a newer quarantine lane report.

… and I had it backwards

The earlier version of this doc and its note claimed the PR lane uploaded BOTH
jacoco halves under one flag while master uploaded one file per flag. The upload
logs say the reverse, and the real cause is a defect this branch already fixed
once somewhere else.

MEASURED, not inferred. Master's build job declares
`files: **/target/site/jacoco/jacoco.xml` for flag `unit` - one glob, one half -
and its log then reads:

    Some files were not found --- {"not_found_files": ["**/target/site/jacoco/jacoco.xml"]}
    Found 13 coverage files to report        <- both halves, every module

The CLI does not expand `**` and nothing routes `files:` through a shell, so the
pattern arrives literally, matches nothing, and the tree-wide fallback uploads
EVERYTHING. It then does the same for `integration`. A pull request's suite jobs
have only produced their own half, so the identical fallback finds only that
half - the integration job uploaded 4 reports, all jacoco-it.

So a PR's `integration` flag is compared against a master `integration` flag that
silently contains the unit half as well, and the delta measures that rather than
the branch. Both figures fall out of it: integration loses the larger half
(-15.32%), unit the smaller (-4.46%).

THE EVIDENCE THAT SETTLES IT WAS ALREADY IN HAND AND I EXPLAINED IT AWAY. Querying
Codecov for master's two flags returned an identical figure for both. I recorded
that as "the API ignores the flag parameter". It does not - both master flags
genuinely hold the same data, which is the whole defect. The note now says so,
because a wrong diagnosis that was corrected is worth more to the next reader than
a clean-looking one.

It is the same ininert-`files:` defect fixed for the test-results upload earlier
on this branch - a configuration line that reads as configuration and does
nothing, whose fallback returns a plausible answer instead of an empty one, which
is exactly what lets it survive review. Third and fourth instances of a class
already fixed here, which the defect-class sweep should have caught then.

Still not fixed in this change, and now for a better-evidenced reason: the repair
alters what a REQUIRED merge gate measures, so it wants to be the only thing in
its diff, with the first clean per-flag comparison as its proof.
@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report

Quarantined test Outcome Owner Meaning
MultiInstanceRebalanceTest.largeNumberOfInstances 🔴 failing (expected) ⚠️ unowned quarantine holding
ProducerManagerTest.producedRecordsCantBeInTransactionWithoutItsOffsetDirect 🔴 failing (expected) #262 quarantine holding

🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its @Quarantined annotation + docs/quarantined-tests.md entry (a merge-blocking review thread has been opened). Lane: non-gating; rules: see the Quarantine Audit check.

Since the previous push: MultiInstanceRebalanceTest.largeNumberOfInstances: 🟡🎲 passed (flapper) → 🔴 failing.

Updated for 81a42cf · run 33620648164 · 2026-09-02 10:46 UTC

Superseded by a newer quarantine lane report.

Two figures taken from one upload log - "13 reports", "Found 13 coverage files" -
were doing the work of a claim that is really about ALL of them. The count is
incidental to the mechanism and wrong the first time a module is added, with
nothing to report that it drifted.

Now "every jacoco report in the tree - both halves", and the note cites the shape
of the log line rather than its number. The evidence is unchanged; what is gone is
a figure a reader could act on as current state.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🧪🔒 Quarantine Lane Report

Quarantined test Outcome Owner Meaning
MultiInstanceRebalanceTest.largeNumberOfInstances 🟡🎲 passed (flapper) ⚠️ unowned proves nothing - passes most runs by nature
ProducerManagerTest.producedRecordsCantBeInTransactionWithoutItsOffsetDirect 🔴 failing (expected) #262 quarantine holding

🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its @Quarantined annotation + docs/quarantined-tests.md entry (a merge-blocking review thread has been opened). Lane: non-gating; rules: see the Quarantine Audit check.

Since the previous push: MultiInstanceRebalanceTest.largeNumberOfInstances: 🔴 failing → 🟡🎲 passed (flapper).

Updated for c04aaa2 · run 33622338223 · 2026-09-02 11:04 UTC

@astubbs
astubbs merged commit 276fdff into master Sep 2, 2026
33 of 35 checks passed
@astubbs
astubbs deleted the ci/quarantine-report-status-changes branch September 2, 2026 11:20
astubbs added a commit that referenced this pull request Sep 2, 2026
Two commits - #400's in-flight tooling front door and #409's
quarantine status-change report. One Java file between them, a script test, so
nothing here reaches this branch's code.

THE CONFLICT: THREE SIGHTINGS, NOT TWO, AND BOTH SIDES HAD IT WRONG

test-load-tightness-flakes.md again, and again because both sides appended a
committedOffsetRemoved[1] latest sighting to the same row. This branch's row
said two sightings and named #407 and #207; master's said two and
named #407 and #409. Neither is right on its own: there are three,
and dropping either side's would lose the evidence the ledger exists to keep.

Resolved to one row citing all three with what each contributed, which is the
part that matters: #407 captured WHICH record survived - the compactor,
not the original - and that rules out the 2026-08-05 nudge-race mechanism;
#409 attached a RATE; #207 captured neither. Both prose sections
are kept because they say different things - one withdraws a claim, the other
adds the rate - and neither restates the other.

A RECOVERY ATTEMPTED, AND RECORDED AS FAILED

#400 landed `node bin/inflight.mjs codecov test <name>`, which reads
per-commit outcomes from history that outlives a CI log - exactly the gap this
branch recorded when the #207 sighting's log returned zero bytes. Ran
against this test it covers only the branch #409 ran on, so that sighting
stays unclassifiable. Written into the note as an attempt that does not reach it,
so the next reader does not run the same query hoping for a different answer.

INHERITED, AND IT INDICTS HOW THIS SESSION SEARCHED

#400 adds a rule worth acting on rather than filing: grep and find read
the WORKING TREE, and roughly two thirds of this repo's docs exist only on
branches that have not merged - so a working-tree search returns a false negative
carrying the authority of a completed check, and `bin/inflight.mjs prior-art` is
what searches every ref. The prior-art checks in this session used `grep -rln`
over docs/, which is precisely the shape that rule names.

Unit lane green across all modules; bin/check-all.sh 15/15.
astubbs added a commit that referenced this pull request Sep 2, 2026
Master gained #409, which added `inflight codecov` -
a new command in the same registry this branch adds `refactor-window` to, with
its own formatters in views.mjs, its own self-tests, and its own worked example
in the tool's doc. Both sides did the same four things in the same four files.

THREE CONFLICTS, ALL ADDITIVE, BOTH SIDES KEPT. Nobody changed the same
behaviour; both changed the same neighbourhood. `--ours`/`--theirs` would have
been catastrophic here for exactly the reason AGENTS.md gives - each takes the
whole file, so either would have silently deleted an entire command and its
formatters with nothing going red. Resolved hunk by hunk:

  bin/inflight.mjs      the two import statements merged into one per module,
                        and both command rows retained
  bin/lib/views.mjs     both sets of formatters kept
  docs/inflight-tool.md both worked examples kept

bin/test-inflight.mjs merged cleanly - the two sides appended their checks in
different regions - and the suite now runs both.

Verified rather than assumed, per the rule about auditing every removal a
conflicted merge produces: `inflight help` lists `codecov`, its three
subcommands and `refactor-window`; views.mjs exports all eleven formatters; and
the only removals against the pre-merge tip are the import line that was merged
and the stale example replaced below.

THE MERGE ALSO CAUGHT A STALE DOCUMENT, which is the argument for merging now
rather than at merge time. The worked example added earlier on this branch still
quoted the output from BEFORE the measurement was fixed - +1609 against a
threshold of 240, naming a rename branch as the blocker. Those numbers were the
artefact this branch exists to have removed, sitting in the document that
teaches the command. It now shows real current output, including the ranking and
growth lines added since, and carries an `issue-refs: exempt` marker because
that output legitimately contains a bare pull-request number the tool prints
verbatim.
astubbs added a commit that referenced this pull request Sep 2, 2026
…nd got the cause wrong

`node bin/inflight.mjs prior-art codecov` returns four codecov notes and marks
exactly one as NOT ON origin/master: this one. The other three arrived with
#409 in the master merge this branch already carries, hours before the
note was written.

ci-codecov-flags-not-like-for-like.md owns the flag-set mismatch and its fix in
#400. ci-the-coverage-uploads-still-use-the-inert-glob.md owns what is
actually wrong with the per-flag gates - the uploader does not expand `**`, so
the pattern matches nothing and the CLI falls back to a tree-wide search;
master's full build therefore hands the WHOLE tree to both the unit and the
integration flag, while a PR's suite job has only its own half. That is why the
two flags report an identical number on the default branch, which both that note
and this one used as evidence for different conclusions.

Its collision check also lists #414, open since before the diagnosis
started here, making exactly that fix with `disable_search: true` and expanded
globs - and proving it from the upload logs rather than by inference.

So the note was a third statement of a fact this repo already had twice, and its
own mechanism - "the base report is partial" - was wrong. A partial base is real
and separate (a CANCELLED master run uploads nothing, which is why the base
totals here read 83 files against a head of 93), but it is not what makes the
per-flag gates red. That observation is offered on #414 rather than kept
here, because that PR owns the file it belongs in.

The rule this broke is in AGENTS.md and I quoted it in a merge commit two hours
before ignoring it: grep and find read the working tree, prior-art reads every
ref. Both offending notes were IN the working tree by then, so even the weaker
check would have worked - what failed was searching at all before writing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01228AtRiHyTzeH7SjcXzgNj
astubbs added a commit that referenced this pull request Sep 2, 2026
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
astubbs added a commit that referenced this pull request Sep 2, 2026
Four commits, no Java touching this branch's paths. Three conflicts, all the same
shape as before - two writers appending sightings to the same ledgers - and this
time both writers were recording the SAME two flakes, so neither side's count was
right and taking either whole would have lost real observations.

MdcContextPropagationTest: master had three sightings (one local on #262,
two on #416's CI across consecutive heads); this branch had a fourth, its
own local suite, and a fifth that matters more than the rest - MASTER'S OWN
`full build` at ebd8bcb. Unioned to five, with the master one called out,
because it is the one that stops being a test problem: `build` is master's only
coverage uploader, so its failure is why #414's merged glob fix has not
taken effect and why every open PR still sees red per-flag gates.

committedOffsetRemoved[1] latest: master had #407, #409 and
#416; this branch had #407, #207 and #409. Union is
four, and the row now says which of them captured what - #407 the
surviving record, #409 a rate, the other two neither.

Master also re-added a processInKeyOrder row at two sightings where this branch
already carries the six-sighting version from an earlier merge; its copy is
dropped rather than landing a duplicate row under one test name.

bin/check-all.sh 15/15, core compiles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01228AtRiHyTzeH7SjcXzgNj
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.

3 participants