Repository navigation
ci: announce a quarantined test that started passing, and share the sticky-comment machinery - #409
Conversation
…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.
|
@codex review Context, so this pass spends its effort where the local one could not: an independent Codex review was Where the risk actually is:
Two known items, so they are not re-reported: |
…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.
[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its Since the previous push: Updated for Superseded by a newer quarantine lane report. |
…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.
There was a problem hiding this comment.
💡 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".
[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its Since the previous push: Updated for Superseded by a newer quarantine lane report. |
…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.
[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its No quarantined test changed outcome since the previous push. Updated for Superseded by a newer quarantine lane report. |
…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.
|
@claude review this Using the mention route deliberately: three What already happened, so you spend effort on what is still open. An independent Codex review Look hardest at whether that class is genuinely closed, and at what sits next to it:
Known, and not yours to re-report: |
|
Claude finished @astubbs's task in 4m 50s —— View job Review completeTodo
One environment note, not a PR defect. I couldn't execute 1. The "confident false negative" class — looks genuinely closedWalked every finding from the earlier Codex pass against current HEAD (
I didn't find a remaining path where absence/error/truncation collapses into a clean negative in these two files. 2.
|
…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.
[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its Since the previous push: Updated for Superseded by a newer quarantine lane report. |
… 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.
[superseded - a quarantined test changed outcome] 🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its Since the previous push: Updated for Superseded by a newer quarantine lane report. |
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.
🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its Since the previous push: Updated for |
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.
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.
…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
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
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
Was stacked on #400 for the front door its Codecov commands are a row in;
that has now merged, so this targets
masteragain 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
@Quarantinedannotation 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-scriptrequire()s it the waypr-checklist.ymlalready loads four gate modules.Three consumers.
throughput-report-comment.jsandquarantine-report-comment.jshold each report's own vocabulary. The throughput step inmaven.ymlwent 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-pushedanchor.Two live defects found on the way
gh apiupsert had no author filter — the same class that let the SpotBugs step overwrite a human's comment.set -etook the whole step down, so a rate limit while commenting could red a job whose lane ran fine. Posting is now a separatecontinue-on-errorstep; 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.jsruns the real reporter three times against a fixture whose outcome flips and feeds its actual output through the reader. RenamingPASSED_ACTIONor 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_RUNcannot distinguish "not selected" from "the build broke before integration tests", andsteps.run.outcomecannot help because it isfailureon almost every run by design. A design decision, so it is written down rather than guessed at.app.codecov.ioaccess; 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.ymlandclaude-code-review-dispatch.ymlgrant the reviewer two.test.jsfiles by name. Broadening that to the glob would let a reviewer run PR-authored JavaScript — a security-shaped decision, not made unasked.Checklist
docs/features/— N/A - CI reporting, not a library featuredocs/inflight/working note started —ci-quarantine-lane-not-run-cannot-name-a-broken-build.mdplus the PIT/Codecov notece-simplifyandce-code-reviewlocally — N/A - asking for@claude review thison the PR insteadAdded since the original description
Codecov, reachable from the front door.
bin/inflight.mjs codecovexposes the per-test outcomeand wall-clock history Codecov keeps per commit -
codecov test <name>for "which commit did thischange at" (a bisect that costs one HTTP request),
codecov flakyfor the sighting evidence aquarantine entry needs,
codecov slowfor what owns the CI wall-clock. Unauthenticated, because thisrepo 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 passedliterally to a CLI that does not expand
**, matched nothing, and the CLI fell back to a tree-wide*test*.xmlsearch - ingesting 18 logback config files alongside 39 genuine reports, entirelygreen. Now expanded with
find, withdisable_search: trueas a second lock, and moved off thedeprecated
test-results-actionontocodecov-action@v5.CodeQL HIGH (
js/bad-tag-filter) fixed rather than dismissed. The superseded marker was derivedby 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.mjsreportsmutant of explicit-help-succeeds goes redas FAILED. Thispre-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.urlresolves to the realpath whileprocess.argv[1]does not, and macOSos.tmpdir()is a symlink (/var/folders/...->/private/var/folders/...). So inside theself-test's temp dir the "invoked directly" guard at the bottom of
bin/inflight.mjsis false, theCLI body never runs, and
inflight helpprints nothing and exits 0. Changing only the path formflips 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 wrongreason, 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
/tmpis 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.jscollapsesthem — 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 havedeleted 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_TOKENwas documented in
AGENTS.mdand 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 flakycould exit 0 with an apparently complete "no flakes" — in the exact situationa 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%) andcodecov/project/integration(−15.32%) fail on branches thatchange no Java. Cause proven from the upload logs: the uploader's CLI does not expand
**, sofiles:matches nothing and it falls back to a tree-wide search. On master's full build that findsevery jacoco report — both halves — so
unitandintegrationeach receive the whole tree and reportan 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 instancesremain, 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.mdanddocs/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.mjsfails two mutation checks inherited from #400 (arealpath/symlink guard; macOS-only, invisible in CI). Not introduced here — confirmed by control.