Summary
The exclusion ledger from #403 records precisely which subject each stage removed from the analysed surface. No human-facing surface carries that subject. A reviewer is told how many exclusions are new and never what they are.
This is the last unchecked box on #403:
- [ ] Surface the ledger in the human-facing reason text and in next_action
Eight of the epic's nine boxes shipped and are verified working across three repositories. This one did not, and it is the box that completes the epic's own thesis — "a stage computed the right signal, stored it, and did not connect it to the decision." The ledger fixed that at five stages and left it standing at the sixth: its own output.
Reproduction
github/github-mcp-server at #3020 — a PR adding one readOnlyHint: true tool, find_duplicate. CLI 0.16.0b7 at 9233c6b8.
Manifest reviewed at base binding all 115 published tools (agent_bindings.declarations, complete: true), head at 116, so the new tool is the only new gap and nothing outranks it:
"binding_coverage": {"total_catalog_tools": 116, "reachable_tools": 115, "unbound_tools": 1,
"pass_eligible": true, "gap_count": 1,
"reason_counts": {"missing_binding_evidence": 1}},
"surface_exclusions": {"total": 1, "gated": 1, "gap_backed": 1, "truncated": false}
The ledger entry is exactly right — right stage, right subject, right accounting:
("binding", "find_duplicate [github_mcp]", "evidence_gap")
What the reviewer gets, verbatim from pr-comment.md:
- Merge verdict: `blocked`
- Summary: 14 active finding(s) block release; 131 review item(s) accepted as debt.
1 of 83 evidence gap(s) are new in this diff.
- Reason: 14 active findings block release.
- Capability delta: +0, 0 modified, -0
- Next action: 14 active finding(s) block release; 131 review item(s) accepted as debt.
1 of 83 evidence gap(s) are new in this diff.
Checked programmatically across all three surfaces:
| surface |
names find_duplicate? |
headline |
no |
control.next_action |
no |
pr-comment.md |
no |
surface_exclusions.entries[].subject |
yes |
Two things compound it
No Most severe: clause appears. That clause is what carried the subject in the cases where this looked fine — on adk-samples#1745 the comment led with "Most severe: create_sap_sales_order has financial write capability without required controls." Here the blockers are pre-existing debt on the other 115 tools, so the most-severe finding is unrelated to the diff, and the one thing the diff actually did is reduced to a digit in "1 of 83."
Capability delta: +0, 0 modified, -0 sits directly above it on a PR that adds a tool. That number is self-consistent — the tool is unbound, so it is not bound capability — but the two lines together tell a reviewer that this PR changed no capability and that one anonymous gap appeared.
Why the earlier narrowing was too generous
I reported this once before as "the projection never names the subject," then corrected it to "it names the subject fine when a blocker exists." That correction was right about the mechanism and wrong about the scope of the problem. The subject is carried by the findings projection, not by the ledger. So the gap is exactly: an excluded subject that has a gap but no blocker is nameless in every human surface — which is the normal case for a newly-added tool that has not yet been declared, i.e. the case #403 was built for.
Suggested direction
The reason text already composes several clauses (N active findings, M review items, X of Y gaps are new). Adding the gated subjects to the new-gap clause is a rendering change, not a decision change:
1 of 83 evidence gap(s) are new in this diff: find_duplicate [github_mcp] is not bound by any reviewed declaration.
With a cap and an "and N others" tail for a diff that adds many, mirroring how Most severe: already handles the findings side. next_action wants the same subject so the reader is sent somewhere specific, and capability_review is worth a separate look — whether an added-but-unbound tool should appear in the delta at all, or whether the ledger line is the right place for it.
Acceptance
Follow-up to #403, whose other eight boxes are verified working. Found 2026-08-24 while auditing that epic's completion against a purpose-built case where the new tool is the only new gap.
Summary
The exclusion ledger from #403 records precisely which subject each stage removed from the analysed surface. No human-facing surface carries that subject. A reviewer is told how many exclusions are new and never what they are.
This is the last unchecked box on #403:
Eight of the epic's nine boxes shipped and are verified working across three repositories. This one did not, and it is the box that completes the epic's own thesis — "a stage computed the right signal, stored it, and did not connect it to the decision." The ledger fixed that at five stages and left it standing at the sixth: its own output.
Reproduction
github/github-mcp-serverat #3020 — a PR adding onereadOnlyHint: truetool,find_duplicate. CLI0.16.0b7at9233c6b8.Manifest reviewed at base binding all 115 published tools (
agent_bindings.declarations,complete: true), head at 116, so the new tool is the only new gap and nothing outranks it:The ledger entry is exactly right — right stage, right subject, right accounting:
What the reviewer gets, verbatim from
pr-comment.md:Checked programmatically across all three surfaces:
find_duplicate?headlinecontrol.next_actionpr-comment.mdsurface_exclusions.entries[].subjectTwo things compound it
No
Most severe:clause appears. That clause is what carried the subject in the cases where this looked fine — onadk-samples#1745the comment led with "Most severe: create_sap_sales_order has financial write capability without required controls." Here the blockers are pre-existing debt on the other 115 tools, so the most-severe finding is unrelated to the diff, and the one thing the diff actually did is reduced to a digit in "1 of 83."Capability delta: +0, 0 modified, -0sits directly above it on a PR that adds a tool. That number is self-consistent — the tool is unbound, so it is not bound capability — but the two lines together tell a reviewer that this PR changed no capability and that one anonymous gap appeared.Why the earlier narrowing was too generous
I reported this once before as "the projection never names the subject," then corrected it to "it names the subject fine when a blocker exists." That correction was right about the mechanism and wrong about the scope of the problem. The subject is carried by the findings projection, not by the ledger. So the gap is exactly: an excluded subject that has a gap but no blocker is nameless in every human surface — which is the normal case for a newly-added tool that has not yet been declared, i.e. the case #403 was built for.
Suggested direction
The reason text already composes several clauses (
N active findings,M review items,X of Y gaps are new). Adding the gated subjects to the new-gap clause is a rendering change, not a decision change:With a cap and an "and N others" tail for a diff that adds many, mirroring how
Most severe:already handles the findings side.next_actionwants the same subject so the reader is sent somewhere specific, andcapability_reviewis worth a separate look — whether an added-but-unbound tool should appear in the delta at all, or whether the ledger line is the right place for it.Acceptance
find_duplicatein the reason text and innext_action.not_claimedadds no clause (no noise on a settled workspace) —test_a_settled_workspace_records_nothingis the existing guard for the ledger side.catalog_subjectspelling, so it cannot drift from the gap row it came from (the join defect fix(evidence): a declaration cannot discharge a category it does not cover (#409 follow-ups) #413 fixed one layer down).Follow-up to #403, whose other eight boxes are verified working. Found 2026-08-24 while auditing that epic's completion against a purpose-built case where the new tool is the only new gap.