Repository navigation
ci(copyright): check every file type, not only Java, and fix the 42 it finds - #338
Conversation
…t finds The gate has only ever looked at Java. It reported "255 java files, 0 violations" and that reads as a clean tree, but it was a clean tree of one file type out of a dozen. Everything else - workflows, poms, shell, JavaScript, Python, XML - has been outside its view since the fork, so a file could land with no notice at all and the check would go green. Widening it to the whole tree turns up 41 files that should have carried a notice and did not, in the repo as it stands today. They are fixed here rather than exempted: 21 hash-comment files, 12 XML, 8 JavaScript. Two different fixes, because the policy distinguishes two kinds of file and so does this change. Fork-original files - added after the fork point, owing nothing to upstream - get the fork header. Upstream-derived files that have been modified since the fork point instead get a Modifications line added to the Confluent notice they already carry, which is what Apache 2.0 s4(b) and s4(c) ask for and the convention Corretto and MariaDB use. Eleven of the 41 are that second kind, including the root pom; giving them a fork header would have claimed original authorship of a derived file, so getting this wrong is worse than leaving them alone. The scanner itself is driven by a file-type table mapping a glob to a comment style, so adding a language later is one line rather than new logic. An extensionless file that opens with a shebang names its own language and needs no entry. The table carries entries for languages this repository does not yet have - Go, Rust, Swift, Kotlin, C - which cost nothing while unused and mean the first such file to arrive is covered rather than silently skipped, which is the failure this change exists to end. Verified: the scanner's own self-test passes; `./mvnw validate` resolves the whole reactor, so every modified pom still parses; every modified shell script passes `bash -n`; every workflow still parses as YAML; and the gate reports 0 violations across 403 files against the pinned fork point.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
✅ Duplicate Code ReportTwo engines run in parallel for cross-validation. Each has its own thresholds tuned to its baseline - the real safety net is the per-engine "max increase vs base" check. ✅ PMD CPD
No new clones introduced by this PR. ✅ jscpd (language-agnostic)
|
|
🧪🔒 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 |
Records what #332 cost, next to the stacked-PR rule it qualifies. That PR was a branch and a draft for a single 118-line design note. The cost was not cosmetic: two PRs of shipped, tested code stacked above it, and the dependency rule then gated them behind a draft whose own first paragraph said three decisions were open and not to start. Neither referenced its subject - the stacking was chronology, not dependency. The rule the episode suggests is narrow: a branch carrying only a document does not need its own PR unless somebody must review that document separately from the work it describes, and "whoever picks the work up will read it" is not separately. Riding on this branch rather than taking one of its own, deliberately. A one-line convention change does not warrant a PR, which is the same judgement the rule itself is about - and the alternative would have been to demonstrate the mistake while writing it down.
…ne that has to credit the fork
Simplify-then-review pass over the widened scanner. No change to any of the 41
header additions - a wrong header is a licensing error, not a style nit - and
`--report` output is byte-identical before and after.
**The mods-line test was looser than the grep it replaced, and that is a real
defect.** `read_header` checked for "Modifications Copyright (C)" and the fork
holder as two independent substrings of one line; the
`grep "Modifications Copyright (C).*${FORK_HOLDER}"` it replaced required the
holder to FOLLOW the phrase. So a line reading
`@author Antony Stubbs and contributors - Modifications Copyright (C) 2026 SomeOtherCorp`
was accepted as the fork's modifications claim while crediting somebody else.
One `case` pattern with a `*` between the two literals restores the ordering at
the same zero-subshell cost. Fixture 5b in the self-test is the regression case,
and it goes RED against the pre-fix scanner (verified on a copy: 2 failures,
"detects a mods line crediting another holder" plus the violation count) and
green after.
**The grandfathering excuse was written out twice.** The registered-rename branch
and the plain upstream-derived branch carried the same eight lines, differing only
in which blob sha is the origin and how the message reads; the extraction and
recovery branches carried the same two lines as each other. What is duplicated
there is the licensing verdict, and two copies of an excuse drift in the one
direction nobody notices - a file quietly stops needing the notice it owes. Now
`check_derived` (grandfathering-aware) and `check_always_derived` (no fork-point
blob to be excused by), and each branch is one call. `require_confluent` sets
CONFLUENT_FAIL instead of printing, which is what let the grandfathering callers
hold the message back - the old shape did it by writing a temp file per scanned
file.
**Three claims in the prose were not true of this tree.** The header said the scan
grew past `.java` "until the fork grew seven client libraries in seven other
languages", and both the script and docs/copyright.md described "the TypeScript
client" as an existing thing. There is no Go, Rust, Swift, Kotlin, TypeScript or
Ruby file in the tree; those clients live on the language-proxy stack
(#340 adds .c/.go/go.mod/.mjs/.options under
parallel-consumer-proxy-clients/, already correctly headered). The PR body already
had this right - "languages this repository does not have yet" - so the tables are
unchanged and only the prose is corrected, with the reason listing them early is
sound stated where the table is.
**The recovery table cited a file that resolves on no reachable branch.**
`docs/inflight/branch-classic-comparison-demo.md` is on `docs/direct-pull-claim-check-then-act-diagnosis`,
which has no PR - not on master and not on the branch the recovery arrives with.
`bin/check-file-refs.sh` could not catch it because it reads .md/.adoc/.txt/.html
only, so a citation inside a shell script is invisible to it: the same dangling
path in a scratch .md fails that gate with exit 1, and in the .sh it exits 0.
Citation dropped rather than repaired, and what the verification actually depends
on - `ffda9c6a` staying reachable - said plainly.
**docs/copyright.md owns which header a file gets, and did not mention the third
provenance table** this PR introduces. Added, with why a recovery is not filed as
an extraction: its origin is one commit, so the claim is verified rather than
trusted.
Also dropped `fp_path_of`, a `$(...)` wrapper around `fork_point_path` that forked
a subshell per manifest entry - the same cost the wrapped function exists to avoid.
Rejected: genericising the three manifest-parse loops and three linear searches.
bash 3.2 has no namerefs, so it needs eval-based indirection - worse than the
duplication. Also rejected: widening `*/go.sum` and friends in EXEMPT_PATHS to
match the repo root. Over-broadening an exemption is the wrong direction for a
licensing gate, and a root-level `go.sum` is already handled correctly - it lands
in the unclassified set and fails the build, which is the design.
Verified locally, not deferred to CI: bin/test-check-copyright-headers.sh green,
bin/check-copyright-headers.sh 409 files / 0 violations, `./mvnw -N validate`
exercising the same gate through exec-maven-plugin, all 14 bin/test-check-*.sh
suites, and check-shell-sigpipe / check-file-refs / check-issue-refs /
check-action-versions / check-docs-data / check-inflight-tags green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ld have failed Review of this PR found five holes in the guarantees the scanner states about itself. Four are in code this PR wrote; all five are reproduced on throwaway fixtures rather than argued, and every one gets a red arm that goes RED against this PR's own pre-fix scanner (af794e5) and green after. **The fork-original arm tested for a NAME, not a NOTICE.** `HAS_FORK_HOLDER` was a window-wide search for `Antony Stubbs and contributors`, so a file whose header read only `@author Antony Stubbs and contributors`, with no `Copyright (C)` anywhere, passed green - in any of the 45 enforced languages. The tell: rename the holder to anything else and the same file goes red, because the test was matching the string, not the claim. That is precisely the shape the PR exists to end ("a file could land with no notice at all and the check would still go green"). Now `HAS_FORK_NOTICE`, same-line, matching its two siblings. The comment defending the window-wide test said an `@author` byline "is excluded by the mods-line rule above" - but the fork-original branch is the flag's only consumer and never reaches that rule, so the defence never covered the case. **Grandfathering excused a fork copyright claim over upstream code.** `require_confluent` fails identically for "no notice at all" and "has a notice that is not Confluent", and `check_derived` reached for the excuse in both. So an upstream file that upstream never marked could be rewritten, stamped `Copyright (C) 2026 Antony Stubbs and contributors`, pass green, and be counted in the grandfathered tally as an unmarked upstream file. The PR body names this exact error class as the one "worse than leaving them alone". The excuse is now gated on `HAS_NOTICE = 0`: not growing a notice is what Apache 2.0 4(b) permits; claiming authorship of someone else's work is not. **The excuse also failed OPEN on an unreadable blob.** `git cat-file blob ... || true` left `$content` empty, which reads as "never marked" - so on a blobless partial clone an upstream file that WAS marked and has since lost its header was grandfathered. A read we could not perform is not evidence; it now warns and declines to excuse. **The shebang rule was implemented for `hash` only.** A `.mjs`/`.js`/`.ts` CLI with its notice above `#!/usr/bin/env node` is broken - the kernel stops seeing a shebang - yet passed green, while the identical `.sh` file failed. Both this script's own contract and docs/copyright.md state the rule unconditionally. Factored into `shebang_below_header` and called from both styles, so a third style cannot inherit the gap by omission. **"A quiet skip is not reachable from here" was reachable by naming a file in almost any language but English.** `git ls-files` C-quotes any path with a non-ASCII byte, so `$f` arrived as the literal `"Caf\303\251.java"`, `[ -f "$f" ]` was false, and the file was dropped before classification - not enforced, not exempt, not unclassified, absent from `--report` entirely. Measured: a headerless `Café.java` and an unclassifiable `mystère.zig` both passed green, `--report` listing 2 rows for 4 tracked files. Now `-c core.quotePath=false`, with a path that is still quoted reported as unclassified and a non-regular entry (gitlink, deleted) counted as exempt with a reason. New case 38d asserts `--report` emits exactly one row per tracked path, which is the invariant stated end-to-end rather than per-rule - the assertion that would have caught this. **The self-test died silently when its own parser broke.** Cases 25 and 26 capture `$(grep ...)` unguarded; a zero match returns 1 and `set -euo pipefail` killed the whole script on the spot - no FAIL line, no summary, and the "could not rebuild/read" diagnostics three lines further on were unreachable exactly when they were needed. Every case after that point vanished with it. Proven: against a scanner whose PACKAGE_MOVES the parser cannot read, the suite printed 29 lines and no summary; it now prints 65, reports both diagnostics, and exits 1 saying "8 scanner self-test(s) FAILED". Also: ENFORCED_TYPES now says its layout is load-bearing (the self-test reads it with an anchored awk, as PACKAGE_MOVES already documented for itself), and the header cites docs/solutions/workflow-issues/duplication-scanners-do-not-look-where-agents-duplicate-2026-08-12.md, which already generalises this defect class - "the scanners did not miss the duplication; they never looked at the files containing it". Deliberately NOT changed, and reported instead: narrowing `.claude/*`, which exempts seven hand-written hook scripts that already carry correct headers - that is a policy call about whether unshipped agent tooling owes a notice, and it is the author's. Likewise the duplicated grandfathering prose between this script and docs/copyright.md. No table row was added or removed; ENFORCED_TYPES and EXEMPT_PATHS remain byte-identical to 935d8b8, and none of the 41 header additions is touched. Verified locally, not deferred to CI: gate 409 files / 0 violations and `--report` 636 rows for 636 tracked paths, `./mvnw -N validate` through exec-maven-plugin, the copyright self-test green and its new arms red against af794e5, all 14 bin/test-check-*.sh suites, and nine other repo gates. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The review closed five real holes in the gate on this branch. What is left is a policy call, two body/doc corrections, and - the part worth keeping - the observation that a glob narrower than the gate's claim is now a defect CLASS with three instances, not a one-off. The copyright gate was instance one and is fixed here. check-shell-sigpipe scans bin/*.sh only; file-ref-gate reads four prose extensions and already grew once for .html, so it is the second recurrence with nothing recording it. Written down because these lived only in an agent report.
|
@claude review this |
|
Claude finished @astubbs's task in 5m 46s —— View job Review: PR #338 — copyright gate widened beyond Java
Note on tooling: this sandbox checkout is a shallow clone ( What I verified by reading the code
Minor observations (not blocking, low severity)
On what's already trackedThe five gate holes were the substance of this review pass and are closed. Everything else already surfaced — Summary: this is a solid, well-tested fix for a real gap (255 Java-only files checked → whole tree), with genuine red-arm coverage for every hole the review process found. No blocking issues found; the three notes above are minor/speculative and don't need to hold up merge. |
… not The type table exempted `*.md`, `*.adoc` and `*.html` because "the notice would render into the document". That is false. All three have non-rendering comment syntax, and this repository already depends on it - every docs/inflight note carries `inflight-type` markers no reader ever sees. A false reason is worse than no reason, because the obvious way to act on it is to add headers to a hundred documents. The exemption stands; the reasoning is replaced with the real one, and docs/copyright.md now carries the argument for the whole table rather than leaving it implicit. The real basis, stated so the next person does not have to rederive it: a notice does not create protection - copyright subsists automatically under Berne, and has needed no notice in the US since 1989, so prose is protected either way. What a notice does is reach a reader who has the file and NOT the repository: it carries the terms, it makes the file legible to a licence scanner, it defeats an innocent-infringement defence, and for upstream-derived files it discharges Apache-2.0 4(b). So the question is whether a file can arrive detached from LICENSE. Source jars are published, so .java can and does; the poms travel beside the jar, which is what makes the root pom's Modifications Copyright line the most load-bearing line the gate enforces. Prose cannot: no .md or .adoc sits under src/main, nothing sweeps docs into an artefact, and README.adoc is generated at build time and lives at the repository root. A per-file notice there has no recipient. Everything else that can hold a comment stays uniformly headered, ships or not. That is deliberately over-inclusive, for a mechanical reason rather than a legal one: "does this file travel?" is a per-file judgement, easy to get wrong, and silent when wrong. A type-keyed rule is one a scanner can enforce and a reviewer can check.
The `inflight: tags` job was red: check-branch-self-reference.sh flagged three lines in docs/inflight/pr-338-review-findings.md as claims this merge falsifies. That gate ships on this very branch, and the note it fired on is the note this branch added - the recurrence its own header predicts. The fix is the one the gate asks for, applied to the filename as well as the prose. docs/inflight/AGENTS.md says a `pr-` note is "context about an open PR", and that a closing item which leaves follow-ups is shrunk to those and renamed. After this merges #338 is not open, so `pr-338-review-findings.md` would be a name that lies while its content is still true. It becomes ci-copyright-gate-review-leftovers.md, tense-corrected, with `post-merge: checked` blocks on the two paragraphs that still cite the PR - permanent history the gate's header explicitly says is fine to cite forever. Nothing in the tree referenced the old path. Two findings left in the same commit that resolved them, per the note's own contract: - The PR body no longer omits the AGENTS.md rule; it carries an "Also in this PR, and not about copyright" section. - bin/soak-test.sh no longer passes -Dlicense.skip. docs/copyright.md, changed by this PR, says that property no longer exists; -Dcopyright.skip=true sat beside it on both lines and is the live one, so the flag was dead, not load-bearing. One finding was stale and is corrected rather than dropped: check-shell-sigpipe.sh does not scan `bin/*.sh` only - it globs `bin/*.sh` and `.claude/hooks/*.sh`, one directory level each. The gap is real but narrower than recorded: bin/lib/, scripts/ and the two repo-root scripts are outside it, four of those five carry no `set -o pipefail`, and upstream-sweep.sh's `grep -qx` reads a file argument rather than a pipe, so it cannot hit the bug. Verified file-ref-gate.js's CITING_FILE is still /\.(md|adoc|txt|html)$/i, so that instance stands as recorded. Verified locally on a full (non-shallow) clone: the copyright gate scores 409 files, 150 of them non-Java (53 sh, 46 yaml, 21 xml, 19 yml, 8 js, 1 py, 1 cmd, 1 extensionless), 0 violations; its self-test passes; the self-reference gate is green under the CI branch name and PR number. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@claude review this |
|
Claude finished @astubbs's task in 2m 3s —— View job Review: PR #338 — copyright gate widened beyond Java (re-review at
|
"needs cleanup" on the findings note — done in 9c5042fReplying here rather than in-thread because that comment sits in an unsubmitted pending review of yours; GitHub refuses a reply into another pending review, and I did not want to touch (or submit) your draft. Resolve/discard it at your convenience. The concrete thing that was wrong: the note was failing
One judgement left to you: the note still bundles four items — two copyright-gate policy calls, the AGENTS.md-length backstop, and the cross-gate narrow-glob defect class — against Verified locally on a full (non-shallow) clone: copyright gate scores 409 files, 150 non-Java, 0 violations; |
The re-review at 9c5042f carried forward two non-blocking observations about bin/check-copyright-headers.sh that nothing in the tree recorded. Neither is worth reopening the gate work for, and both are the kind of thing the next session rediscovers if it is not written down. Only the FIRST notice line is placement-checked: syntax_violation() reads HDR_LINES[$NOTICE_IDX], so a dual header's `Modifications Copyright` line is never tested for sitting inside a comment. Checked how far that reaches - the shebang and `<?xml ?>` arms cannot be violated by a later line, because a later line is by definition below a first line that already cleared them, so the only live case is a mods line written outside the comment syntax. That case is loud rather than silent (`Modifications: command not found` in shell, a compile error in Java, an unparseable document in XML), which is why it is recorded as worth-closing-next-time rather than fixed under this PR. The same-line Confluent-claim test can false-positive on a fork-original header that puts `Copyright (C)` and `Confluent` on one physical line. No file does today and the failure is a red build, not a silent pass - the deliberate trade-off the same-line design already argues for in the script. Noted so it is not re-derived. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@claude review this Head is now |
|
Claude finished @astubbs's task in 46s —— View job Review: PR #338 — copyright gate widened beyond Java (re-review at
|
…one base Review feedback, "this will need syncing". Measured rather than assumed, and it is worse than a routine conflict. bin/check-copyright-headers.sh exists on master in its Java-only form, and both #338 and #340 independently rewrote it from that same base: master to #338 is +609/-59, master to #340 is +477/-40, and the two copies differ by +202/-89 across ~24 hunks (merge base 0e21b90). Whichever lands second gets a large conflict in that one file. The reason this earns a note rather than being left to whoever hits the conflict is that resolving it the wrong way is SILENT. #340's copy carries none of the five gate-hole fixes, and still carries the prose exemption's retired false reason. A conflict resolved toward it - or hand-resolved hunk by hunk - restores a scanner that reports a clean tree while not doing its job, which is the exact failure the widening exists to end, and it goes GREEN rather than red. Checked that the resolution is genuinely asymmetric rather than assuming the newer branch wins: everything #340's copy has that #338's lacks is code #338 replaced on purpose - HAS_FORK_HOLDER (the name test, not a notice test), the hash-only inline shebang check now shared as shebang_below_header(), fp_path_of(), and the false prose reason. The polyglot rows both branches need (*.go, *.rs, *.swift, *.kt, *.c, go.mod, *.mjs) are byte-identical in the two copies, so taking the newer file whole loses nothing. Filed separately from the copyright-gate leftovers note on purpose: this carries a different impact (coordination, not ci) and a different delete trigger - it dies when the second branch lands, not when the gate's open questions are settled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@claude review this Head is now |
|
Claude finished @astubbs's task in 2m 48s —— View job Review: PR #338 — copyright gate widened beyond Java (re-review at
|
The note filed one commit ago pinned the collision on #340. That is the branch stacked ON TOP of it, and the mistake matters more than a wrong number would: `gh pr diff #340` shows a ONE-LINE change to bin/check-copyright-headers.sh, because #340 is based on feats/polyglot-demos rather than master and inherits the rewrite from it. A reader following the note to the PR it named would have seen a trivial diff and concluded the note was overblown - the same false negative this class of finding is about, reproduced inside the note warning about it. The review pass hit exactly that and had to go find the blob by hand. Corrected, measured against the right branch: the rewrite is #331 (feats/polyglot-demos), master to it is +476/-40, it to #340 is +1/-0 (the `*.options|hash` row), and its copy differs from this branch's by +203/-89. Verified #331's copy is missing the same five fixes, so the asymmetry argument is unchanged - only its subject is. Two other corrections from the same pass: - Dropped "then re-apply only genuinely new entries". There are none: `*.options|hash` is the only row the stacked branch adds and this branch's copy already carries it, so the take-whole is complete rather than a first step. - Named the two fixes the bullet list had left to the summary sentence - grandfathered() failing open on an unreadable blob, and plain `git ls-files` instead of `-c core.quotePath=false` - so all five are checkable from the note rather than four plus a claim. Also recorded that bin/test-check-copyright-headers.sh is untouched by the other branches, so the one script is the whole of the conflict. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@claude review this Head is now Measured myself against the right branch: master→#331 +476/-40, #331→#340 +1/-0, #331 vs this branch +203/-89, and #331's copy missing |
|
Claude finished @astubbs's task in 2m 58s —— View job Review: PR #338 — copyright gate widened beyond Java (re-review at
|
Chaos Pain Suite went red on this branch, and the existing runner-load entry already argues the class is box contention rather than any PR's doing - but it argues from unrelated branches minutes apart, which a reader can dismiss as coincidence. This run supplies a tighter control arm on the same branch, so the next person does not have to re-derive it. Chaos PASSED on 3c1ff83 (run 32797902524, 01:32-01:44) and FAILED on 8a366ec (run 32799585950, 02:02-02:14). The whole diff between those commits is one markdown file under docs/inflight/, +55 lines, no code - on a branch that touches zero .java files. An outcome that flips across a delta which cannot reach the engine is not a regression, and that is a stronger statement than interleaved successes on different branches. Also recorded because the SIGNATURE differs from the one that entry describes. That one is a killed process: logs stop dead, no BUILD FAILURE, no stack trace. This one failed loudly mid-ChaosRevokeUnderWorkCooperativeIT (seed 784617418707025255) with starvation symptoms - records queued over 10s, "Clean execution pool termination failed" across a dozen PC instances, and a RebalanceInProgressException storm. Those read exactly like a product stall to anyone who has not checked the load first, which is the expensive mistake. Deliberately not doing two things. Not rerunning to get green: the box is already carrying concurrent chaos suites, a rerun adds load to the cause and would paper over the honest signal. Not quarantining: quarantine is master-state and needs a diagnosis of the test, and this is neither - the diagnosis is the environment. Chaos is not in the required-status-checks ruleset, so the red does not gate the merge; every required check on this head is green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Chaos Pain Suite went red — diagnosed as box contention, not this PRRecorded in The control arm is unusually tight, which is why I trust it. Chaos passed on The signature is starvation, and it is a different one from the entry already in that note. That entry describes a killed process — logs stop dead, no Two things I deliberately did not do:
If you'd rather see it green before merging, say so and I'll rerun once the box quiets down — but I'd be re-rolling dice, not fixing anything. |
…ints at `docs/merge-checklist.md` was corrected on master to say the squash message is printed in chat and never written into the PR body. This file's one-line pointer still said "write the squash message where it is used, not into the conversation, unless asked for it" - which is the superseded rule, stated in the place an agent reads first. That is the duplicate-drift failure this file's own "How to write this file" section warns about, and it is not theoretical: this file is loaded whole every session and the checklist is injected on merge-shaped prompts, so the two were telling the same agent opposite things in one turn. Left alone, the copy read first wins. Rewritten as a pointer that carries only the part which binds every session - never the PR body - and defers the rest to the document that owns it, rather than restating a rule that has now been wrong once. Rides on this PR rather than its own: PRs are expensive, this is two lines of prose, and this branch already carries an unrelated AGENTS.md process rule for the same reason.
…ame was about to lie Antony's review on #348 said the note "needs extraction or deletion". Taking that as: extract the still-open items into a note named for the AREA, and delete what the PR discharged. docs/inflight/AGENTS.md is explicit that "A filename must never carry state", and `pr-` means "Context about an open PR that gh cannot tell you" - so pr-348-review-findings.md starts lying the second the PR merges, and every citation to it would then have to be repaired rather than left alone. The directory has already paid this: `parked-` and `next-` were removed for exactly this reason, all 23 `next-` files being `open`. The three sibling PRs hit the same wall and renamed the same way - #341 to ci-bsd-portability-gaps.md, #338 to ci-copyright-gate-review-leftovers.md, #347 to test-lincheck-lane-open-items.md. test-, because the note is about test infrastructure: a probe harness, its correspondence to product code, and what no check verifies about either. Re-checked every item rather than moving them wholesale. What changed: - The duplication-lane item is discharged, so its measurement is dropped (similarity peaked at 59.7% against the 80% fail line; both caps hold). What survives is the part nothing else records: the near-duplicate arms ARE the measurement - the ~130x suppression between reduced and faithful is the finding - so they must never be collapsed to quiet the scanner. The no-allowlist hazard stays owned by ci-dup-similarity-cannot-accept-known-duplication.md rather than restated. - "A vacuous run is indistinguishable from a clean one" is now "Nothing enforces reading the positive control" - jcstress-poc/pom.xml carries the warning durably as READ THE CALIBRATION BEFORE BELIEVING ANY ZERO, so the open part is only that it is prose no check reads. - The drift-check item and the "how does this grow" item were the same item from two sides - the proposed check is the mitigation for the growth problem - so they are one section. - Every pre-merge tense is gone: "on this branch", "did not close", "delete it when #348 lands" now read as they will after the merge, which is what bin/check-branch-self-reference.sh exists to force. The post-merge: checked block is narrowed to the header that names the PR. - Verified still open, not assumed: the plan doc cites neither docs/refactoring.md nor core-control-thread-contract-debts.md; no durable doc points at the module; #349 is open, so the priority tension stands. The other four inflight notes this PR touches stay separate files - one item per file is the directory's whole reason for existing.
TEXTUALLY THERE WERE NO CONFLICTS, WHICH IS NOT THE SAME AS NOTHING TO RESOLVE. Nine files were touched on both sides and all nine auto-merged correctly - the five workflows and two gate scripts took master's rewritten bodies with this branch's two-line header on top, and AGENTS.md and pom.xml kept both sides' edits. The real conflict this merge had to settle was semantic: master added files that the widened gate is the first thing to judge. TWO NEW FILES UNDER config/ FAILED THE WIDENED GATE, both from master's static-analysis work: - `config/spotbugs-exclude.xml` is fork-original and carried no notice. Given the fork header inside the existing `<!-- -->` block, below the XML declaration where the gate requires it. - `config/infer-known-findings.txt` was UNCLASSIFIED - the file type is in neither table, which is a violation in its own right rather than a skip, and that is the design working. Classified `enforce`/`hash` by BASENAME, not as `*.txt`. Its extension lies about its format: the file is a `#`-commented record set and bin/infer-test.sh strips `^\s*(#|$)` before comparing it, but `.txt` in general is plain text with no comment syntax at all, so `*.txt|hash` would demand a `#` header on the first genuine text file to arrive and be wrong about it. Same shape as the CMakeLists.txt, Makefile and Rakefile rows already in the table. Verified the strip still yields the same 21 data lines as master's copy, so the ratchet's identity set is untouched. The gate now scores 447 enforced files - 278 Java, 169 not - with 0 violations, and the per-language self-test arm generated from ENFORCED_TYPES covers the new row. The set grew by roughly the number of files master added, which is the check that matters: a widened gate exiting 0 on a glob that matched nothing is indistinguishable from a pass. THE CHAOS-CONTENTION REASONING THIS BRANCH ADDED IS NOW SUPERSEDED, and saying so is the point. `docs/inflight/ci-disabled-jobs-and-runner-load.md`'s 2026-08-25 entry argues a Chaos Pain Suite red was a contended box; master's 2ccd3c7 then re-keyed the concurrency group off the ref and onto the `box-exclusive` matrix key and put every job that can occupy the host into it. A chaos red taken after that commit is a new fact, not another instance of this entry, and the note now says so and hands the story to `ci-chaos-lane-serialised-confirm-no-coresidency.md`, which master added to own it. MASTER WIDENED "NEVER WRITE DOWN WHAT A COMMAND CAN ANSWER" TO NAME COUNTS EXPLICITLY (docs/inflight/AGENTS.md), and this branch's notes were carrying three that this merge made wrong: a `416 files, 0 violations` verification, `AGENTS.md ... it is now 531` (551 now), and a block of diffstats against a merge base two merges old. Each is replaced by the shape plus the command that reproduces it. The historical figures stay - what the Java-only gate used to report, and the 41 files the widening found - because no command yields those and they are what motivate the work. ONE LEFTOVER GOT MORE ACCURATE RATHER THAN LESS. The note's "a glob narrower than the claim" class cited `bin/check-shell-sigpipe.sh` globbing one directory level; master extracted that resolution into `bin/lib/shell-corpus.sh` and gave it to `bin/check-shell-hazards.sh` as well, so the gap doubled its reach instead of closing. Master's new ShellCheck lane looks like a fourth instance and is not: `bin/check-shell-lint.sh` describes the same corpus in the same words but resolves it with `git ls-files 'bin/*.sh'`, and a git pathspec `*` crosses `/`, so it does scan `bin/lib/`. Two gates whose headers claim identical scope scan different sets, which is worth knowing before anyone "fixes" one of them. VERIFIED ON THE MERGED TREE. `bin/check-all.sh --with-tests` - 36 passed, 0 failed. ShellCheck at the lane's `error` floor over all 74 corpus scripts: clean. At `warning` over this PR's own shell files: one SC2209 on a line this branch wrote (`STYLE=hash` reads as the shell builtin), quoted here; the remaining SC2034 is in `scripts/upstream-sweep.sh`, pre-existing and outside the gated corpus. `bin/check-branch-self-reference.sh` run the way CI runs it, with PR_NUMBER set, rather than bare where it exits 0 announcing it checked nothing. `check-shell-lint.sh` reports CANNOT RUN rather than pass here - shellcheck is not installed on this box and toolchains are managed elsewhere. The ShellCheck figures above come from the already-cached `koalaman/shellcheck:stable` image at the same severity and over the same file list. THE SHARED CLONE WAS RE-SHALLOWED MID-MERGE by a sibling agent's depth-limited fetch, and `git merge-base` began returning nothing while every gate that depends on history quietly reported CANNOT RUN. Repaired with `git fetch --unshallow` and every check re-run on full history; the figures above are the post-repair ones. This is the case `.claude/hooks/check-shallow-history.sh` was added for on 3d51667 - the `shallow` file lives in the shared git-common-dir, so one sibling re-shallows every worktree at once. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Found while merging master into this branch: the shared repository went shallow three times in one
session, `git merge-base` started returning nothing, and gates that need history degraded to CANNOT
RUN. It was not a sibling agent's stray fetch, which is what it looked like. It reproduces on
demand:
git fetch --unshallow origin
git rev-parse --is-shallow-repository # false
bash bin/check-quarantine-owners.sh # exits 0, silent about this
git rev-parse --is-shallow-repository # true
The gate reaches an owner PR's merge preview with `git fetch --quiet --depth=1 origin "$base"` and
the same for `pull/$pr/merge`. A depth-limited fetch writes the `shallow` file, and that file lives
in the shared --git-common-dir, so one run truncates history for every worktree of the clone rather
than for the caller alone.
WHY THIS IS WORSE THAN ITS SIZE. `bin/check-all.sh` globs `check-*.sh`, so the pre-review sweep and
the pre-push hook both run it: the instruction to run all the gates is also an instruction to
corrupt the clone, and the sweep's second half is then weakened by its first. The damage lands on
OTHER commands - empty merge bases, ahead-counts in the hundreds, a commit that plainly landed
reporting "not an ancestor of master" - each of which reads as a repository catastrophe rather than
as objects that were never fetched. `.claude/hooks/check-shallow-history.sh` denies the queries
while shallow, which is the right guard and does nothing about the thing doing the shallowing.
NOT FIXED HERE, DELIBERATELY. The preview check is what closes the quarantine loop and is worth
keeping; the change is to stop shallow-fetching into the working repository, which is a behaviour
change to a gate this PR has no other business in. Recorded with the reproduction, and with the
invariant a fix should assert rather than describe: unshallow, run the gate, fail if the depth
flipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Agent-written comment (posted through Antony's
|
…is fixed on master #365 landed the fix (`3a98b8d6d`), so this note now describes a defect that no longer exists. `docs/inflight/AGENTS.md` is explicit that the directory tracks only what is currently OPEN and that a closed item is deleted rather than rewritten into a FIXED narrative - and leaving it would have been worse than untidy, because a note asserting the repo's own gate sweep corrupts the clone is exactly the kind a future session acts on. Nothing is lost. The note's own "what a fix has to preserve" section proposed fetching into a scratch git dir, which is what #365 implemented, and the transferable half - the class, the sweep's reasons, and the "none found" list with where it looked - is now docs/solutions/workflow-issues/a-gate-truncated-the-shared-clone-2026-08-26.md, which arrived on this branch with the master merge below. Verified nothing in the tree cited the deleted path.
Master had moved 6 commits past this branch's merge base (5b73af7). Two textual conflicts, both in workflow files this branch had only added a header to, so both resolved by taking master's content and keeping the header: .github/workflows/mutation-full-sweep.yml - content conflict on the very first lines. Master's da8b009 rewrote the file's opening description (manual-only -> nightly plus dispatch) on the same lines this branch had pushed down by inserting the copyright header. Resolved as the header plus master's new three-line description verbatim; nothing of either side is dropped, because the branch's edit to this file was only ever the header. .github/workflows/pr-highcpu-fast-feedback.yml - modify/delete. Master's 025d0b7 deleted the file when it took everything per-PR off the self-hosted box; this branch had added the copyright header to it. Took master's delete. A header on a file that no longer exists carries no intent, so there is no both-sides case here. Six files auto-merged with both sides intact and were checked rather than trusted: .github/actionlint.yaml, .github/workflows/chaos-pain.yml, .github/workflows/cancel-closed-pr-runs.yml, .github/workflows/repo-hygiene.yml (master collapsed nine per-gate jobs into one check-all run - kept), AGENTS.md (master's "write prose to a FILE" rule and this branch's squash-message correction both present), and pom.xml (the Modifications line sits under Confluent's, unchanged). Master added no file that the widened copyright gate now fails: 447 enforced, 0 violations, 169 non-Java - identical to the pre-merge baseline, because master's two new files are prose (docs/inflight/ci-bsd-portability-gaps.md) and an edit to an already-headered workflow. The settled policy in docs/copyright.md, "Why a header at all, and why prose does not get one", is untouched and was not reopened. One semantic conflict that no textual conflict marked, caught by the pre-commit check-file-refs gate: this branch's 2026-08-25 re-check entry in docs/inflight/ci-disabled-jobs-and-runner-load.md handed the co-residency question to ci-chaos-lane-serialised-confirm-no-coresidency.md, and master's 025d0b7 deleted that note as answered. Repaired by saying what actually settled it rather than by silencing the gate: the re-keyed concurrency group had a failure of its own (26 of 32 box jobs evicted while pending), the box bought 14% of wall-clock, and everything per-PR came off it. Co-residency is now not merely unlikely but unavailable as an explanation for a chaos red on a PR, which is a stronger statement than the one it replaces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hrase-and-master-drift One conflict, in `ci-disabled-jobs-and-runner-load.md`, where both sides append. Kept both: master's re-check narrows the co-residency argument to one branch and a docs-only delta and records that everything per-PR is now off the self-hosted box, and this branch's entry records a red that was FILED here and did not belong, because a truncated log made a running job look like a killed one. Master's continues the entry's chronology, so it goes first and the correction follows it. Takes #338, which extends the copyright gate past Java to every file type - the sweep that made this branch conflict at all, since it touched headers across the tree.
…r self-tested The registration check this branch added reports every hook `settings.json` registers that nothing under `bin/` references, and `remind-master-drift-on-push.sh` was the first thing it caught - 205 lines, landed by #357, with no coverage of its own. #357 did test the SHARED push detection thoroughly, through the sibling reminder's fixture and with a negative control, so what was missing was this hook's own contract; master's copy of this suite even cites "the drift section" that was never added. Fixed here rather than exempted, because a gate with a recorded hole is a gate nobody trusts, and this is the same trade the copyright gate took in #338: widen the check and fix what it finds. Twelve cases, covering what the hook promises: it names the SUBJECTS of the commits the base ref gained (the reason it exists at all - a divergence count is what `rev-list --left-right --count` already answers), it names the files changed on both sides while not reporting base-only files as overlap, it throttles on the base SHA so one tip is reported once, it reports again the moment the base moves, it counts UNCOMMITTED work as this branch's side of the overlap, it takes the other arm of the report when nothing overlaps, and it stays silent when there is nothing to inherit - level with the base, pushing the base branch itself, or a command that is not a push. Offline by construction: the hook only fetches when `MASTER_DRIFT_REF` contains a slash, so a local branch stands in for `origin/master` and no case touches the network. Two things this suite caught in its own cases, both worth stating because they are the failure mode this file exists to prevent. The level-with-base case first PASSED while the fixture sat in a broken merge state, so it was asserting nothing; the catch-up is now a `reset --hard`, since both sides had appended to the same file and the merge conflicted. And every silence case now gets a FRESH stamp directory: by that point the stamp already holds the current base SHA, so the throttle alone would explain the silence, and a case that passes whether or not the exit it names exists is decorative. Mutation-checked rather than assumed. Dropping the SHA throttle, reporting base-only files as overlap, reading `HEAD` instead of the working tree, and dropping the behind-count guard each fail exactly the case that names it; the hook was restored byte-identical afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5LgTetnPkQciHFNaQWwSN
) A fan-out of eleven per-language demo agents took the host volume from ample to 8.8 GiB free of 926 GiB in about an hour, and took the Docker VM's virtual disk with it. One agent's build died outright; two others pruned under each other. Nothing warned, because the session had started with plenty of room - and a SessionStart check would have reported all clear for exactly that reason. The only instant that can see what the last command left behind is just before the next one, so this is a PreToolUse hook on Bash, and it never blocks. No `if` filter, for the same reason the squash guard has none: `Bash(docker *)` would miss `cd demo && docker compose up` and every wrapper script, which is most of how containers actually get built here. It earns the right to run on every call by costing two stat-class syscalls with no docker CLI on the fast path, and by saying nothing until a threshold trips. WHO THE WARNING IS FOR DECIDED THE DESIGN. The throttle stamp is keyed per SESSION, not per Unix user. One stamp per UID meant the first agent to notice silenced every other concurrent session for the window - in the incident above, ten of eleven could not have been told while they were the ten still filling the disk. Warning everybody trades that for a stampede, which the same incident shows is real: two agents pruned under each other. So every session is warned and the message carries the safety - each agent is told to report the situation and suggest a reclaim, never to run one, so the OPERATOR is the gate against duplicate effort rather than the throttle. An operator can tell duplicate cleanups apart; a ten-minute timer cannot. Keying is best-effort: session_id is read past every fast-path exit, with no python3 on that path, and an absent or unparseable payload falls back to the shared stamp - the old behaviour - because the failure this file refuses is "warned" becoming "silent". The value reaches a file path, so it is filtered with `tr -cd` rather than escaped. The stamp is written AFTER the message, so a kill between the two cannot swallow a warning for the whole window - likeliest exactly when the disk is full, which is this hook's subject. THE SELF-TEST IS THE INTERESTING PART, because a warn-only hook's correct behaviour on a healthy machine is to print nothing, which is byte-identical to being broken, unregistered or absent. So almost every case forces it to speak, and the one case asserting silence is paired with forced cases proving the same path can talk. Every case pins all four thresholds: an earlier version inherited them and three cases flipped to failing mid-session when the machine dropped below the default warn line - a self-test for a disk warner must not be a function of the disk. Review hardened this substantially and the fixes are folded into the feature commit, so no point in the history carries the vulnerable version for a bisect to land on: a crafted throttle stamp ran a command as the user through bash arithmetic resolving a non-numeric operand as a variable name (demonstrated, not theorised); "never exits non-zero" was false for any non-numeric first token; `mkdir -p` succeeds against a directory another user owns, so the state dir was never checked and a symlinked stamp gave arbitrary file truncation; a device name containing a space - macOS autofs `map auto_home` - shifted df's columns so Used was reported as free, 94 GiB "free" on a disk with 1; a fully pruned Docker could never clear its own alarm; a torn cache read undercounted; a future-dated stamp silenced the warner until the clock caught up. Twelve mutants injected, the hook restored byte-identical each time, every fix killed by exactly the case that should kill it. One new case was itself decorative and had to be repaired - it passed with every guard deleted. TWO THINGS THIS BRANCH FOUND IN OTHER PEOPLE'S CODE, both because a check it adds went red rather than because anyone looked. The registration assertion - every registered hook is self-tested, and vice versa - caught `remind-master-drift-on-push.sh`, 205 lines with no coverage of its own; master's copy of the suite even cited "the drift section" that was never added. Fixed rather than exempted, the trade #338 took: widen the check and fix what it finds. Twelve cases, each mutation-checked. And the count sentence in docs/agent-harness.md is now pinned by BOTH its numbers, after master reworded it to distinguish hook scripts from registrations while this branch's check still pinned the older single count - so merging the two went red on the doc rather than on either change, which is the check working. The new test section then caused a CI-only failure worth recording rather than just fixing: it ended by returning to the repo root, and the section after it reaches the push fixture's scratch repo by INHERITING the cwd, because its enriched-refusal cases need a findable PR, which needs a named branch. A local checkout is on a branch, so it passed; every runner's checkout is detached, so it failed. It now restores whatever cwd it found. Merging master also parsed this suite with bash 3.2 for the first time, via the new `shell: macos` lane, and it went red on this branch's own code: a python heredoc inside a command substitution, which 3.2 scans as shell text without honouring `#` comments, so an apostrophe in a comment unbalanced the quotes and it read to end-of-file. It presented as exit 2 - CANNOT RUN - after sixty cases had passed: a gate that could not measure, wearing the message of a missing tool. Fixed by moving the heredoc to a file so the body is never scanned as shell, rather than by balancing the quotes and leaving the next author one apostrophe away. Verification with a control: 173 passing cases to 208 on this branch, both green at 0 failures, measured serially here and on a detached master worktree. An earlier description of this work claimed 14 failures matching pristine master; that was wrong, and the way it was wrong is the point - both sides had been measured while other work ran on the same box, so two contaminated runs were being compared to each other. Known open, tracked in docs/inflight/ci-low-disk-warner-open-items.md: the Docker Desktop sparse-image path still has not run against BSD userland (the macOS lane reaches the `stat -f` arm and the df column read, but a hosted runner has no Docker.raw), `df` on the fast path has no timeout, Linux docker_root is hardcoded past daemon.json's data-root, and silencing is only possible through variables documented as test-only.
…s engine back Merges `feats/proxy-dispatch-clients` (#390, the top of the Wagon A stack) into this branch, so that #293 can be retargeted onto that rung and its displayed diff collapse to the engine residue. This is the plan's "retarget move" - `docs/plans/2026-08-31-001-process-god-branch-decomposition-plan.md` - and it is an ordinary merge: no history was rewritten and nothing was force-pushed. The pre-merge tip is preserved as `origin/backup/pre-stack-merge-293`. The merge also brings `master` forward, because the stack is current with it and this branch was not. That is most of the volume and most of the conflicts. ## Which side won, and why The rule was: the god branch's SEMANTIC content, expressed on the stack's refined STRUCTURE. - **Engine-only files stayed** - `ProxyProcessor`, `ConfigureHandler`, the dispatch waves, epochs, leases, reconnect and the produce path. They are the residue this campaign exists to expose. - **Stack-only files were taken** - the ten foreign clients, the runner registry, the sidecar shim, the self-retiring guards, `Main` and `ParentDeathWatchdog`. - **Records lost, everywhere.** The stack converted five value types from Java records to plain final classes because Jabel rewrites a record into a class with no source positions and Error Prone 2.42.0 crashes rather than reporting on it. This branch never met that only because its root pom had no Error Prone; master's does now, so the conversion is load-bearing here. - **Dependency pins are the stack's**: protobuf 3.25.8 (`requireUpperBoundDeps` refuses 3.25.5 against grpc-protobuf's transitive ask), gson 2.14.0, `error_prone_annotations` 2.48.0 in the proxy and the Java client aggregator and 2.47.0 in the protocol module - deliberately unequal, because their highest requesters differ. - **`clients.yml` is this branch's**, not the stack's. The stack's copy is a smaller earlier file whose own header says it has no conformance lane, no per-language static analysis and no dependency audit; only its `--component clippy` fix and its workflow-level `PC_FOREIGN_CLIENTS_STRICT` were folded in. - **Master's copy won for everything master owns** - the gate scripts, the copyright checker and its self-test, `repo-hygiene.yml`, `docs/ci.md`, `docs/copyright.md`. This branch's only delta in most of those was a copyright header in a pre-canonical form, written before #338 landed the canonical one on master. - **The two ledgers were merged as unions**, not resolved to a side. `docs/inflight/bug-857-family.md` keeps both sides' sightings, this branch's renumbered per the file's own stated convention; `docs/quarantined-tests.md` drops three entries whose owning PRs retired the annotation on master, because a registry entry without a `@Quarantined` test is a hard gate failure. ## The verdict-free work return, re-expressed on master's shape Unit U4 is still on this branch as `WorkManager.onAbandonedResult` and its call sites, and `WorkManager.java` auto-merged keeping them - so resolving `WorkContainer`, `ProcessingShard` and `ShardManager` to master alone would have compiled to nothing. Master moved underneath it: #335 collapsed `inFlight` and `maybeUserFunctionSucceeded` into one atomic `ExecutionState`, and #336 and #373 replaced the shard's raw `availableWorkContainerCnt` with a compare-and-set claim. `ProcessingShard.onAbandoned` and `ShardManager.onAbandoned` are therefore taken verbatim from `feats/proxy-verdict-free-return` (#295), which is cut on current master and is the authoritative re-expression - it routes the restore through `includeInSelection` rather than incrementing a counter that no longer exists. `WorkContainer` is NOT #295's, and the difference is deliberate: that branch marks abandonment with an `AtomicBoolean` cleared by the claim winner, while this branch keys it by delivery (`markAbandoned(long)`, `isAbandonedForCurrentDelivery`, `isReturnForSupersededDelivery`). The delivery-keyed form is strictly more: it tells a late return for delivery n from a live return for delivery n+1, which the boolean cannot, and acting on that confusion ends a running flight and decrements `numberRecordsOutForProcessing` twice. It also needs no clearing on redelivery, so it does not race the claim. Master's own `deliveryCount` - already an `AtomicLong` incremented only by a won claim - supplies the identity, so the field is the one thing added. ## The reconciliation duties #387 and #390 recorded Both are discharged, and the guards they left behind now pass because the gap closed rather than because anything was edited. **`ProxyHarness` and `ConformanceHarness` are one class again.** #387 cut the latter out of the former with the engine lane removed, and wrote into the class header, the module pom and a deferred inflight note that whoever landed the engine must merge them - leaving open which module it ends up in, on the grounds that "the engine lane decides". It decides the sidecar module: the lane needs `ConfigureHandler` and `ProxyProcessor`, and `TestModeMain` - the process a spawned foreign runner actually starts - is in that same test tree and would otherwise have to import the conformance module and close a cycle. So the NAME is the extracted rung's and the MODULE is this branch's, and `parallel-consumer-proxy-conformance` reaches it through the proxy test-jar its pom already declared. The stack's refinements to the class survive: `Delivery` stays a plain final class, and the `startEmbeddedClient` lane stays beside the restored `startEngine`. **`ConformanceDriver.spawnAgainst` is back**, with `LanguageRunner` a `ConformanceBinding` again - it stopped being one on #390 for exactly as long as there was no engine to spawn against - and the ten foreign bindings are registered in `ConformanceBindings.selectable()`. `ConformanceBindings.deferredUntilTheEngineArrives()` is kept and now returns an empty list, which is the honest reading: nothing is deferred. It is not deleted because `TheEngineArrivingMustBringTheForeignCellsTest` reads it, and because the next capability to arrive without a cell needs that list rather than a new one. ## Two duplications the merge surfaced, resolved rather than left - **The protocol documents existed twice.** #383 moved `protocol-specification.md` and `client-authoring-guide.md` into `parallel-consumer-proxy-protocol/docs/` and predicted the rename conflict here. The protocol module wins on the merits - all three artifacts are the contract rather than the server - but the CONTENT is this branch's, because the stack's copies carry "no client generates from this schema yet" and a `git show` fallback for a Go generator script that exists here. `SpecificationCoverageTest` moved with them; every citation was re-pointed. - **`HarnessScenario` existed twice**, once per harness. The stack's plain-class version won, in the sidecar module beside the class it serves. ## Five Java records had to become plain final classes, or nothing compiled `master` gained Error Prone after this branch forked, and Error Prone 2.42.0 does not report on a Jabel-desugared record - it crashes, failing the whole compilation with a message attributed to line 1 of an unrelated file. #387 hit this one rung down and converted its five value types; the same thing was waiting here for `ProxyProcessor.ManifestOutcome`, `ManifestReconciler.Reconciliation`, `InFlightRegistry.InFlight`, `LivenessSettings` and `TestModeMainTest.Run`. Neither term can move - the root pom pins Error Prone at 2.42.0 because 2.43.0 needs a JVM this build cannot use, and Jabel serves the release 8 target - so the records lost, which is what the rest of the repository already does. `grep -rn "record "` now finds none. ## Two defects this merge introduced and then fixed - **The default lane ran a test that needs a profile.** Keeping BOTH sidecar-spawning tests - this branch's engine-backed `OneRecordThroughTheSidecarTest` and the stack's engine-less `SidecarHandshakeTest` - left the Kotlin and Scala exclusion property naming only the first, so the second ran in an ordinary build with no `sidecar-classpath.txt` and failed. Both are now behind one regex, which is also the arrangement that makes the pairing legible. - **Every proxy module's data record existed twice.** This branch keeps module-maturity and testing-evidence records in per-module `docs/data/*.d/` fragments; the stack, which never imported that mechanism, wrote them into the monolithic files. The merge kept both and `bin/check-docs-data.sh` went from green to 37 structural problems. The fragments win, and deleting the monolithic copies also deleted eleven rows asserting "NO RECORD HAS CROSSED THE WIRE" - true on a stack whose sidecar hosts no engine, false here. ## How it was verified JDK 17, macOS. Full default reactor `./mvnw --fail-at-end test`: **BUILD SUCCESS**, every module green including the conformance suite - so the `java-grpc` cell runs against a live engine for the first time, and `GrpcSpikeConformanceTest` answers all five scenarios over a real gRPC stream. The four self-retiring guards #387 and #390 left behind all pass, and **none of them was edited**: `TheEngineArrivingMustBringTheGrpcBindingTest`, `TheEngineArrivingMustBringTheGrpcCellTest`, `TheEngineArrivingMustBringTheForeignCellsTest` (which asserts about every registered language, not one) and `SelectorMatchingNothingFailsTest`. `bin/check-all.sh` real exit code 1, with three gates failing and all three inherited, established against the pre-merge tip rather than assumed: `check-inflight-tags` reports the same 53 problems before and after; `check-file-refs` was already red for the `@`-prefixed bridge imports that #378 fixes; and `check-branch-self-reference` did not exist on this branch before the merge - its findings are on notes inherited from `master` byte-identical. `check-proto-lint` and `check-proto-breaking` report CANNOT RUN for want of `buf`. **Not run locally: `-Dpc.foreignClients`.** It builds C++ and Swift inside containers, and this box is under a low-disk warning that names a container build as what would tip it over. CI's per-language rows are where that lane runs. ## Two findings recorded rather than fixed - `ParallelEoSStreamProcessorTest.processInKeyOrder` failed its own preamble sanity check once here - and the control arm reddened `master` HARDER: unmodified `master`, uncontended, failed all three parameterisations where this branch under concurrent load failed one. That refutes the existing ledger's "the base branch was green" row, and is the first evidence the flake is master-state. Recorded in `docs/inflight/test-processinkeyorder-sanity-check-races-the-first-poll.md`. - One `java-grpc` conformance run left its last record unsettled, 1 of 2 full-reactor runs and green 3 of 3 in isolation. `core` and `java-direct` were green in the same run, so the suspect is the engine's settle path under a full ceiling rather than the scenario. The scenario was NOT weakened - `docs/inflight/proxy-a-java-grpc-ceiling-run-left-its-last-record-unsettled.md`. ## What is deliberately NOT done here `Main#sessionServiceFactory` still returns `NoEngineSessionService`, so the production entry point hosts no engine. That is unit U10's, which #384 named as still having no PR, and it owns the drain that must land with the engine - plus eight cross-language handshake tests assert the `UNIMPLEMENTED` refusal by status code specifically. Recorded rather than faked, in `docs/inflight/proxy-the-production-entry-point-still-hosts-no-engine.md`. The engine is exercised end to end through `TestModeMain` regardless.
Independent of any feature work: this fixes a gap in the copyright gate itself. It serves no single issue, so it cites none.
Description
The gate has only ever looked at Java. It reported
255 java files, 0 violations, which reads as a clean tree - but it was a clean tree of one file type out of a dozen. Workflows, poms, shell, JavaScript, Python and XML have been outside its view since the fork, so a file could land with no notice at all and the check would still go green.Widening it to the whole tree turned up 41 files that should have carried a notice and did not. They are fixed here rather than exempted. Merging master has moved the count twice since, and both moves are the gate working rather than noise: it gained
config/spotbugs-exclude.xmlandconfig/infer-known-findings.txt- landed by other work, judged for the first time here - taking it to 43; then master's025d0b7eadeleted.github/workflows/pr-highcpu-fast-feedback.ymloutright when it took everything per-PR off the self-hosted box, so one of the 41 no longer exists and the count is now 42. That the number moves at all is the point: this is the first change that makes such a file impossible to land unnoticed.Two different fixes, because the policy distinguishes two kinds of file
Modifications Copyrightline added to the Confluent notice they already carry. That is what Apache 2.0 §4(b) and §4(c) ask for, and the convention Corretto and MariaDB use.Eleven of the 42 are the second kind, including the root
pom.xml. Giving them a fork header would claim original authorship of a derived file, so getting this distinction wrong is worse than leaving them alone. The first pass of this change did exactly that and was corrected.Breakdown of the 42: 20 hash-comment (15
.yml, 2.yaml, 2.sh, 1.py), 13 XML, 8 JavaScript, 1.txt— that last one isconfig/infer-known-findings.txt, classified by basename rather than by adding a*.txtrow, because.txtin general has no comment syntax and a blanket row would be wrong about the first real text file to arrive.How the scanner works
It is driven by a file-type table mapping a glob to a comment style, so adding a language later is one line rather than new logic. An extensionless file that opens with a shebang names its own language and needs no entry.
The table carries entries for languages this repository does not have yet - Go, Rust, Swift, Kotlin, C. They cost nothing while unused, and they mean the first such file to arrive is covered rather than silently skipped, which is precisely the failure this change exists to end.
Where it came from
Extracted from the language-proxy stack, where the wider scanner was written because that work introduces files in nine non-JVM languages. It is included here on its own because the gap it closes exists on
masterright now and should not wait behind a 115k-line feature branch. Nothing in this PR depends on that work.What review found: the widened gate was still green while not doing its job
Five ways a file could pass that should have failed. Each was reproduced on a fixture, and each fix has a red arm verified against the pre-fix scanner:
@author Antony Stubbs and contributors, with noCopyright (C)anywhere, passed green in any of the 45 languages. The tell is that renaming the holder makes it go red.hash-only, so a.mjsCLI carrying its notice above#!/usr/bin/env nodepassed while an identical.shfailed.git ls-filesC-quotes non-ASCII paths, soCafé.javaand an unclassifiablemystère.zigboth vanished.--reportshowed 2 rows for 4 files, breaking the every-file-is-classified invariant the scanner rests on.Plus one in the self-test itself, which died silently when its own parser broke - 29 lines and no summary.
Proof the gate scores non-Java rather than matching an empty glob: 447 files enforced, 0 violations, of which 169 are non-Java - 70
.sh, 46.yaml, 23.xml, 18.yml, 8.js, 1.py, 1.txt, 1.cmd, 1 extensionless-with-shebang. (It read 409/150 at the first master merge; the rise is master's own files entering the gate's view. The totals held across the second merge while the mix moved - master added.claude/hooks/check-shallow-history.shand deleted a workflow - which is the gate tracking the tree rather than a fixed list.) Positive control: headerless probe.ymland.pyfiles make this gate exit 1, while the merge-base gate reportsChecked 255 java files ... 0 violationsand exits 0.--reportclassifies 636 of 636 tracked files with none unclassified.Merge blast radius: ten open PRs sampled; every genuinely new enforced-type non-Java file already carries a notice, so merging this turns none of them red. PR #334's four protoc
_pb2*.pyfiles are correctly exempt, verified by running the gate on their real paths in a throwaway repo.Also in this PR, and not about copyright
AGENTS.mdgains a PR-discipline rule - "A rung in a stack has to earn its PR" - drawn from #332, where a branch and draft PR carrying a single 118-line design note gated two PRs of shipped, tested code behind the dependency rule. It rides along here rather than in its own PR precisely because that is what the rule asks for.Decided here: why anything gets a header, and why prose does not
The type table exempted
*.md/*.adoc/*.htmlbecause "the notice would render into the document". That is false - all three have non-rendering comment syntax, and this repository already depends on it, since everydocs/inflight/note carriesinflight-typemarkers no reader ever sees. A false reason is worse than none, because the obvious way to act on it is to header a hundred documents.The exemption was right; the reasoning is now the real one, and
docs/copyright.mdcarries the argument for the whole table.A notice does not create protection - copyright subsists automatically under Berne and has needed no notice in the US since 1989, so prose is protected either way. What a notice does is reach a reader who has the file and not the repository: carry the terms, make the file legible to a licence scanner (no header reads as unknown licence, which in corporate ingestion often means cannot use), defeat an innocent-infringement defence, and for upstream-derived files discharge Apache-2.0 4(b).
So the test is whether a file can arrive detached from
LICENSE. Source jars are published, so.javadoes; the poms travel beside the jar, which makes the rootpom.xml'sModifications Copyrightline the most load-bearing line this gate enforces. Prose cannot - no.mdor.adocsits undersrc/main, nothing sweeps docs into an artefact, andREADME.adocis generated at build time and lives at the repository root.Everything else that can hold a comment stays uniformly headered, ships or not. That is deliberately over-inclusive for a mechanical reason rather than a legal one: does this file travel? is a per-file judgement, easy to get wrong, and silent when wrong. A type-keyed rule is one a scanner can enforce and a reviewer can check.
Known open, tracked in
docs/inflight/ci-copyright-gate-review-leftovers.md.claude/*is exempt, which exempts seven hand-written hook scripts that already carry correct headers. Narrowing it is verified green (416 files, 0 violations); whether unshipped agent tooling owes a notice is a policy call.bin/check-shell-sigpipe.sh(bin/*.shand.claude/hooks/*.sh, one directory level each), and.github/scripts/file-ref-gate.js(four prose extensions only, which is why a dangling path inside a.shwas invisible - and it already grew once, for.html). Three instances wants adocs/solutions/entry rather than a third one-off fix.Checklist
docs/copyright.mdgains the policy for the two file kinds and the type tabledocs/features/-N/A - a repository gate, not a product surface; it changes nothing a user of the library can observe.ce-simplifyandce-code-reviewlocally -N/A - asked for @claude review on the PR instead, five times, each pass re-run against the new head; that is the route whose inline threads mechanically block this merge. This box was added to the template by master's f0472531f after the PR was opened.