Skip to content

fix(discovery): three acceptance-gate obligations have no mechanism, and the gate scripts carry no grant #2267

Description

@kyle-sexton

discovery's post-dispatch acceptance gate is the plugin's central safety mechanism, and it is uneven: step 1 is the only step a model executes from prose rather than a script, one stated parent/agent obligation has no gate step at all, the gate invocations carry no allowed-tools grant so a gate that cannot run is silently skipped, and three consecutive releases had to fix assertions in the deterministic layer that could not fail. Individually small; together they are the same shape — an obligation stated without a mechanism that can observe whether it was met.

Rows

  • I7 B-F6plugins/discovery/skills/explore/SKILL.md:44, plugins/discovery/skills/research/SKILL.md:44 — gate step 1 (payload well-formedness + echo-back comparison) is executed by prose; steps 2 and 3 each invoke a script and cite an exit status. Same row: agents/explorer.md:9 is maxTurns: 30 while agents/researcher.md:9 is maxTurns: 40 — the read-heavier workload has the smaller budget, unexplained.
  • I7 B-F11plugins/discovery/skills/explore/SKILL.md:50, plugins/discovery/skills/research/SKILL.md:50,58 — the gate scripts are invoked through Bash and neither SKILL.md declares allowed-tools. Every gate in this plugin is engineered to fail closed; the un-run case is unstated.
  • I9 D-F7plugins/discovery/skills/explore/SKILL.md:42, plugins/discovery/skills/research/SKILL.md:42 — the memory root's self-ignoring .gitignore guard is declared "the agent's obligation, per its own contract" and no gate step grades it. The observed run left it unwritten.
  • I7 B-F8plugins/discovery/CHANGELOG.md:157 (0.13.1), :229 (0.12.2), :252 (0.12.1) — three consecutive releases each fixed a test or eval that asserted something it did not test, each found by an independent verification pass rather than the authoring context. An assertion that cannot fail is the script-layer analogue of the self-graded gate these skills refuse everywhere else.

Evidence

Step 1 has no mechanismskills/research/SKILL.md:44:

1. **The payload is well-formed** — `preload_token` matches the sentinel verbatim, and an `artifact:` pointer is present. Missing either is a **failed dispatch** whatever the `status` field says; a missing token is a discard, per the rule above.

Steps 2 and 3 read bash "${CLAUDE_PLUGIN_ROOT}/scripts/check-dispatch-artifact.sh" … / check-coverage-complete.sh … and "Cite the exit status … not a reading of the directory, because the context most motivated to call the dispatch finished is the one that would be doing the reading." Step 1 is exactly that reading.

No grantgrep -c 'allowed-tools' over both SKILL.md files returns 0 at HEAD, while skills/explore/SKILL.md:50 and skills/research/SKILL.md:50 instruct the parent to run the gate through Bash.

The guard has no stepskills/research/SKILL.md:42 ends: "The memory root's self-ignoring .gitignore guard remains the agent's obligation, per its own contract." The gate at :44-58 grades the payload, the index, its sidecars and the coverage ledger. No step reads the guard.

Qualifications that must survive into any fix

  • B-F11 is a surface to verify, not a confirmed bypass. The auditor observed no denial and the packet records no gate invocation at all. Do not write the fix as if the gate is known to fail open in practice. Relevant docs caveat (skills page, fetched 2026-08-11): a skill's allowed-tools grant holds "during the turn that invokes the skill; the grant clears when you send your next message" — for a multi-turn parent that argues for settings-level allow rules too, not allowed-tools alone.
  • B-F6's original reading is superseded by the packet's own evidence-2.md. The bare-prose return that motivated it may have been a maxTurns: 30 turn-limit stop rather than a payload-contract violation — 46 tool calls inside a 30-turn budget is ordinary, and a turn-limit stop produces exactly the observed shape. The discriminator is unrecoverable. Remediation order changes accordingly: measure/raise explorer's maxTurns first (one line), then have the agent emit a skeleton payload early, and only then consider a check-payload-shape.sh. The existing mitigation ("Budget a turn for the payload") is prose the agent must self-schedule against a limit it has no way to observe.

Provenance

Severity: MED · Provenance: AUDITOR_VERIFIED
Origin: handoff-inbox items 2026-08-10-plugin-quality-audit-four-components (023241Z) and 20260811-021645-plugin-audit-four-components-and-guard-deadlock-ownership
Ledgers: .work/handoff-inbox-batch-4/ledgers/I7-four-components-023241Z.md § "Lane B — discovery 0.13.1" (B-F6, B-F8, B-F11) · .work/handoff-inbox-batch-4/ledgers/I9-021645-four-components.md § "Lane B — discovery / research" (D-F7)
Verified against origin/main 9b34a82a. discovery went 0.13.1 → 0.14.0 in #2222 (d6e4b452) after the ledgers were written at dacb8661; every anchor above is re-pinned at 9b34a82a and differs from the ledgers' line numbers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions