Skip to content

fix(provenance): discharge the rubric-v3 re-score, and the answer key it found in the rubric - #3472

Merged
kyle-sexton merged 10 commits into
mainfrom
claude/detect-copied-external-content-k5aw77
Aug 28, 2026
Merged

fix(provenance): discharge the rubric-v3 re-score, and the answer key it found in the rubric#3472
kyle-sexton merged 10 commits into
mainfrom
claude/detect-copied-external-content-k5aw77

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

No linked issue

Summary

Rubric v3 shipped in #3471 with an obligation attached: its own rule blocks every precision figure, and with it every class's fix eligibility, until the golden set is re-scored against v3. That re-score is done here. It reproduces the recorded table exactly — and the run that produced it found that the rubric carries its own answer key, which is the more useful result.

Also closes the two design gaps the report-only sweep recorded on #3465, and fixes a false positive in the stamp detector that two independent sweep legs found.

Fix

The v3 re-score: 8 tp / 0 fp / 0 fn / 2 tn, no verdict moved

Ten cases, three judges each, thirty judges in independent OS processes — not sequential passes in one context. Cases relabelled so no directory name or path reached a judge. Each saw the candidate passage, the fetched source, the whole containing file and the rubric, per the v3 dispatch; none saw expected.json, the fingerprint figures, or another judge's verdict. Every panel unanimous.

c04 is the only case whose attribution reaches grading, so it is the only one C3's stated scope could have moved. All three judges took the new test where v2 left it: the derivation is one lift inside otherwise-original material, so a See also bullet two sections below understates its scope and C3 passes.

No class becomes fix-eligible, for the reason that was already there. Every class measures 1.00 against the 0.95 bar and every class sits below min_n_per_class 10 (n = 2, 5, 1, 2). The re-score lifts the rubric-version block; the class-size block stands, and is what keeps the #3465 sweep report-only.

The rubric carried its own answer key

In #3471 I added a paragraph to rubric.md recording the expected tally, the panel size, and an enumeration of which golden case turns on which criterion — including the one case the scope change exists to restate. The pipeline inlines the whole rubric into every judge prompt at the judgment step. All thirty judges read the prediction before grading.

I wrote it so the figures would not sit under a cloud they did not deserve. Putting it in the file judges read is what made a blind measurement against that rubric impossible. Found by the re-score's own fresh-context verifier, which returned FAIL on the method while confirming the arithmetic; the run withdrew its blindness claim rather than defending it.

The verdict was tested against the leak rather than assumed safe. c04 was re-judged by a second three-judge panel against the same rubric with the preamble removed and every criterion, carve-out, scope sentence and worked example intact. All three returned STANDS on the same scope-mismatch reasoning. The tally stands; the blind-panel claim does not.

A second leak of the same kind sits in the c08/c09/c10 shared source.md, and is deliberately not fixed here: that line is inside the text the fingerprint compares, so removing it moves the containment figures those cases record. The fix and a re-score are one atomic change; splitting them would ship a measurement that no longer reproduces from its own fixtures.

The modal "may" is no longer read as a month name

may is a month and an ordinary English modal, and both detectors matched it bare, so "the first read may raise a permission prompt" became a stamp candidate with an unparseable date and landed in the declined bucket — indistinguishable, to whoever adjudicates that bucket, from a real stamp the parser failed on. 17 of the 22 month-name declines carried the word.

may now needs a digit beside it. Three sites, not two: both detectors and the classifier in check-stamps.sh, which a single-site fix would have missed. The other eleven months still match bare, because over-reporting into a bucket a human reads is the safe direction.

Over 1,352 files: declines 45 → 28, month-name 22 → 5. parsed unchanged at 499 and findings unchanged at 0 — the load-bearing numbers, since they say no real stamp was reclassified and none had been masked.

Two design gaps from the sweep

A vendored-snapshot basis now has a rule: caps at source-fetched-similar, records source.route with each failed live fetch, and is never fix-eligible — fix eligibility rests on current upstream state, which a snapshot cannot establish. The searched-surfaces listing is recorded as prose-only and unenforced, because emit-findings.sh has no field for it; a run's listing is that run's own claim, never validation evidence.

Verification

Four independent fresh-context verifiers, none judging its own work. Two returned FAIL and both were right:

Red-first reproduced verbatim (56/3 and 47/3 against pre-fix scripts, 59/0 and 50/0 after), test diffs additive-only (73 insertions, 0 deletions), May 2026 still detected in both forms in both scripts, mawk portability confirmed, shellcheck clean repo-wide.

Limits recorded rather than left to be discovered. The re-score's panel was not blind, for the reason above. Four case bodies state their own intended answer and v3 requires judges to read the whole file. c07's expected.json describes two mutually exclusive routes; all three judges took the carve-out, which the rubric's order of evaluation requires, and neither the fixture nor the rubric was edited to match the run. A latent window-slack edge exists for the unbounded may rule and is documented at the rule rather than fixed, since widening the slack would move a boundary two earlier commits tuned.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A


Generated by Claude Code

claude added 5 commits August 28, 2026 11:52
…es listing as unenforced

Two design gaps from the report-only sweep (#3465):

Gap 1: the evidence-tier table had no row for a comparison basis that is an
in-repo vendored snapshot (live fetch failed, only the committed copy under
vendor/ with a declared upstream ref and sync date was available). New section
in reference/source-fetch.md states the rule: the finding caps at
source-fetched-similar, is never fix-eligible, records
source.route: vendored-snapshot with the snapshot path, upstream ref, sync
date and each failed fetch, and the follow-up is human. SKILL.md step 9
carries the cap so tier mapping cannot miss it. No existing tier row in
reference/rubric.md was touched.

Gap 2: the not-found searched-surfaces listing is required by prose in three
places but scripts/emit-findings.sh has no field, count, or schema for it, and
the relay boundary withholds not-found findings from the findings file
entirely. Decision (b): record the limitation rather than assert a capability.
reference/source-fetch.md now states the listing is prose-only and never
validation evidence; SKILL.md's "does not" bullet says the same.

Also flagged in reference/dispositions.md, where a reader of the fix contract
sees it: a corpus file can be the rendered output of a generator whose source
of record lives outside the markdown corpus (the sweep found one at
docs/native-surfaces/records.json), so a disposition applied to the rendering
edits a never-hand-edit file. Flag only; no guard built.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
"may" is a month name and an ordinary English modal verb. Both stamp
detectors matched it bare, so prose like "the first read may raise a
permission prompt" became a stamp candidate whose date form could not be
parsed and landed in the declined bucket. A reader adjudicating that
bucket could not tell such a line from a real stamp we failed to parse:
17 of the 22 month-name declines in the 2026-08-28 corpus sweep carried
the word, and 13 of them were the modal sitting inside a keyword window.

"may" now needs a digit beside it before it counts as a date. Every date
form carries one ("may 2026", "may 17", "17 may") and the modal does not.
The tightening is deliberately narrow: the other eleven months still
match bare, because over-reporting into a bucket a human reads is the
safe direction and under-reporting is not.

Both scripts carry the month list on purpose, so both change together and
the suites pin the agreement directly: extract-breadcrumbs.test.sh runs
check-stamps.sh over the shared fixture and asserts the two candidate
counts match. A real "May 2026" stamp is asserted as still detected in
both suites, which is the regression this fix could plausibly cause.

Corpus at 1,352 files, --as-of 2026-08-28: candidates 544 -> 527,
declined 45 -> 28, month-name declines 22 -> 5, bare-year declines 23
unchanged, parsed 499 unchanged, findings 0 unchanged. Every one of the
17 dropped lines carried the modal; the 5 that remain are genuine
month-name stamps. The extract-breadcrumbs inventory moved by the same
17 lines (534 -> 517), so the two definitions stayed in step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
Composed centrally rather than by the workers that did the work: three
agents ran concurrently in one tree, and having each write its own entry is
how a sibling's commit got corrupted earlier in this branch's history.

Covers the vendored-snapshot tier rule (caps at source-fetched-similar,
records source.route, never fix-eligible), the modal-"may" false positive
(three sites, declines 45 to 28, parsed and findings both unchanged), the
searched-surfaces listing recorded as prose-only and unenforced, and the
generated-output warning in the fix contract.

Minor rather than patch: the tier contract gains a case it could not
previously express, which is additive guidance a consumer can rely on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
Version 3's version-history paragraph recorded the expected re-score tally,
the panel size, and an enumeration of which golden case turns on which
criterion, including the one case the scope change exists to restate. The
pipeline inlines the whole rubric into every judge prompt at the judgment
step, so all thirty judges in the version-3 re-score read the prediction
before grading, and that run had to withdraw its claim of a blind panel.

Found by that run's own fresh-context verifier, which returned FAIL on the
method while confirming the arithmetic.

I wrote that paragraph to keep the figures from sitting under a cloud they
did not deserve. Putting it in the file judges read at judgment time is what
made a blind measurement against the rubric impossible. The prediction and
the enumeration are changelog material and now live there. The rubric keeps
the criteria, the carve-outs, the scope rule, the worked examples and the
tier table, and now states that a judge should be able to read all of it and
still not know the answer.

Also records the re-score result (8 tp / 0 fp / 0 fn / 2 tn, no verdict
moved, no class fix-eligible because every class is below min_n_per_class),
the sensitivity panel that re-judged c04 without the leaking preamble and
returned the same verdict on the same reasoning, and a second leak of the
same kind in the c08/c09/c10 shared source.md. That one is deliberately not
fixed here: the line sits inside the text the fingerprint module compares,
so removing it moves the containment and span figures those cases record.
The fix and a re-score are one atomic change and splitting them would leave
a measurement that no longer reproduces from its own fixtures.

Refs #3465.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
…made stale

Both found by the fresh-context verifier on the "may" fix, which passed the
change itself.

The changelog justified deferring the underscore-boundary fix by citing a
`last_verified_2026_08` stamp that would be lost. No such token exists
anywhere in the repo. The verifier checked, found nothing, and supplied the
real line: plugins/work-items/skills/track/actions/add.md:104 carries
"last_checked": "2026-04-08", which parses today and would indeed be lost.
The conclusion held; the evidence for it was invented, and an invented
exhibit in an entry about provenance is the wrong defect to ship.

Also records what the verifier established and the deferral rationale had
not: a token-boundary fix repairs exactly 1 of the 23 bare-year declines,
because the rest carry years that are already token-boundaried. That is the
real argument for deferring, and it is stronger than the one given.

The window-slack comments in both scripts justified their 9 characters as
"one short of the longest form matched below". That stopped being true when
the "may" rule landed: unlike every other form it is unbounded in length, so
a contrived "may" plus 10 or more non-letters plus a year, starting at
exactly wlen, runs past the slack and stops being a candidate. Latent, not
live: no such line exists in the corpus, parsed and findings both stayed
flat, and the ordinary "may 2026" at the same offset is still caught.
Widening the slack would move the RSTART <= wlen boundary two earlier
commits tuned, for a form nobody writes. Both comments now say
fixed-length, and the rule carries the edge and the reason it is left.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 2m 56s —— View job


Code review complete

  • Gather context (PR body, changed files)
  • Invoke /review:code-review per custom instructions
  • Post inline comments for any findings anchored to changed lines
  • Post final summary

Scope: reviewed the full diff (git diff origin/main...HEAD), 10 files in plugins/provenance/. Security findings are scoped out of this lane (repo has a dedicated claude-security-review.yml).

Finding posted inline

One high-signal correctness issue, posted as an inline comment on plugins/provenance/skills/audit/scripts/check-stamps.sh#L307:

  • Contradicting corpus figures for the same measurement. The code comment in check-stamps.sh:307 and the matching comment in check-stamps.test.sh:217 both say "13 of the 22 month-name declines" carried the modal "may", but CHANGELOG.md:91 and the PR description both say "17 of the 22". Same corpus, same --as-of date, two different numbers depending on which file is read. This lane can't execute the shell script to determine which figure is the real one (no shell execution in this lane's tool scope), so I'm flagging the inconsistency itself rather than asserting which side is wrong. Notable because this PR's own verifier caught a similar unverified-figure problem elsewhere in the same diff (the invented last_verified_2026_08 exhibit) — this looks like the same class of defect surviving in one more spot.

What I checked and found sound

  • rubric.md: the leaked answer-key paragraph is removed and replaced with a rule stating the file must stay criteria-only; matches the PR's stated fix.
  • check-stamps.sh / extract-breadcrumbs.sh: the "may" regex split (may[^a-z]*[0-9]|[0-9][^a-z]*may) is applied consistently across all three sites the PR claims (both detectors + the check-stamps.sh classifier block), and the window-bound reasoning in the comments checks out against the code.
  • source-fetch.md / dispositions.md / SKILL.md: the new vendored-snapshot tier rule and the "searched-surfaces listing is unenforced" claim are both accurate — confirmed emit-findings.sh has no searched field, matching the claim in SKILL.md/source-fetch.md.
  • check-stamps.test.sh / extract-breadcrumbs.test.sh: new fixtures are well-targeted (modal-vs-dated "may" in multiple positions) and assert cross-script agreement, which is a real gap the PR says a prior fix missed.
  • plugin.json version bump (0.3.2 → 0.4.0) matches the new CHANGELOG.md entry.

Everything else in the diff (rescoring narrative, design-gap docs) is prose/documentation with no executable surface to verify beyond the one inconsistency above.

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 7s —— View job


Security review complete ✅

  • Load /review:security-review skill instructions
  • Skip-gate check (PR open, security-relevant surface, no prior applicable review)
  • Fetch PR diff (origin/main...HEAD) and read changed files
  • Hunt for logic/trust-boundary/injection/secret-handling/Actions-permission issues
  • Adversarial validation of any candidate findings
  • Post findings (inline + summary)

No security-relevant surface in this diff. All 10 changed files are within plugins/provenance/:

  • CHANGELOG.md, SKILL.md, reference/rubric.md, reference/dispositions.md, reference/source-fetch.md, .claude-plugin/plugin.json — documentation and version bump only (rubric wording, disposition guard notes, vendored-snapshot tier rule, searched-surfaces limitation). No code, no executable logic.
  • scripts/check-stamps.sh (0fe6444) and scripts/extract-breadcrumbs.sh (cfc10f9) — narrow awk-regex tightening so the word "may" needs an adjacent digit to be classified as a date/stamp candidate, plus a matching classifier update in check-stamps.sh. Considered and ruled out:
    • ReDoS: the new alternation (may[^a-z]*[0-9]|[0-9][^a-z]*may) runs only against win, a substring bounded to wlen + 9 (≤ 69) characters, so pathological input size isn't reachable regardless of pattern shape.
    • Injection: these scripts read local markdown files already tracked in the repo (not untrusted network input) and pass no user-controlled data into eval, command substitution, or a shell sink.
  • scripts/check-stamps.test.sh, scripts/extract-breadcrumbs.test.sh — new fixtures/assertions in local temp dirs, no external input.
  • No .github/** or workflow files touched; no permissions, secrets, auth, or network-fetch code changed.

No CRITICAL / IMPORTANT / SUGGESTION findings to report.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Last security-reviewed head: ce9c01241744b16dc88ac3d5e4b68595014a6cb9. On the next push, the relevance gate compares only the commits since this SHA; delete this comment to force a full re-review.

Comment thread plugins/provenance/skills/audit/scripts/check-stamps.sh Outdated
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude has reviewed this PR 5 times. The lane skips further automatic reviews after 5; deleting this comment resets the count.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52307d7717

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/provenance/skills/audit/scripts/check-stamps.sh Outdated
…red number

The review lane found the script comments saying 13 of 22 while the
changelog said 17 of 22: same measurement, same corpus, same date, two
numbers depending on which file you read. Measuring turned up a third.

The shipped figure is 19 of 24, over 1,352 files at --as-of 2026-08-28 on
this branch head. The 17 of 22 was measured correctly, but against the
branch base before it was repaired: that base sat two commits behind main
and so lacked merged changelog entries whose own prose contains the modal.
Repairing the base moved the count to 19 of 24. The 13 matched no tree.

All three sites now carry the measured number and say the count is
tree-dependent, because a corpus that includes this repository moves when
this repository gains prose. That is the same effect 0.3.2 records, showing
up one layer further out: not a stale absolute this time but a figure that
was accurate at the commit it was taken on and stopped being so when the
base changed underneath it.

Reworded to avoid an apostrophe: the comment sits inside the single-quoted
awk program, and "repository's" terminated the string. Caught by shellcheck
before commit rather than in CI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 5m 34s —— View job


Code review complete

  • Gather context (PR body, changed files, prior review comments)
  • Invoke /review:code-review criteria and follow its instructions
  • Post inline comments for findings anchored to changed lines
  • Post final summary

Scope: this is a re-review at the new head (bf0a7b7, 6 commits total). The two issues flagged by the prior review round (script comments saying "13 of the 22" vs. CHANGELOG.md/PR body saying "17 of the 22", and the invented last_verified_2026_08 exhibit) are both fixed in the two newest commits (52307d7, bf0a7b7) — all three sites now say "19 of the 24" and cite the real measurement/exhibit. Confirmed by reading the current file contents, not just the commit messages.

Finding posted inline (new)

Digitless "May" stamps silently disappear instead of declining, posted on check-stamps.sh:359 and extract-breadcrumbs.sh:219:

Before this PR, may was part of the bare month-name alternation, so a real stamp keyword next to a digitless May (Checked last May against the vendor page.) matched, failed to parse as a date, and landed in the declined bucket for a human to see. After the fix, that same line matches nothing in keyword_window() (check-stamps.sh) or is_stamp() (extract-breadcrumbs.sh) — both return empty/0 — and the caller in each script (if (win == "") next / the is_stamp(line) guard) drops the line entirely: not declined, not inventoried, invisible to both scripts.

The other eleven months are unaffected and still over-report into the declined bucket as intended. Only digitless "may" moved the other direction — from over-reporting to silent non-reporting — which is the direction CHANGELOG.md's own "Added" entry says the fix "must not trade" for. Neither check-stamps.test.sh nor extract-breadcrumbs.test.sh has a case for a stamp keyword next to a digitless "May" (only a dated "May 2026"/"17 May 2026" stamp and a modal "may" with no stamp keyword nearby), so the gap ships untested. I verified this independently by tracing the awk control flow end-to-end and cross-checking against the pre-PR regex (git show 4f11a43:.../check-stamps.sh) rather than taking Codex's earlier comment on the same line at face value — the two reviews reached the finding independently and agree on the mechanism.

This is a corpus-realistic pattern ("Verified this May", "Checked last May") the design otherwise explicitly tries to keep visible, so it's worth a decision: either accept the narrower blind spot as intentional (and say so in the comment/changelog), or keep digitless "May" landing in the declined/inventoried set the way the other eleven months do, distinct from a modal "may" with no stamp keyword nearby at all (which is the case this fix is actually meant to suppress).

What I checked and found sound

  • rubric.md, dispositions.md, source-fetch.md, SKILL.md, plugin.json, extract-breadcrumbs.test.sh — unchanged since the prior review round validated them (leaked-answer-key removal, vendored-snapshot tier rule, searched-surfaces-unenforced claim, version bump); re-confirmed those files are untouched in the two newest commits.
  • check-stamps.test.sh / CHANGELOG.md diffs in the reconciliation commits — the "19 of 24" figure, the tree-dependence caveat, and the new modal-may.md test fixture are internally consistent across both files now.

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 18s —— View job


Security review complete ✅

  • Gather context (PR body, changed files, HEAD SHA)
  • Invoke /review:security-review per custom instructions
  • Skip-gate check (PR open, diff since last reviewed head, prior applicable review)
  • Post findings (inline + summary)

Skip gate hit — no new security-relevant surface since the last review.

Last security-reviewed head: 52307d77177d1af0bd156bbd4a51170dea00a9aa. Current head bf0a7b7a37ebaa6c283a3d0541c1da5ee1406258 adds exactly one commit on top of that (bf0a7b7), which touches only:

  • CHANGELOG.md — prose only, reconciling the "17 of 22" vs "13 of 22" month-name-decline figure to a single measured "19 of 24" number and explaining why it drifted.
  • check-stamps.sh#L304-L312 — comment-only edit, same reconciliation. No code line changed; the may[^a-z]*[0-9]|[0-9][^a-z]*may regex logic already reviewed at the prior head is untouched.
  • check-stamps.test.sh#L214-L221 — comment-only edit in a test file, no assertions changed.

No executable logic, permissions, secrets, auth, injection surface, or GitHub Actions config changed in this delta. This is the fix for the inline figure-mismatch finding from the prior code-review pass, not a new logic change, so the prior security review (no CRITICAL/IMPORTANT/SUGGESTION findings) still applies in full. Nothing new to report.

Comment thread plugins/provenance/skills/audit/scripts/check-stamps.sh Outdated
Comment thread plugins/provenance/skills/audit/scripts/extract-breadcrumbs.sh Outdated
claude added 2 commits August 28, 2026 12:30
… paths

Requiring a digit beside "may" kept the modal out of the declined bucket and
took a real stamp form with it. "Verified this May" carries no digit, so
keyword_window() returned "" and the caller dropped the line at `if (win == "")
next`, before it could reach n_dec++ and be classified. is_stamp() returned 0
the same way. The line was a candidate declining as a month-name form, visible
to a human reading the declined bucket, and it became invisible to both audit
paths at once. That is under-reporting, the one direction this detector must
not move in, and it was inconsistent: "Verified in June" still matched bare and
still declined.

Either signal now counts as a May date, and either alone is enough: a digit
beside it, as before, or a capital M read from the ORIGINAL line rather than
the lowered copy the rest of the scan works from. Edited prose capitalises the
month and not the modal, and tolower() was throwing away the only signal that
separated them. Both scripts get one may_form() and all three sites call it:
keyword_window(), the classifier that sets the decline reason, and is_stamp().

Two costs, measured over the 1,352-file corpus at --as-of 2026-08-28 rather
than assumed. A capitalised modal opening a sentence or a table cell reads as a
month, which over-reports into a bucket a human reads and is the safe
direction; seven of the 24 capitalised "May" lines are that shape. ALL-CAPS
defeats case, so a digitless ALL-CAPS May date stays invisible, but all 34
ALL-CAPS "MAY" lines in the corpus are RFC-2119 modals and not one is a date,
so reading that form as a month would cost 34 false candidates to buy a date
form nobody writes. A digit still rescues "VERIFIED MAY 2026".

Corpus figures are unmoved: 527 candidates, 499 parsed, 28 declined, 0
findings, and extract-breadcrumbs.sh inventories the same 517 stamp lines. The
repo carries no digitless May stamp today, so this restores a detection the
fix had silently dropped rather than changing what the tree reports.

The suites' existing cross-script agreement assertion could not have caught
this: it asserts both scripts return the same count over a shared fixture, and
it passed while both were equally wrong. The new cases pin non-zero expected
counts on each side.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
…asurement

Three independent reviewers found that requiring a digit beside "may" made
a digitless stamp vanish, and vanish upstream of the declined bucket rather
than inside it, so it escaped both audit paths without appearing anywhere a
human reads. The correction is in 3c538bc; this records what happened and
what it cost.

Two things worth keeping beyond the fix.

The suites could not have caught it. They assert both scripts return the
same count over a shared fixture, which passes when both are equally wrong,
and both were. A cross-implementation agreement test detects divergence and
is blind to a common error, and a shared definition is what makes a common
error likely. The new cases pin a non-zero expected count on each side, so
agreement is backed by a known answer instead of two implementations
nodding at each other.

The case signal was measured rather than assumed: over 1,352 files, 1,458
lines carry a lowercase modal "may", 24 carry a capital "May" of which
about 17 are dates, and 34 carry ALL-CAPS "MAY" of which none is a date.
That is what makes the capital usable and what rules out reading ALL-CAPS
as a month. Both accepted costs are named and tested.

Corpus figures are unchanged at 527 / 499 / 28 / 0: this tree carries no
digitless-May stamp, so the regression was latent here and real in
principle, which is why tests rather than a corpus count had to catch it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 4m 3s —— View job


Code review complete

  • Gather context (PR body, prior review rounds, changed files)
  • Invoke /review:code-review and diff against the last-reviewed head (bf0a7b7)
  • Post inline comments for findings anchored to changed lines
  • Post final summary

Scope: this is a re-review at the new head (a827aa5, 8 commits total, git diff bf0a7b7..HEAD). Two commits landed since the last round — 3c538bc (the fix for the digitless-May regression both Claude review rounds and Codex flagged on bf0a7b7) and a827aa5 (changelog documentation of that fix). No other files changed in this delta; rubric.md, dispositions.md, source-fetch.md, SKILL.md, plugin.json are untouched since the previous round that already validated them.

The flagged regression is fixed

Traced may_form() in both scripts by hand against the awk control flow (this lane's tool scope has no general script execution, only gh pr diff/view/comment/review + Read/Glob/Grep, so I could not run check-stamps.test.sh — this is a static trace, not an executed one):

  • Two independent signals now admit a May date, either sufficient alone: a digit beside may (case-insensitive, as before), or a capital M read from the original line (worig) rather than the lowered copy the rest of the scan works from — check-stamps.sh:342-349, mirrored in extract-breadcrumbs.sh:183-196.
  • The trailing-boundary requirement (May([^a-z]|$)) correctly excludes Mayonnaise/Maybe-shaped words (next char is a lowercase letter, so the alternation fails) while still catching May at end-of-window. Case sensitivity on the literal May correctly excludes MAY/ALL-CAPS from the capital-only path (falls back to needing a digit, which is the documented, tested trade-off).
  • win_orig/rest_orig are cut at the same offsets as the lowered win/rest (tolower() preserves length), so the character alignment may_form() depends on holds. The re-cut in the decline-reason classifier (substr(win_orig, 1, length(win)) at check-stamps.sh:459) matches the comment's stated reasoning — it prevents a capital May sitting only in the 9-character slack from relabeling a decline that was actually accepted on a different rule.
  • Traced all 10 fixture lines in check-stamps.test.sh:274-309 and extract-breadcrumbs.test.sh by hand against the regex; each expected candidate/non-candidate outcome matches what the code should produce, including the two deliberately-accepted trade-offs (capitalized sentence-initial modal over-reports; ALL-CAPS digitless MAY stays invisible).
  • The new fixtures close the exact gap the regression shipped through: prior suites only asserted cross-script agreement on line count, which passes when both implementations are equally wrong. The new cases pin non-zero expected counts, so agreement is now checked against a known answer.

On the corpus figures

CHANGELOG.md's "unchanged at 527/499/28/0" claim (plugins/provenance/CHANGELOG.md:134-137) is internally consistent (527 = 499 parsed + 28 declined) and consistent with the prior round's reconciled "19 of 24" figure, but it's a measurement over this repo's corpus that I can't independently execute in this lane — treating it as author-claimed and unverified, per the review lane's rule against restating claimed verification as confirmed.

No new correctness issues found. The two issues raised in the prior two rounds (the "13 vs 17 of 22" figure mismatch, and this digitless-May regression) both read as resolved in the current head.

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 0s —— View job


Security review complete ✅

  • Gather context (PR body, changed files, HEAD SHA)
  • Invoke /review:security-review per custom instructions
  • Skip-gate check (PR open, diff since last reviewed head, prior applicable review)
  • Post findings (inline + summary)

Skip gate not met — new code changed since the last review, so this ran a full pass on the delta.

Last security-reviewed head: bf0a7b7a37ebaa6c283a3d0541c1da5ee1406258. Current head a827aa5 adds two commits on top of that (3c538bc, a827aa5), and unlike the previous delta this one is not comment-only — it changes executable awk logic:

  • check-stamps.sh (may_form() + call sites) — factors the digit-or-capital "is this the month May" test into a shared function, called at all three sites (keyword_window()'s window test, the d_reason classifier, and the equivalent in extract-breadcrumbs.sh).
  • extract-breadcrumbs.sh (may_form() + is_stamp()) — same shared function, called from is_stamp().
  • CHANGELOG.md, check-stamps.test.sh, extract-breadcrumbs.test.sh — prose and new fixtures/assertions covering the digitless-May case; no security surface.

Reviewed the new may_form() logic specifically for the classes this lane covers:

  • ReDoS: both new regexes ((may[^a-z]*[0-9]|[0-9][^a-z]*may) and May([^a-z]|$)) run only against win/worig (check-stamps.sh) or rest/rest_orig (extract-breadcrumbs.sh), all of which are substr(..., wlen + 9) — bounded to at most ~69 characters regardless of input file size. Same bound as the prior head's already-reviewed pattern; no nested quantifiers or new catastrophic-backtracking shape introduced.
  • Injection: no new input sources. Both functions operate on local markdown files already tracked in the repo (same trust boundary as before), no shell/eval/command-substitution sink touches this data, and no new CLI argument parsing was added.
  • Trust boundary: unchanged — these scripts still only read files under audit, not user-supplied or network-fetched content.

No CRITICAL / IMPORTANT / SUGGESTION findings to report. The correctness question this delta raises (digitless-May handling) is outside this lane's scope — it's already being tracked in the code-review thread on this PR.
· branch claude/detect-copied-external-content-k5aw77

…sified

Three findings from the fresh-context verifier on the digitless-May fix. It
passed the code and failed the state, on numbers rather than logic.

The capitalised-May split was wrong. The comment and the changelog said
seven of the 24 capital "May" lines are modals and about 17 are dates. Read
line by line at 3c538bc it is ten modals and 14 dates. That figure sat in
the paragraph headed "measured on this corpus, not assumed", one commit
after a commit whose whole purpose was reconciling a figure to one measured
number. Corrected in both places, with the commit attached.

The "corpus effect: none" claim falsified itself one commit later. It is
true at 3c538bc, where the tree carried no digitless-May stamp. But the
paragraph explaining the fix quotes "Verified this May" inside a verified
keyword window, in a file the corpus scans, so from a827aa5 onward the
corpus does carry one: 529/499/30 post-fix against 528/499/29 pre-fix, an
effect of +1 rather than none. Prose about the detector moved what the
detector reports, for the fourth time on this branch. Every figure in the
entry now carries the commit it was taken at, which is the rule the file
already states and keeps relearning.

Also records a latent under-report the verifier constructed: may_form()
returns on the digit branch, so RSTART belongs to that match, and a
digit-adjacent "may" beyond the window makes the caller reject before the
in-window capital is consulted. Appending a stray "7 may" to a valid line
removes its candidacy. No corpus line has that shape. Left rather than
chased, and recorded at the rule: returning the leftmost of the two matches
would fix it, while trying the capital branch first only mirrors the bug, so
the obvious one-line swap is not a fix. Both scripts inherit it identically,
so the cross-script agreement assertion is blind to it, exactly as it was to
the regression this commit series started from.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 2m 43s —— View job


Code review complete

  • Gather context (PR body, prior review rounds, changed files)
  • Invoke /review:code-review and diff against the last-reviewed head (a827aa5)
  • Post inline comments for findings anchored to changed lines
  • Post final summary

Scope: re-review at the new head (ce9c012, 9 commits total, git diff a827aa5..HEAD). One new commit landed since the last round — ce9c012 — touching exactly two files, and the diff is comment/prose-only in both (verified no non-#-prefixed line changed in check-stamps.sh):

  • check-stamps.sh#L328-L349 — comment-only: corrects the capitalised-May split (7/17 → 10/14, now cited against the commit it was counted at) and adds a new paragraph documenting a latent under-report in may_form().
  • CHANGELOG.md#L120-L163 — the matching changelog reconciliation, plus a new passage explaining that the changelog text added one commit earlier (quoting Verified this May as an example) made the corpus it measures over grow by exactly the digitless-May stamp the entry was documenting.

No executable awk/bash logic changed in this delta — may_form()'s two branches, the caller's RSTART <= wlen guard, and every regex are byte-identical to the previously-reviewed head.

The newly-documented edge case checks out

Traced the claimed gap by hand against both scripts' may_form() + caller (check-stamps.sh:342-349+397, extract-breadcrumbs.sh:193-199+239):

may_form(w, worig) tries the digit-adjacent regex against the whole window w first; match() returns true (and sets global RSTART) as soon as any occurrence exists, including one that falls in the 9-character slack zone beyond wlen. If that's the only digit-adjacent may in the window, the function returns before ever testing the capital-May branch against worig — so a valid, in-window capitalized May earlier in the same window never gets consulted, and the caller's RSTART <= wlen guard rejects the whole match. A trailing 7 may appended after a legitimate Verified this May can therefore flip a would-be-declined stamp to silently dropped. Confirmed the same structure exists verbatim in extract-breadcrumbs.sh:239 (may_form(rest, rest_orig) && RSTART <= wlen), matching the changelog's "both scripts inherit it identically" claim.

This matches the class of under-report the PR spent the last two rounds fixing, but it's correctly scoped as a documented, deliberately-unfixed latent case (no corpus line has the shape, and the comment explains why the obvious one-line reorder doesn't fix it — swapping branch order just moves which side of the bug you're on). No new test coverage was added for it, consistent with how it's framed: recorded at the rule, not shipped as a fix.

Numbers check out internally

Traced the two corpus-measurement claims in CHANGELOG.md:148-158:

  • At 3c538bcc: 527/499/28/0 unchanged, because the digitless-May example text didn't exist in the corpus yet at that commit.
  • At a827aa58: 529/499/30 (post-fix) vs 528/499/29 (pre-fix) — a +1 candidate delta. This is consistent: the CHANGELOG prose added at a827aa5 itself contains Verified this May inside a verified keyword window, which only the post-fix (capital-signal) code catches; the pre-fix (digit-only regression) code does not. This is the same self-referential effect the entry documents, and the arithmetic holds.

One minor observation, not flagged inline as it's below the high-signal bar for this lane: the a827aa58 figure (CHANGELOG.md:157) reports only 3 of the 4 numbers (candidates/parsed/declined), dropping findings where the earlier 3c538bcc figure at line 149 reports all 4. Not a correctness issue since findings is stated elsewhere as the invariant that never moves — just a formatting inconsistency between the two adjacent figures if a future reader diffs them literally.

No new findings to post inline

Nothing in this delta rises to a blocking or flag-worthy issue — it's a self-consistent documentation correction that closes out the last round's regression discussion.

Branch: claude/detect-copied-external-content-k5aw77

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 7s —— View job


Security review complete ✅

  • Gather context (PR body, prior review rounds, changed files, HEAD SHA)
  • Invoke /review:security-review per custom instructions
  • Skip-gate check (PR open, diff since last reviewed head, prior applicable review)
  • Post findings (inline + summary)

Skip gate hit — no new security-relevant surface since the last review.

Last security-reviewed head: a827aa58212d553b6a995a65f45d319d4f1df429. Current head ce9c012 adds exactly one commit on top of that (ce9c012), touching only two files:

  • CHANGELOG.md — prose only: reconciles the corpus-figure split (14 month-dates / 10 modals among the 24 capitalized May lines) and documents that the Verified this May example line in this same file became a digitless-May stamp the corpus itself now carries, moving the post-fix count from 527/499/28/0 to 529/499/30 at a827aa58. No executable content.
  • check-stamps.sh#L328-L353comment-only addition documenting a known, unfixed under-report edge case in may_form() (an out-of-window digit match can suppress an in-window capital-May match via the RSTART <= wlen guard). No code line changed — git diff --stat a827aa5..ce9c012 confirms only these two files moved, and the awk logic reviewed at the prior head (may_form(), keyword_window(), is_stamp()) is byte-identical.

No new executable logic, permissions, secrets, auth, injection surface, or GitHub Actions config changed in this delta. The prior review's findings (no CRITICAL/IMPORTANT/SUGGESTION on the may_form() regex logic — bounded substr window rules out ReDoS, no untrusted input reaches these scripts) still apply in full. The correctness/documentation question this delta addresses (the under-report edge case it now records at the rule) is outside this lane's scope; it's a documented-and-accepted trade-off, not a new code path.

No CRITICAL / IMPORTANT / SUGGESTION findings to report.
· branch claude/detect-copied-external-content-k5aw77

…hape

The code-review lane noted, below its flagging bar, that the a827aa5
figure gave three numbers where the 3c538bc figure beside it gave four,
dropping findings. In an entry whose whole subject is figures that
reproduce, two adjacent measurements should be diffable literally. Both now
read candidates / parsed / declined / findings, and findings is 0 on both
sides, which is the invariant the entry relies on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tvKPLnWr7suxxAupU4p8A
@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 51s —— View job


Code review complete

  • Gather context (PR body, prior review rounds, changed files)
  • Invoke /review:code-review and diff against the last-reviewed head (ce9c012)
  • Post inline comments for findings anchored to changed lines
  • Post final summary

Scope: re-review at the new head (dd9be30, 10 commits total, git diff ce9c012..HEAD). One new commit landed since the last round — dd9be30 — and it is a single-line change in a single file:

  • CHANGELOG.md:157 — adds the fourth number (/ 0) to both sides of the a827aa58 figure, so it now reads 529 / 499 / 30 / 0 post-fix against 528 / 499 / 29 / 0 pre-fix.

No executable code, test, or other doc file changed in this delta.

This closes the observation from the prior round

The last review round noted (as a non-blocking observation, not a finding) that the a827aa58 figure reported only 3 of the 4 numbers (candidates/parsed/declined) while the adjacent 3c538bcc figure at CHANGELOG.md:149 reports all four (527 / 499 / 28 / 0). This commit makes both figures the same four-number shape.

Checked the arithmetic on the corrected figure, consistent with the surrounding prose (both figures were already validated in the prior round; this only adds the trailing findings digit):

  • Candidates: 528 → 529 (+1, the digitless-May stamp the changelog text itself introduced into the corpus)
  • Parsed: 499 → 499 (unchanged)
  • Declined: 29 → 30 (+1, matches the candidate increase — an unparseable stamp lands in declined, not parsed)
  • Findings: 0 → 0 (unchanged, matching the entry's own claim that this number is the load-bearing invariant across every figure in this changelog)

That's internally consistent with the entry's stated +1 candidate rather than none effect and with the load-bearing "findings never moves" claim made elsewhere in the same entry.

No new findings

This is a self-contained prose correction with no executable surface and no new claims — nothing to flag inline. All prior rounds' findings (the "13 vs 17 of 22" figure mismatch, the invented last_verified_2026_08 exhibit, and the digitless-May regression) remain resolved as of this head; nothing in this delta reopens them.

Branch: claude/detect-copied-external-content-k5aw77

@kyle-sexton
kyle-sexton merged commit 5e46685 into main Aug 28, 2026
59 checks passed
@kyle-sexton
kyle-sexton deleted the claude/detect-copied-external-content-k5aw77 branch August 28, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants