Repository navigation
provenance: five limits the relay-boundary hardening deliberately left open #3481
Description
Activity
- addedneeds-triageNot yet classified. Floor until a type and one priority tier are set.Not yet classified. Floor until a type and one priority tier are set.
on Aug 28, 2026 6. The stamp-rule comment claims "ONE exception" where the code has two
Found by the security review on #3473's merge head
68e3899e, which flagged it as out of scope for its lane and asked that it not be lost. Verified independently before filing.emit-findings.sh(the block at ~L282-293) says a stamp rule relays "whatever the record does or does not declare — with ONE exception": when the record's owntierfield names no tier the reader knows.There is a second path.
withheld_verdictis evaluated first in the classification chain, so a stamp record with notierat all but averdictnaming a judgment verdict is withheld beforeown_tier_unreadableis ever consulted. Reproduced:{"findings":[{"rule":"rule-stamp-expired","file":"a.md","line":1,"verdict":"not-found","searched":["x"]}]}→ exit 0, 0 relay rows,
Withheld from the relay: 1 judgment findings, which stay on the human report by contract.The behavior is correct and should not change. A record declaring
verdict: "not-found"has declared a judgment verdict, and withholding it is the boundary doing its job. The comment's own scoping paragraph is also correct on its example — a stamp finding carrying{"verdict": {"reviewed_by": "alice"}}declares no tier and still relays, which I verified separately.What is wrong is only the count: "ONE exception" should be two, the second being the verdict fallback naming a withheld verdict. It is a comment-only correction in the safe direction (over-withholding, never a leak).
Filed here rather than pushed to #3473 because that PR's CI had only just recovered after a long stall, and a comment fix would have cost another full suite cycle against an otherwise-ready merge. It is the same defect class the PR exists to close — a claim its own code does not honor — so it should be corrected, just not at that cost.
Generated by Claude Code
- added a commit that references this issue
on Sep 1, 2026 This was generated by AI during triage.
Claiming for triage-only evaluation (no execution).
Generated by Claude Code
This was generated by AI during triage.
Triage: verified, routed agent-ready (decision-defaulted)
Six recorded limits. Two are actionable defects that reproduce exactly as described; four are deliberate no-action records. The actionable pair is small, doc- and comment-level, and carries one naming decision, so this routes delegable with that decision defaulted rather than gated.
Verification
Item 3, the tier-name divergence, confirmed.
source-not-identifiedappears in exactly one published surface, the audit skill's frontmatterdescription, alongside the three other tier names. Every other prose surface usesnot-found: the rubric, the dispositions reference, the source-fetch reference, the persist-findings context, the plugin README. The reader's dual-spelling tolerance is real and deliberate, carrying an explanatory comment beside the two predicates that accept both. The item's characterization holds precisely: the tolerance is free and should stay, and the defect is the divergence in the published name, not either name.Item 6, the miscounted exception, confirmed. Reading the classification chain,
withheld_verdictis evaluated beforeown_tier_unreadable, so a stamp record declaring a judgment verdict with no tier is withheld on the first predicate and never reaches the second. The comment above the tier predicate claims a single exception. There are two paths, and the comment's own scoping paragraph, which distinguishes averdictobject carrying no tier from one naming a verdict, is correct as written. The correction is to the count only, and it is in the over-withholding direction.Items 1, 2, 4 and 5 confirmed as deliberate no-action records. They are already asserted as stated limits, bounded, or self-labeling. Nothing to execute. Item 2's optional wording refinement is folded into the brief below as an explicitly optional third change.
Agent Brief
Type: Bug
Summary: Reconcile the provenance audit skill's published neutral-tier name with the rest of its prose, and correct the relay-boundary comment that miscounts its own exceptions.Current behavior:
The audit skill's published description names the neutral tier with one spelling while every other prose surface in the plugin uses another. The reader accepts both spellings deliberately, so nothing malfunctions; the divergence is in what the plugin publishes about itself. Separately, the comment introducing the stamp-rule relay exception states that exactly one condition prevents a stamp record from relaying its own declaration, when two do: a record whose own tier field names an unknown tier, and a record declaring a judgment verdict, which is withheld earlier in the chain.Desired behavior:
One spelling of the neutral tier appears across every prose surface of the plugin, including the published description. The reader continues to accept both spellings, unchanged. The relay-exception comment states both conditions and the order in which they are evaluated, so the comment matches the code it describes.Key interfaces:
- The audit skill's frontmatter
description, where the tier names are published. - The findings-emitting script's classification chain and the comment block introducing the stamp-rule relay exception.
- The dual-spelling predicates in that script. These are not to be narrowed; their tolerance is deliberate and load-bearing.
Acceptance criteria:
- Exactly one spelling of the neutral tier appears in the plugin's prose surfaces. Verified by a repository search returning no occurrence of the retired spelling outside the reader's tolerance predicates, their explanatory comments, and the tests that exercise both spellings.
- The reader still classifies records declaring either spelling identically. Demonstrated by the existing dual-spelling tests passing unchanged.
- The relay-exception comment names both withholding conditions and states that the verdict check runs first.
- A test asserts a stamp record declaring a judgment verdict with no tier field produces zero relay rows and is counted as withheld, pinning the second condition the comment now names.
- The plugin CHANGELOG records the rename and the comment correction.
Out of scope:
- Any behavior change to the relay boundary. Both corrections are to published text; the classification chain's logic stays exactly as it is.
- Narrowing the reader's dual-spelling tolerance.
- Items 1, 2, 4 and 5 of this issue. They are recorded limits, not work. Item 2's wording refinement is optional and may be skipped without failing this item.
- The homoglyph confusables table. Explicitly declined in the issue body for a stated reason.
Optional, at the implementer's discretion: item 2's refinement of the persist-findings context, restating the mechanism first and naming rendering-equivalence as the motive it approximates rather than as a goal. Adopt it or leave it; do not expand it into a rewrite.
Decision defaulted: publish
not-foundas the single name — veto before mergeThe issue frames this as genuinely open, noting a real argument for the longer spelling: it says what was not identified, where the shorter one is ambiguous about what was not found.
Defaulting to
not-foundon weight of usage rather than on elegance. It is the spelling in the rubric, the dispositions reference, the source-fetch reference, the persist-findings context, the README, the evals data, and the script's own prose. The divergent spelling appears in one published field. Renaming toward the majority is a one-field edit; renaming away from it touches every reference surface and the evals data, which is a materially larger change for a naming preference.Because the reader accepts both spellings permanently, this choice cannot break a record either way, which is what makes it safe to default rather than gate.
A maintainer who prefers the longer name should say so before merge. The reversal is mechanical, and the implementer should treat a veto as selecting the other direction of the same rename, not as reopening the question.
Labels: replacing the raw
priority: needs-triagewithpriority: low(no leak, no behavior change, published-text accuracy only, and the issue itself states none of the six is a leak); addingagent-ready,status: ready,work-class: scoped.Triage claim released.
Generated by Claude Code
- The audit skill's frontmatter
- addedpriority: lowNice-to-have, cosmetic, or speculative; opportunistic.Nice-to-have, cosmetic, or speculative; opportunistic.status: readyTriaged, unblocked, and fully specified; eligible to pick up.Triaged, unblocked, and fully specified; eligible to pick up.agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.Fully specified and briefed; eligible for autonomous pickup from the frontier.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.A briefed fix or small feature; blast radius bounded by the brief, tests exist.and removedneeds-triageNot yet classified. Floor until a type and one priority tier are set.Not yet classified. Floor until a type and one priority tier are set.
on Sep 6, 2026 This was generated by AI during triage.
Addendum: item 5 is already fixed
Correcting the brief above. Item 5 (the unwritable-destination test case reporting SKIP as a pass under uid 0) was routed as a no-action record on the issue's own framing, "pre-existing, honest about itself, left alone." It has since been fixed.
The provenance CHANGELOG records it under 0.5.3:
emit-findings.test.shcounted a host skip as a pass. One case routed a skip throughpass(), contradicting the rule stated thirty lines above it in the same file: a skip never routes throughpass(), so a proof the host could not run can never be read off the summary as one that did. The suite's honest count on a host wherechmod a-wdoes not bite is 384 passes and 1 skip, not 385 passes.This does not change the routing or the brief. Item 5 was already out of scope; it is now out of scope because it is done rather than because it was accepted. Recording it so the implementer does not go looking for it, and so the issue's count of open limits reads as five rather than six.
Items 1, 2 and 4 remain accurate as recorded limits.
Generated by Claude Code
- added a commit that references this issue
on Sep 7, 2026 - added a commit that references this issue
on Sep 25, 2026 Closing in the backlog-drain close sweep: won't-fix. Evidence: #3911 and 0.5.3 fixed items 3, 5, 6; items 1, 2, 4 are recorded no-action limits ("Not a boundary hole; noted for completeness")
Filed from #3473 so the deliberate exclusions survive the squash. Each was found during the relay-boundary work, judged out of scope for that PR, and is recorded here rather than left in a commit message.
None is a leak. The boundary in #3473 is verified in both directions: 45 adversarial vectors, and a suite grown from 110 assertions to 385, with the survive-direction coverage it previously had none of.
1. An ambiguous tier list is withheld and can refuse the sidecar
{"tier": ["fingerprint-confirmed", "not-found"]}is withheld, and refused at exit 3 when it names no searched surfaces. The value half cannot distinguish it from the["not-found"]evasion that an earlier round closed without reopening that vector.Asserted as a stated limit (
survive-ambiguous), so it is visible rather than silent. The fix, if wanted, is a rule for what a record declaring two tiers means — which is a spec question, not a reader question.2. Letter homoglyphs beyond the dash class
All five recognized names are hyphenated, so hyphen-likes fold to ASCII by the dash class. Going further means a confusables table (Cyrillic
о→ Latinoand the rest), whichjqhas no access to and which would itself become "a list of the code points someone thought of" — the failure mode that leaked four consecutive rounds.The blast radius is bounded: since
7b05e0ca, an unreadable declared tier cannot reach a relay row on any rule, including the stamp rules. Such a record takes## Unparsedor the not-relay-eligible count, both visible, andreview:fanoutnever auto-classifies## Unparsed.Optional wording refinement.
context/persist-findings.mdcurrently frames the goal as rendering-equivalence and then carves out the exception. The text is honest — it says the reader "pursues that", and calls the limit "stated, not closed" — but framing a goal invites the next reader to falsify the frame. Stating the mechanism first and naming rendering-equivalence as the motive it approximates would be marginally more accurate.3.
source-not-identifiedversusnot-foundSKILL.md:2publishes the neutral tier assource-not-identified; everything else —rubric.md,persist-findings.md, the script's prose — saysnot-found. The reader now recognizes both spellings, deliberately and permanently: recognizing one name too many can only withhold a record, which is visible and counted, while recognizing one too few relays a judgment verdict. The tolerance is free and should stay.What remains is reconciling the published name.
SKILL.md:2is the only place the divergent spelling appears, so the rename is cheap and one-directional. The argument for keepingsource-not-identifiedas the published name: it says what was not identified, wherenot-foundis ambiguous about what was not found. The defect is the divergence, not either name.4. Duplicate case-folded keys
{"tier": "a", "Tier": "b"}—jqis last-wins, and the raw record text is unchanged, so a first-wins consumer reading the JSON directly could disagree with the reader. Not a boundary hole; noted for completeness.5. A test case reports SKIP as a pass under uid 0
emit-findings.test.sh's unwritable-destination case cannot exercise its condition when running as root, and self-labels the skip in its message. Pre-existing, honest about itself, left alone.Related