Repository navigation
docs: index every TODO/FIXME/XXX marker, triage into the existing refactoring backlog - #103
Conversation
Two companion files, split so that neither invalidates the other: - docs/TODO_INDEX.md is GENERATED by bin/todo-index.sh - the raw what/where, 91 markers grouped by module with file and line. --check fails when the committed index is stale, so it can be wired into CI later. - docs/TODO_TRIAGE.md is HAND-CURATED - the same markers grouped into themes in priority order. It deliberately names classes and themes rather than line numbers, so regenerating the index never invalidates it. The machine owns what/where; a human owns what-matters. The triage exists so the analysis is done once instead of every future reader re-deriving it. What it found is more useful than a list: - The 91 markers are not 91 tasks. Five of them across three classes carry the same issue number (#233) and describe ONE design problem - OffsetMapCodecManager being encoder, decoder and consumer-holder at once. Four more are the same Apache Kafka MockConsumer workaround copy-pasted into four example tests. - Two P1 groups turn out to be the same story: state that should be instance state is static, and tests reach in to mutate it. That is what produced the largeOffsetMap flake, and #233 is why the class has static state at all - so fixing #233 likely subsumes most of the statics group, making it better value than its size suggests. - Most markers are correctly P4: notes-to-self next to the code they concern. The triage says so explicitly, so nobody converts them into tickets. Also fixes marker detection: shell variables (todo+=, ${todo[*]}), YAML field keys, string literals and references-to-a-marker were all counted. The anchored filters were being applied to grep -n output, so ^ never matched - noted in the script so it is not reintroduced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
Claude finished @astubbs's task in 3m 41s —— View job Claude finished the review
|
Per AGENTS.md's reference convention ('link the PR/issue'). Added at
PR-creation time, since the entry necessarily predates the number.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
✅ Duplicate Code ReportTwo engines run in parallel for cross-validation. Each has its own thresholds tuned to its baseline - the real safety net is the per-engine "max increase vs base" check. ✅ PMD CPD
No new clones introduced by this PR. ✅ jscpd (language-agnostic)
No new clones introduced by this PR. Powered by astubbs/duplicate-code-cross-check |
|
Claude finished @astubbs's task in 4m 7s —— View job ReviewI cross-checked Findings1. 2. The exclusion regex can silently drop a whole line — including a real marker on it — not just the excluded token. 3. Minor: 4. Minor: the "reference to a marker elsewhere" exclusion is a single hardcoded phrase. What's solid
|
✅ SpotBugs ReportNo bugs found (new bugs only — baseline from base branch excluded). |
🧪🔒 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 |
… list Correcting this PR's original shape. It shipped a docs/TODO_TRIAGE.md that duplicated docs/refactoring.md - a 295-line backlog that already had a release-gated 'Breaking changes queued for next major version' section, a 'Remove static state (unblocks parallel test execution)' section, and per-file entries for #233, the encoders and WorkContainer. My triage re-derived all of that from scratch, with worse provenance: refactoring.md carries the upstream issue numbers (#405, #126, #143) and design branches that I did not have. The mistake was concluding 'no breaking-changes list exists' after grepping only docs/inflight.md. Too narrow a search to support the conclusion drawn from it. So: - delete docs/TODO_TRIAGE.md rather than maintain two taxonomies; - fold into refactoring.md the two things it genuinely did NOT cover: the loop-cycle waits in ParallelEoSStreamProcessorTest (a known intermittent failure, where cycle-counting IS the flake mechanism), and the MockConsumer.groupMetadata() workaround duplicated across four example tests; - point TODO_INDEX.md and docs/inflight.md at refactoring.md as the place triage happens, with an explicit 'do not start a parallel list'. docs/TODO_INDEX.md still earns its place: refactoring.md's capture convention is TODO(refactor):, of which there are 3, while the index inventories all 91 plain markers - a population nothing was tracking. AGENTS.md now says refactoring.md owns marker triage, and to read it before concluding some category of deferred work is untracked, since it is easy to miss by grepping only inflight.md. That note is the actual fix for the mistake. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
|
Claude finished @astubbs's task in 2m 7s —— View job Review
The PR has evolved since earlier review passes: the triage now folds into the existing Finding
These two sections of the same generated file name two different destinations for the same kind of marker. It reads like the "Prioritised?" section was added when the design pivoted from a new Fix: update Minor / not blocking (carried over, still present, low priority)
Not verifiedNo shell-execution permission in this environment, so |
There was no docs index at all. AGENTS.md was the de-facto map, but its pointers were scattered across six sections - refactoring.md under 'Refactoring backlog', QUARANTINED_TESTS.md under 'Testing', inflight.md under 'Worktree ownership' - and SELF_HOSTED_RUNNER.md was linked from nowhere. That layout only works if you already know which section to read, which is circular. It is how this PR originally went wrong: grep inflight.md, find nothing, conclude nothing exists, duplicate refactoring.md. The guardrail added earlier had the same flaw - it lived inside the Refactoring backlog section, so it only reached someone who had already found the file it was warning them about. Now the warning is at the top, where it is seen before the mistake rather than after. One row per document: what it owns, and what it is explicitly NOT for - the boundaries are the part that gets guessed wrong. Plus the rule of thumb: happening now -> inflight.md; should happen later -> refactoring.md; already happened -> CHANGELOG.adoc or docs/solutions/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
|
Claude finished @astubbs's task in 1m 15s —— View job Review
The PR's design is sound and the pushed history shows real self-correction (the Finding (carried over from an earlier review pass on this PR - still unresolved)
The commit that pivoted this PR from a new
( But it left the very next section, "How to use this" (
Since precision and non-duplication are the explicit point of this PR (the whole rationale for the generated/curated split, and the thing the earlier Fix: update Minor / not blocking
Not verifiedNo shell-execution permission in this environment, so |
Review on #103: the generated header's 'Prioritised?' section was updated when triage moved to docs/refactoring.md, but the very next 'How to use this' paragraph still told readers that schedulable work belongs in docs/inflight.md - so the generated index contradicted itself two paragraphs apart, and pointed at the exact wrong file. A leftover from the pivot, and precisely the kind of stale instruction that caused the duplication this PR was correcting in the first place. Both paragraphs now point at docs/refactoring.md. Also drops emit_body()'s counter, computed and never read - the real count comes from count_markers(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
Review on #103: the generated header's 'Prioritised?' section was updated when triage moved to docs/refactoring.md, but the very next 'How to use this' paragraph still told readers that schedulable work belongs in docs/inflight.md - so the generated index contradicted itself two paragraphs apart, and pointed at the exact wrong file. A leftover from the pivot, and precisely the kind of stale instruction that caused the duplication this PR was correcting in the first place. Both paragraphs now point at docs/refactoring.md. Also drops emit_body()'s 'total' counter, computed and never read - the real count comes from count_markers(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
|
Claude finished @astubbs's task in 1m 14s —— View job Review
SummaryThe new commit (
No new issues found. The remaining items from earlier review passes (the quoted-string exclusion heuristic in Not verifiedNo shell-execution permission in this environment, so |
3342a3f to
c810bb8
Compare
|
Claude finished @astubbs's task in 1m 32s —— View job Tasks
ReviewThis is commit
No new issues found. The remaining items noted in earlier passes on this PR are cosmetic/latent and non-blocking, unchanged by this commit:
Not verifiedNo shell-execution permission in this environment, so This PR looks ready to merge from a correctness standpoint — the substantive finding from earlier passes is resolved and no new issues turned up. |
|
Both findings fixed in The contradiction was real and worth catching. The generated header's "Prioritised?" section was updated when triage moved to Also dropped On not being able to run 🤖 Generated with Claude Code |
A #103 reviewer could not run bin/todo-index.sh --check to confirm the generated index was regenerated - it had the test wrappers granted but not the repo's own verification scripts - so it had to infer freshness from the diff. That is the exact 'author asserts vs actually verified' gap this whole workstream is about. Grants the fast, read-only VERIFICATION scripts: bin/check-copyright- headers.sh, bin/test-check-copyright-headers.sh, bin/check-quarantine- registry.sh, bin/check-quarantine-owners.sh, bin/todo-index.sh. All are executable with shebangs, so the bare-path grant matches the existing ci-*-test.sh convention. The system prompt now tells the reviewer they exist and to prefer them over trusting the PR body. NOT the quarantine/test RUNNERS (quarantined-test.sh, quarantine-lane-report.sh): those execute suites, are slow, and test execution is already covered by the ci-*-test.sh wrappers. Deliberately still an allowlist, not blanket Bash(*): this job has no fork guard beyond sender.type != Bot - fork PRs are currently protected only by not receiving secrets, so the action fails rather than runs - and it reads attacker-influencable text (diff, PR body, comments). An enumerable allowlist is the margin against injection-into-execution; blanket shell here plus the pull-requests: write added for inline comments would be a pwn-request waiting for a trigger change. Reasoning recorded in the workflow comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
Both findings were correct. 1. Bash(bin/todo-index.sh:*) referenced a script that does not exist on this branch. It is added by #103 (docs/todo-index), which is not merged, so from here the grant is inert and the prompt instruction and changelog line claimed a capability that does not exist. The reviewer was right to check with find/git log rather than take the commit message's word for it; it was wrong only about the script never existing anywhere, since it lives on the unmerged #103 branch. Landing an unverified claim in a PR whose whole thesis is 'do not let assertions go unverified' is exactly the failure this workstream exists to catch. Dropped from the grant list, the system prompt, and the changelog; the grant belongs in #103 alongside the script. 2. Bash(./mvnw --batch-mode -Pci -pl parallel-consumer-core test:*) contradicted the comment sitting directly above it, which says the reviewer runs the repo's wrappers so it runs what CI runs. That bespoke invocation skipped -Dexcluded.groups=performance,chaos, quarantined - which the wrappers hardcode because it was once a silent no-op for unit tests, a real P1, now pinned by QuarantinedAnnotationContractTest for the wrappers but NOT for a workflow-embedded command - was core-only, and dropped clean, -Dlicense.skip and rerunFailingTestsCount. Removed: if the reviewer runs tests, it runs what CI runs. Adds a comment recording both rules, so the next person does not reintroduce either: every grant must be a script that exists on THIS branch, and there is deliberately no bespoke mvnw grant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
#102) The automated reviewer could never check a test claim. This workflow fires on pull_request open/synchronize - BEFORE the CI test jobs finish - so "just read the CI results" was never available to it, and with no execution grants it could only restate whatever the PR description asserted. That is not hypothetical. On #100 the reviewer said so itself: I could not run the build/test suite myself in this sandbox (./mvnw requires interactive approval I don't have here) - the verification table in the PR description (622 tests passing, specific chaos seed reproduction) is asserted by the author but not independently re-run. And it mattered. #100 added a regression test named MockConsumerTestWithRebalanceInProgressException, which matches none of surefire's default include patterns, so it was never collected. The suite was green BECAUSE the new test never ran. A review that trusted the author's totals would have waved through a permanent no-op. What changed: - Grants the repo's own wrappers (bin/ci-unit-test.sh, bin/ci-integration-test.sh) rather than blanket Bash(./mvnw:*), so the reviewer runs what CI runs, with the same profile and group exclusions. Plus the fast read-only verification scripts (bin/check-copyright-headers.sh, bin/test-check-copyright-headers.sh, bin/check-quarantine-{registry,owners}.sh) so it can re-run what a PR claims instead of inferring it from the diff. - Adds JDK 17 + Maven cache and fetch-depth: 0. Without these the grants would be useless - the job had no JDK at all, and a depth-1 clone cannot reach the PR base to diff against. - Tells the reviewer, via --append-system-prompt, that it MAY execute tests, that CI results are NOT available to it, that it must verify a new test was actually COLLECTED rather than trusting a green total, and that it should say plainly when it could not verify something instead of implying it did. - Raises blocking findings as INLINE review comments. The master ruleset sets required_review_thread_resolution, so an unresolved thread is the only thing that actually gates a merge; a blocking finding written only in a PR-level comment can be, and would be, merged straight past. Non-blocking notes stay in the summary - if every nit became a resolvable thread, the gate would stop meaning "something is wrong" and get dismissed when it matters. There is an explicit tie-breaker: if unsure, summarise and say why, rather than block on a maybe. - Caps the job at 30 minutes so a wedged suite cannot hold a runner, and moves to pull-requests: write for the inline comments. Chaos is deliberately not granted (Docker, ~2 minutes per scenario arm, and non-gating). Nor are the quarantine/test runners - those execute suites and are already covered by the ci-*-test.sh wrappers. It stays an enumerable allowlist rather than blanket Bash(*): this job has no fork guard beyond sender.type != Bot, fork PRs are protected only by not receiving secrets, and the reviewer reads attacker-influencable text (diff, PR body, comments) - so the allowlist is the margin against injection becoming execution. Two defects the reviewer itself caught during review, both fixed here: a grant for bin/todo-index.sh, which exists only on unmerged #103 and was therefore inert while the changelog advertised it as shipped; and a bespoke ./mvnw invocation that contradicted the comment above it by skipping the group exclusions the wrappers hardcode - a past P1 that QuarantinedAnnotationContractTest pins for the wrappers but cannot pin for a command embedded in a workflow string. KNOWN LIMITATION, recorded in docs/inflight.md: none of this has executed. claude-code-action refuses to run when the workflow file differs from the default branch's copy, and reports that skip as SUCCESS - so this PR's own green review check verified nothing, and the first real exercise is the next PR merged after it. Also left open there: whether CLAUDE_CODE_OAUTH_TOKEN is reachable from a Bash subprocess running PR-authored build code, that actionlint is not granted (so the reviewer cannot lint the very artefact this PR changes), and whether integration tests fit the 30-minute cap.
…esn't exist Two findings from the #101 review rounds. 1. gotK1 was write-only state, left behind when the old while (!gotK0 && !gotK1) loop condition was replaced by the event-based wait. Removed. 2. docs/inflight.md claimed the remaining awaitForSomeLoopCycles/sleep waits in this class were 'filed under Test infrastructure - timing-based waits in docs/refactoring.md'. That section exists only on #103's unmerged branch - zero occurrences here or on master - so the ledger cited a reference to nothing, in the PR whose own thesis is 'do not let something look tracked when it is not'. The reviewer flagged it three rounds running. Reworded to say #103 adds that entry and to file them there once it merges, rather than claiming they already are. Same mistake as the bin/todo-index.sh grant on #102: cross-referencing content that lives on another unmerged branch. Full reactor after both: 11 modules, 740 tests, 0 failures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
# Conflicts: # CHANGELOG.adoc # README.adoc
Review on #103 noted the scan covers .java/.sh/.xml/.yml/.yaml but not docs, and that the omission was reasonable but unexplained in the script's own scope comment. It was the missing rationale that was the gap, so this adds it - with evidence rather than assertion, because 'surely docs could contain a TODO too' is a reasonable first reaction. Scanning them was tried. It added 4 hits and not one was a marker: three were docs/refactoring.md QUOTING markers the index already lists from their .java source, so the same work appeared twice and the count inflated, and the fourth was prose ('seeded from a code scan (TODO/FIXME + ...)'). That is structural, not bad luck - refactoring.md is where marker triage lives, and AGENTS.md / inflight.md / CHANGELOG.adoc describe this tool, so scanning docs means indexing the index-of-work. The generated index is the reductio: on its own it accounts for ~95 self-referential hits. Also keeps two things the experiment surfaced, needed regardless: - a filter for compound names (todo-index.sh as a FILENAME, not a marker). This is live, not theoretical: merging master brought in #102's workflow comment naming the script, and the scanner was counting it as a 92nd marker. --check caught it, which is a decent self-validation of the tool. - excluded belt-and-braces, so a future widening cannot make the index index itself. Index back to 91 markers, --check clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
|
Claude finished @astubbs's task in 2m 33s —— View job Tasks
ReviewThis commit ( Verified claims:
One latent, non-blocking observation (same class already flagged and accepted twice before on this script): the new compound-name exclusion is applied via Not verifiedNo shell-execution permission in this environment (confirmed again — Nothing here blocks merge. This is a solid, well-evidenced close-out of the last open nit. |
# Conflicts: # CHANGELOG.adoc # README.adoc
|
Claude finished @astubbs's task in 1m 47s —— View job Tasks
ReviewThe only change since the last review pass is
No new issues found. All findings from earlier passes on this PR are resolved (the generated-header contradiction, the dead Not verifiedNo shell-execution permission in this environment ( |
AGENTS.md already said changelog entries must link the PR ("a bare #NN
refers to this fork ... and link the PR/issue"). It was missed anyway,
for a structural reason rather than carelessness: the entry is written
BEFORE the PR exists, so at authoring time there is no number to cite,
and nothing afterwards prompts you to return. #100, #101, #102 and #103
each shipped an entry with no self-reference, and each was caught one at
a time by a human reading the diff. That is the profile of a rule worth
making mechanical.
The gate: when a PR ADDS a new CHANGELOG.adoc entry, at least one added
entry must reference this PR (pull/NN or #NN).
Deliberately narrow, to avoid false failures:
- only genuinely new asciidoc bullets count, so reflows and edits to
older entries are ignored;
- word-bounded matching, so #100 does not match inside #1000;
- opt-out for a PR that touches the changelog without adding an entry of
its own: `changelog-ref: N/A - <reason>` on its own line in the body,
reason required, mirroring the checklist's existing N/A convention;
- bots exempt, consistent with the checklist gate;
- no changelog change at all means nothing to check - whether an entry
was NEEDED is already the checklist item's job.
The logic lives in .github/scripts/changelog-ref-gate.js rather than
inline in the workflow, and is unit tested (28 tests, plain node, no
runner) with the tests running BEFORE the gate, so a regression fails
the job loudly instead of silently mis-judging PRs. That is not
gold-plating: this logic shipped two real misjudgements during review -
an opt-out regex a body merely QUOTING the syntax could trip, and
positional add/remove pairing that mis-read a block holding both an edit
and a new entry. Mirrors the existing copyright.yml self-test step.
Pairing is by CONTENT, not position: an added bullet reads as an edit of
a removed one when they share a (#NN) citation, or failing that when
their wording overlaps enough. Position alone turns -A,-B,+A',+NEW,+B'
into exactly the wrong answer - +NEW consumed as an edit and never
checked.
KNOWN LIMITATION, raised in review and confirmed by construction. When
NEITHER bullet carries a citation, pairing falls back to word overlap -
and this changelog is full of same-template entries ("build(deps): Bump
<lib> to <version>"). Given a removed "Bump Testcontainers to 1.20.0", a
genuinely new "Bump Kafka to 3.9.0", and a reworded edit of the
Testcontainers line, the gate pairs the NEW line as the edit: it escapes
the citation check while the real edit is falsely flagged. Wrong both
ways, and the escaping half is the false negative this gate exists to
prevent.
Not tuned blind - the threshold is a considered choice and a guessed
replacement could easily be worse. Instead it is pinned by a regression
test that states outright it records ACCEPTED rather than DESIRED
behaviour (a failure caused by the gate getting stricter is an
improvement), and documented next to EDIT_THRESHOLD with the direction
to take if it bites: FAIL CLOSED, so an uncertain pair is treated as a
new entry needing a citation. That trades a silent miss for a visible
false alarm - and the false alarm has an escape hatch, whereas the miss
has nothing.
Needs pull-requests: read to fetch the patch, and an actions/checkout
(persist-credentials: false, since nothing there consumes secrets) so
the module and its tests are on disk.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

Description
91
TODO/FIXME/XXXmarkers were scattered across the tree with nothing tracking them. This adds a generated inventory, and triages the useful ones into the backlog that already exists rather than a new one.docs/TODO_INDEX.md- generated inventorybin/todo-index.shwalks the tracked tree and writes every marker, grouped by module with file and line.--checkexits non-zero when the committed index is stale, so it can be wired into CI later.This does not overlap
docs/refactoring.md's existing// TODO(refactor):convention - that captures 3 markers; this inventories all 91, a population nothing was tracking.Triage goes in
docs/refactoring.mdAn earlier revision of this PR added a separate
docs/TODO_TRIAGE.md. That was wrong and has been removed.docs/refactoring.mdwas already a 295-line backlog (before this PR; 319 after) that had:## Breaking changes queued for next major version- already listing the deprecatedcommitIntervaloptions andParallelConsumerOptionsfields I'd "discovered";### Remove static state (unblocks parallel test execution)- with upstream refs (upstream #405,#126,#143) and design branches I didn't have;WorkContainer,PartitionState.My triage re-derived that taxonomy from scratch, with worse provenance. The root error: I concluded "no breaking-changes list exists" after grepping only
docs/inflight.md- too narrow a search to support that conclusion.Two findings genuinely absent from
refactoring.mdwere folded into it:ParallelEoSStreamProcessorTestwaits on loop cycles, not events (four markers). Not a style point - the class is on the known intermittent-failure list, and cycle-counting is the flake mechanism: a loaded machine changes how much happens per cycle.MockConsumer.groupMetadata()workaround duplicated x4 across the example tests - one Apache Kafka defect, copy-pasted four times. Not ours to fix; the duplication is.Guardrail
AGENTS.mdnow records thatrefactoring.mdowns marker triage, and says to read it before concluding some category of deferred work is untracked - because it is easy to miss by grepping onlyinflight.md, which is exactly how this PR went wrong. That note is the durable fix; the rest is cleanup.TODO_INDEX.mdanddocs/inflight.mdboth point atrefactoring.mdfor triage, with an explicit "do not start a parallel list".Marker detection
101 markers on the first pass, 91 real. Excluded with reasons recorded in the script: shell variables (
todo=(),${todo[*]}), YAML field keys (upstream-map.yamlusestodo:as a schema field), string literals, prose referencing a marker elsewhere, and compound names where the word is part of a filename rather than a marker (todo-index.sh). That last one is live, not theoretical: merging master brought in #102's workflow comment naming the script, and the scanner counted it as a 92nd marker until filtered — caught by--check, which is a fair self-validation of the tool.Worth flagging one self-inflicted bug found while building it: the anchored exclusions were applied to
grep -noutput, so^never matched and they silently did nothing - noted in the script so it isn't reintroduced.Why
.adoc/.mdare not scannedRaised in review, and worth recording because "surely docs could contain a TODO too" is a reasonable first reaction. It was tried, and the result is in a comment in
list_files():Scanning docs added 4 hits and not one was a marker. Three were
docs/refactoring.mdquoting markers the index already lists from their.javasource — so the same work appeared twice and the count inflated — and the fourth was prose ("seeded from a code scan (TODO/FIXME + ...)").That is structural rather than bad luck:
refactoring.mdis where marker triage lives, andAGENTS.md/docs/inflight.md/CHANGELOG.adocdescribe this tool, so scanning docs means indexing the index-of-work. The generated index is the reductio — on its own it accounts for ~95 self-referential hits.The comment also says what to do instead: if a genuine marker ever does land in prose, move it into the code it concerns, where the scan will find it.
$OUTis now excluded belt-and-braces so a future widening cannot make the index index itself.Notes
--checkexists for it, but making it gating means every added or removed marker fails the build until regenerated - worth deciding deliberately, not as a side effect here.Checklist
CHANGELOG.adoc) - Build & CI, citing this PR;README.adocregeneratedTODO_INDEX.md;refactoring.mdgains two backlog items and the marker-triage ownership note;inflight.mdandAGENTS.mdupdated to point at itbin/todo-index.sh --checkis its own round-trip check and was run against the generated output🤖 Generated with Claude Code