Skip to content

feat(discovery)!: dispatch explore and research to purpose-built subagents - #1425

Merged
kyle-sexton merged 29 commits into
mainfrom
feat/discovery-subagent-dispatch
Jul 26, 2026
Merged

feat(discovery)!: dispatch explore and research to purpose-built subagents#1425
kyle-sexton merged 29 commits into
mainfrom
feat/discovery-subagent-dispatch

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Makes /discovery:explore and /discovery:research dispatch a purpose-built plugin subagent by
default
, so exploration and research execution stop consuming the orchestrator's context window.
The main conversation gains a file pointer and a summary; the reading stays with the worker.

Four changes carry that:

  • Two agentsdiscovery:explorer and discovery:researcher, the plugin's first. Each
    preloads its skill through skills:, so the discipline arrives as content at turn zero rather
    than as a recollection the agent may or may not reach for. Neither declares memory, and that
    omission is load-bearing: declaring it auto-enables Edit regardless of the tools list, which
    would falsify each agent's own tool-honesty note.
  • A preload-liveness sentinel. A skills: entry that fails to resolve is skipped silently
    logged to the debug log and nowhere else — producing an undisciplined run that still writes an
    artifact and still reports complete coverage. That failure is indistinguishable from success at
    every other seam this design builds, so each skill carries a token the agent echoes back and the
    parent discards any run whose token is missing or mismatched.
  • Corpus enumeration + a scripted coverage gate. A new mandatory Phase 0 enumerates a bounded
    corpus into research-checklist.md before the first query, with a per-item depth criterion
    fixed at enumeration time. New outcome-gate criterion 11 cites the exit status of
    check-coverage-complete.sh rather than a reading of the table — the context most motivated to
    call a checklist finished is the one reading it.
  • Index-plus-sidecars, at every size. EXPLORE.md / RESEARCH.md are always an index with
    content in sidecars carrying a machine-readable header. Sidecar headers carry per-claim
    sources[] with url, tier, and pool — that is what makes the independent-corroboration
    criterion gradeable by a verifier that never saw the run.

Two structural consequences worth calling out for review:

  • The outcome gate grows an Owner column. Criteria where the run would judge the quality of
    its own choices move to a sibling verifier the parent dispatches; project fit stays with the
    parent, which alone holds the consuming project's conventions. A dispatched run returns
    verification: pending and renders no verdict on those rows.
  • /discovery:explore-deep is retired (BREAKING). Once /explore dispatches a named agent by
    default, the -deep variant is a second door onto an execution path the base skill already has.
    research-deep is deliberately kept — its heaviest tier needs Workflow and its multi-topic path
    needs Agent, neither reachable from a dispatched context, so that tier genuinely is a second
    execution path.

Three statements that preferred inline execution are overturned, not softened, because
dispatch-by-default contradicts them outright. Two were making a real point badly and are restated
in terms that hold in either posture: the run that judges a claim should be the run that read the
source, and summarization loss is bounded by what the artifact persists.

Both skills document an inline escape hatch and the three conditions under which it is correct —
tight turn-by-turn iteration, cost on a lookup too small to justify an envelope, and an invoking
context that is itself a subagent (hoisting). Running inline relaxes no discipline.

Plan and design contracts: docs/topics/discovery-subagent-dispatch/.

Test plan

Automated, all green locally:

  • bash scripts/run-plugin-tests.sh — full suite passes, including the new
    check-coverage-complete.test.sh (auto-discovered).
  • bash plugins/discovery/scripts/check-coverage-complete.test.sh — 19 cases.
  • shellcheck -x plugins/discovery/scripts/*.sh — clean.
  • check-skill.sh for all five discovery skills — 0 errors, 0 warnings each, up from three
    skills carrying one or two warnings before this branch. explore's 1 and setup's 5 tracked
    trigger phrases are preserved verbatim.
  • claude plugin validate plugins/discovery, check-changelog-parity.sh --check and
    --check-bump origin/main, validate-plugins.sh, check-skill-portability.sh origin/main,
    markdownlint over all changed paths — all exit 0.

Live runtime probe (the part no static check reaches). A real dispatch of
discovery:researcher, with the sentinel absent from the dispatch prompt so it could only have
arrived via preload:

  • skills: preload landed — the agent quoted the token verbatim.
  • Edit is genuinely absent from the agent's tool set — the tool-honesty note is a fact about the
    harness, not a claim about it.
  • The C2 payload returned with verification: pending and named its artifact.
  • The artifact set landed in the slice the envelope named: index + sidecar + coverage ledger.
  • Under a deliberately minimal budget the run reported status: truncated / coverage: partial and
    marked its claim MEDIUM rather than laundering a partial run into a complete-looking one.

The probe also caught two contract defects that static checks could not, both now fixed: the
run wrote its ledger with a prose Status column instead of the Done checkbox column the gate
parses, and improvised a sidecar header with one document-level confidence instead of per-claim
claims[]. Both because the detail lived only in spokes it never opened. The gate correctly failed
closed (exit 2) on the drifted ledger; SKILL.md now carries the exact ledger shape inline and
makes the sidecar-schema read imperative before the first sidecar is written.

TDD on the one piece of executable code: check-coverage-complete.test.sh was written first and
caught three real bugs — awk's exit inside a rule still runs END, which was overwriting a
fail-closed exit 2 with a pass (twice), and a row with extra columns shifted the Done position
undetected.


Contract (pruned with pointer)

docs/topics/<slug>/ is the contract tier — committed on the task branch as contracts lock, pruned before merge, with the contract pasted here in its place (docs/conventions/topic-docs/README.md, "Contract-slice lifecycle"). PLAN.md is 113 KB against a ~64 KB body cap, so per that same section this pastes the contract and references the rest: the Brief (the decisions a reviewer needs) and Phase 0's measured facts (what execution actually found) are below; the eight phase bodies, the two review passes, and their 28 dispositions are in the branch history at docs/topics/discovery-subagent-dispatch/PLAN.md before the pruning commit.

Actionable follow-ups graduated through the tracker seam: #1426 (apply the sweep to the remaining plugins — carries the full 138-row ledger in its body) and #1427 (the playbooks:fable-5 delegation conflict).

Brief — goal, constraints, the 14 ratified decisions, and acceptance criteria

Brief

TLDR

Make /discovery:research and /discovery:explore dispatch a purpose-built plugin subagent by
default instead of executing in main context. The subagent runs the full discipline, writes an
index-plus-sidecar artifact set into the topic's memory slice, and returns only a file pointer plus a
one-paragraph summary. Add a corpus-enumeration phase and a coverage-ledger artifact so bounded
corpora are exhaustively accounted for rather than skimmed. Then sweep the other 138 marketplace
skills for the same treatment.

Goal

Move exploration and research execution off the orchestrator's context window. The orchestrator's
job becomes routing file pointers between stages and deciding, per stage, whether it needs to read
an artifact at all. Artifacts are structured for progressive disclosure so any consumer — parent or
sibling agent — reads only the slice it needs.

Constraints

Version floor: Claude Code 2.1.219 (the version these facts were verified on). Every claim below
is version-gated in the source docs, and several are false on versions still in the field — record the
floor with the claims or the contract silently misleads:

  • Background as the default subagent execution mode — v2.1.198. Below it, the "background filter
    is the default filter" premise fails outright.
  • background: false on a context: fork skill — v2.1.218. Below it, forked skills always blocked
    the turn, so the escape hatch named below does not exist.
  • The narrow tool set applying to a backgrounded context: fork skill — v2.1.218.
  • CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH — from v2.1.172 through v2.1.216 subagents nested by
    default up to five layers and the limit could not be changed. On that range the env change recorded
    below is inert and unnecessary. It is correct on 2.1.219.
  • /subtask (the forked-subagent command) — v2.1.212; it was /fork before.
  • The skills: preload exclusion for bundled /verify and /code-reviewv2.1.215.

Harness facts verified against official docs this session
(https://code.claude.com/docs/en/sub-agents, https://code.claude.com/docs/en/skills,
https://code.claude.com/docs/en/hooks):

  • Subagent frontmatter fields: name, description, tools, disallowedTools, model,
    permissionMode, maxTurns, skills, mcpServers, hooks, memory,
    background, effort, isolation, color, initialPrompt.
  • skills: preloads full skill content at subagent startup. Requires the target skill to keep
    disable-model-invocation: false — all discovery skills qualify today. Preloading is not a
    restriction: the subagent can still invoke unlisted skills through the Skill tool.
  • Two tool filters narrow every subagent — except a conversation fork. The docs are explicit:
    "Forks skip both filters and receive the main conversation's exact tool pool," with Agent the
    only carve-out inside that exemption ("in a fork the tool stays listed but returns an error instead
    of spawning"). A fork IS a subagent, so every unconditional "in every subagent" claim below is
    scoped to non-fork subagents. This matters because history-fork is one of the four mechanisms
    in the audit vocabulary. Whether AskUserQuestion functions from a background fork is
    undocumented in either direction — do not assume it does.
    Filter 1 (every non-fork subagent): Agent (unless
    CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH is set), AskUserQuestion, Workflow, EnterPlanMode,
    ExitPlanMode, ScheduleWakeup, TaskOutput, WaitForMcpServers, EndConversation. Filter 2
    (background only, and background is the default) reduces built-ins to Read, Grep, Glob,
    Bash, PowerShell, Edit, Write, NotebookEdit, WebFetch, WebSearch, TodoWrite,
    Skill, ToolSearch, EnterWorktree, ExitWorktree, Monitor, TaskStop, SendMessage,
    Artifact, plus every MCP tool.
  • Workflow is unavailable in every non-fork subagent. research-deep's inline-dispatcher
    requirement is still correct, but rests on independent grounds — the skill states it itself
    (research-deep/SKILL.md:19) and its multi-topic path needs Agent, which errors even in a fork.
    The filter argument alone does not cover a fork.
  • AskUserQuestion is unavailable in every non-fork subagent. /explore's "surface open
    questions to the USER" contract degrades to text in the returned summary; the parent must
    re-surface them.
  • TaskCreate/TaskGet/TaskList/TaskUpdate do not survive the background filter for a
    subagent spawned through the Agent tool; TodoWrite does. Carve-out: teammates in agent teams
    additionally keep the task tools and the cron tools, so this is a property of the dispatch
    mechanism, not of dispatch as such — state the carve-out wherever the rule is applied, or it
    over-blocks any row whose only blocker is a task tool. Decision 5's "ledger must be a file" holds
    regardless, but on durability and parent-readability, not on this filter.
  • A context: fork skill is a regular agent type, not a conversation fork. It receives the
    narrow background tool set unless it sets background: false, which makes it block the invoking
    turn. Only the fork subagent type (/subtask, CLAUDE_CODE_FORK_SUBAGENT) inherits the
    parent's exact tool pool.
  • Plugin-shipped agents ignore the hooks, mcpServers, and permissionMode frontmatter
    fields.
    This costs less than it appears: settings.json hooks still fire inside subagent tool
    calls (the hook payload carries agent_id/agent_type precisely to distinguish them), subagents
    inherit the main conversation's MCP tools, and permission mode is inherited from the parent —
    a parent in auto mode yields a subagent in auto mode. Two further losses, previously
    understated:
    (1) permissionMode inheritance is total only under specific parent modes — under a
    parent in the shipped default mode a project or user agent can override to plan/acceptEdits/
    dontAsk/bypassPermissions and a plugin agent cannot; the earlier claim generalized from auto,
    the one parent mode where the loss is nil. (2) session-level hooks give observability inside a
    subagent (agent_id/agent_type in the payload) but not scopingPreToolUse matchers filter
    on tool name, so per-agent behavior must be branched inside the hook script, and frontmatter hooks
    have a cleanup lifecycle the settings form lacks.
  • Topic-docs placement: .claude/topic-docs.yaml is absent from this repo, so defaults apply —
    memory slice .work/<slug>/, contract slice docs/topics/<slug>/. Discovery writes memory tier
    only; that does not change.
  • Precedent: this marketplace already ships plugin subagents in review/ (6) and
    plugin-quality/ (1).

Environment change made this session: CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH: "5" added to
~/.claude/settings.json (chezmoi-tracked; backfill to melodic-software/dotfiles is owed). Env
vars are read at session start, so nested spawning is inert until the next session.

Decisions

# Decision Resolution
1 Default execution posture /research and /explore dispatch a subagent by default, with a documented inline escape hatch for tight turn-by-turn iteration. This deliberately overrides research/SKILL.md's current "prefer direct research when results inform decisions" guidance; summarization loss is bounded because the full artifact is on disk and the parent may read it on demand.
2 Mechanism Custom plugin subagents (discovery:researcher, discovery:explorer) with skills: preload, dispatched by the parent skill. Rejected: context: fork on the existing skills (no per-invocation prompt beyond $ARGUMENTS, silent narrow-tool-set trap) and dispatcher-only with a prose discipline reminder (discipline arrives as recall, not content).
3 Nested fan-out CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH is an optional detected capability, not a hard requirement. discovery:setup recommends it; without it the dispatched agent does its breadth sequentially — slower, same coverage. Same graceful-degradation shape as explore-deep's existing CLAUDE_CODE_FORK_SUBAGENT note.
4 Progressive disclosure RESEARCH.md / EXPLORE.md are always an index, with content in sidecars from the start — not only past the current ~2000-word overflow threshold. The index carries a task restatement, a one-line abstract per sidecar, and a section/claim → file+anchor table. Sidecars carry a machine-readable header (topic, claims, confidence) so a consumer can grep-then-read a single section.
5 Coverage discipline A new mandatory discipline in research/SKILL.md, its recipe in context/discipline.md, materialized as a research-checklist.md in the memory slice. Kept distinct from the existing Phase-1 numbered-gap list: that one chases unknowns, this one enforces exhaustive coverage of a bounded corpus. The file is mandatory because dispatched agents have no Task tools.
6 Corpus enumeration Gets its own Phase 0 — enumerate the corpus and write the checklist before any query. Sections, topics, subjects, and the per-item depth criterion are fixed up front and become the ledger; Phase 1 researches against it. Folding enumeration into Phase 1 lets breadth queries crowd it out, which is the speed-reading failure being fixed.

| 7 | Dispatch is the default posture; INLINE-ONLY is the claim needing justification | Preserving the MAIN agent's context is the primary goal of this effort. A skill that would flood main context dispatches unless there is a real reason it cannot. This inverts the sweep's original burden of proof and is what re-derived 15 of the original 57 INLINE-ONLY verdicts. |
| 8 | The orchestration boundary (the unifying principle) | The parent owns the pre-dispatch envelope — precomputed values, scope confirmation, intake answers, budget authorization, capability checks — resolved in main context and passed into the dispatch prompt. The agent owns a bounded middle: no load-time machinery, no user turn, no unresolved scope. The parent also owns the post-dispatch boundary: verification hand-off. A dispatched agent never verifies its own work. Three independent agents converged on the second half without seeing each other. |
| 9 | Nested spawning (supersedes Decision 3's degradation clause) | Classify every nested spawn by what it buys. Throughput nesting (N independent units, identical epistemic standing) — Decision 3 stands: absent nesting the agent goes sequential, slower with the same coverage. Independence nesting (a context that has not seen what the parent produced) — Decision 3's clause is false: self-critique is not a slower control, it is the absence of one. Remedy is neither the env var nor blocking dispatch: the dispatched agent does not run the step, returns verification: pending plus a verification request, and the orchestrator dispatches the verifier as a sibling. Independence is a property of context provenance, not spawn parentage — a verifier reading the artifact off disk has never seen the producing context, whoever spawned it. CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH reverts to a recommended optimization. |
| 10 | Dispatch and producer ≠ critic | Dispatch satisfies the requirement by construction under a criteria-only envelope; the dispatched agent then runs the checklist inline rather than re-dispatching, because it is the fresh pair of eyes. Hoisting, not nesting. Distinguish two guarantees: independence (who renders the verdict — dispatch buys it) from decorrelation (how many priors examine it — dispatch does not, and never claimed to; the remedy is a cross-vendor reviewer, orthogonal to dispatch). |
| 11 | Interactive gates — the three-cell test (supersedes the positional "mid-flow" wording) | Parameter question: answerable from intent, arguments, and the conversation before the run starts — never a blocker. Test: could the parent ask this without having read a single file the skill reads? Discovered question: existence or content depends on what the run finds — blocks only if it steers. Elicitation question: blocks when the answer IS the deliverable. Batches split because "mid-flow" was read positionally (inside the step list) rather than temporally. |
| 12 | Discipline delivery to a dispatched agent | Preload the existing discovery:research skill via the agent's skills: field. Author no new contract skill: the thin-mandate / heavy-reference split already exists in the file tree (SKILL.md carries the bars, phases, and outcome gate; context/discipline.md carries tier tables, recipes, calibration). Measured: preload injects the SKILL.md body only — supporting files stay on demand — and ${CLAUDE_PLUGIN_ROOT} expands on the preload path, so the pointer to the sibling arrives working at turn zero. The mandate must arrive guaranteed, not on request, because the skill exists to stop an agent doing less than the bars require. Dispatch the skill by name; never transcribe it into a prompt (that breaks ${CLAUDE_PLUGIN_ROOT} expansion, drops ! precompute, and freezes a drifting copy). |
| 13 | Dispatch granularity (revises Decision 1) | Phase-level. The dispatch unit is a phase or action; the whole-skill verdict is a roll-up, so the 138-row ledger survives as an index. No new frontmatter — two shipped declaration shapes suffice: an execution-site column on an action-router table, and per-step annotation plus a handoff contract. For /discovery:research: one dispatched span covering Phase 0 through Phase 4 and the outcome gate's mechanical criteria; parent keeps topic resolution at the front and presentation at the back; the gate's confidence criterion moves to a parent-dispatched sibling verifier; project-fit stays with the parent, which alone holds the consuming project's conventions. |
| 14 | -deep variants | Split, not symmetric. Keep research-deep — Tier 1 needs Workflow and the multi-topic path needs Agent, which errors even inside a true fork, so its heaviest tier is genuinely not runtime-selectable from a dispatched context. The governing convention's operative test is same execution path vs. a second execution path, not frontmatter-vs-runtime, so runtime dispatch does not void it. Retire explore-deep conditionally — relocate into plugins/discovery/agents/explorer.md, but only once that agent reproduces its project-memory loading and sidecar-on-collision behavior. plugins/discovery/agents/ does not exist yet. |

Amendments (ratified 2026-07-24)

  1. Decision 2 under-specified the discipline-delivery path. A plugin agent can receive its
    discipline via skills: preload or by granting the Skill tool and invoking at runtime. All
    seven agents this marketplace ships today use runtime invocation; none uses skills:. Whether
    !-precompute blocks, allowed-tools grants, and ${user_config.…} substitution fire under
    preload is unverified — the docs specify them for when a skill runs. Proposed: prefer
    runtime invocation, reserving skills: preload for skills with no load-time machinery.
  2. disable-model-invocation: true blocks BOTH paths, not just preload. It bars preloading and
    bars the Skill tool from invoking the skill at all. Skills carrying it are undispatchable
    without flipping the flag: dometrain:sync, education:teach, firecrawl:update,
    planning:questionnaire (sweep incomplete).
  3. A fourth mechanism exists. The Agent tool's subagent_type: "fork" is the only one inheriting
    the live transcript; plugin-agent and context-fork both discard it. Skills whose input is the
    conversation itself require it. It is rollout-gated by CLAUDE_CODE_FORK_SUBAGENT, degrades to
    stop rather than inline, and its cost scales with transcript length — inverting this Brief's
    context-saving premise on a long session.
  4. Decision 3's optional nesting has a correctness edge, not just a speed one. A skill that
    mandates its own fresh-context fan-out as a control (planning:plan Step 3, labelled "MANDATORY —
    never skip"; planning:audit-answers Step 2) silently degrades that step to inline self-critique
    when dispatched without CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH. Proposed: nesting is a hard
    prerequisite for dispatching that subset, optional elsewhere.
  5. Decision 4 is not universal. /education:quiz-me's report contract explicitly forbids linking
    .work/ or docs/topics/ paths — the sidecar shape. Index-plus-sidecars applies to
    artifact-producing skills, not to every dispatched skill.
  6. State the interactive blocker as the absence of a USER, not of AskUserQuestion. Several
    skills gate that tool behind a use_ask_user_question config defaulting OFF and fall back to
    inline prose rounds, equally unreachable from a subagent.
  7. ! precompute may optimize, never enable — fleet-wide, independent of dispatch. The managed
    setting disableSkillShellExecution: true disables ! substitution for plugin-sourced skills and
    cannot be overridden. Any plugin-shipped skill whose correctness depends on ! output is
    therefore broken in exactly the managed/enterprise posture this marketplace designs for. 64 of the
    138 non-setup skills carry ! precompute; each must be correct without it. A standing authoring
    rule, not a dispatch concern.
    7b. CLAUDE_CODE_FORK_SUBAGENT is a control, not a gate — the "rollout-gated" framing is stale.
    Forks have been enabled by default since v2.1.161; the variable now only forces on (1) or off
    (0), and the command is /subtask as of v2.1.212. So history-fork is less caveated than
    recorded, not more. The residual caveat that does stand: "Letting Claude itself spawn forks is
    experimental and may change in future releases." The same stale requirement is reproduced in
    shipped skill text at plugins/discovery/skills/explore-deep/SKILL.md:3 — which is doubly wrong,
    since it is also attached to the wrong mechanism (context: fork is not the fork subagent type).
  8. Discipline delivery: preload a thin contract, Read the bulk. Supersedes the earlier
    preload-vs-runtime framing, which posed a false binary — and the binary was false in a second way:
    plugin-quality:auditor uses neither path. Its tools list omits Skill entirely and it
    declares no skills:, so its discipline arrives as method in the agent body plus files it Reads
    from paths handed to it in the dispatch prompt. That third shape is immune to both the preload
    uncertainty and the disable-model-invocation block, and it is essentially what this amendment
    converges on. The six review/ agents do grant Skill, so "runtime invocation is the
    marketplace's proven default" survives; "all seven use it" does not. The documented supporting-files pattern is
    strictly better: skills: preloads ONE small always-applies contract skill (phase gates, outcome
    gate, non-negotiables; well under the 500-line guidance; authored to need zero !,
    ${user_config.…}, or allowed-tools), while the heavy multi-phase reference lives in sibling
    files read with Read at the phase that needs them. This beats runtime-invoking a heavy skill on
    two counts: the bulk no longer depends on the model electing to call Skill, and a phase-scoped
    file read costs less than landing a whole skill in context. Discovery is already shaped this way —
    skills/research/context/discipline.md is already a bundled reference — so this is a small delta,
    not a rewrite. Keep Skill in the agent's tools regardless. The contract skill must NOT set
    disable-model-invocation: true, which silently blocks preload; user-invocable: false is the
    correct flag for hiding it from the slash menu while keeping it preloadable.
    9a. Three preload behaviors now settled empirically (probe plugin loaded via --plugin-dir,
    headless -p, subagent defined with tools: [] and tool_uses: 0 so it could not have shelled
    out to forge the values):
    • ! shell substitution FIRES on the preload path, and its output is byte-identical to the
      normal Skill-tool invocation. Preload is not a degraded injection path. This retires the
      largest part of the risk — 36 of the 49 dispatch candidates had ! as their only concern.
    • allowed-tools grants DO NOT fire on the preload path, verified against a positive control
      (slash invocation permits the declared command, exit 127) and a no-skill baseline (identical
      refusal). Broader finding: the grant does not fire on the Skill-tool path either — in this
      build it appears honored only on slash invocation. So any skill relying on its own
      allowed-tools when invoked by a model rather than typed by a user is already silently
      degraded today, dispatch or not. 13 skills corpus-wide carry one.
    • ${user_config.…} remains INCONCLUSIVE. The placeholder survived literally on every path
      tested including slash invocation, so no positive control was achieved; the probe rig's
      --plugin-dir plugin likely has no resolvable pluginConfigs identity. A real verdict needs a
      marketplace-installed plugin with a confirmed-live value. 25 skills carry the substitution.
      9b. Two further preload behaviors settled empirically (probe against installed discovery v0.8.2
      via plugin-agent frontmatter; scripts and transcripts in the session scratchpad, run11/run12):
    • Preload injects the SKILL.md body ONLY. Supporting files under the skill directory stay
      on-demand — they are not dragged into the agent's startup context. This is the property that
      makes the recommended shape work: the mandate arrives guaranteed while the heavy reference stays
      lazy, so preload and progressive disclosure are not in tension.
    • ${CLAUDE_PLUGIN_ROOT} expands on the preload path. The pointer a preloaded skill carries to
      its sibling reference file therefore arrives as a working absolute path at turn zero, with no
      resolution step required of the agent.
      9c. Two preload behaviors remain undocumented and untested. Whether preload still fires when
      Skill is absent from tools or listed in disallowedTools (strongly implied by two doc
      sentences read together, never asserted); and whether preloaded content survives auto-compaction —
      the re-attachment budget is defined over "the most recent invocation", and a preloaded skill was
      never invoked. Amendment 8 is chosen so neither is load-bearing.
      Note that ! firing does not argue for preloading precompute: it fires at every spawn before
      the agent knows it needs the data, which multiplies under fan-out.

Acceptance criteria

  • Invoking /discovery:research <topic> with no other arguments dispatches a subagent; the main
    conversation gains a file pointer and a summary, not the research transcript.
  • The same holds for /discovery:explore <scope>.
  • Both skills document the inline escape hatch and the condition under which it is correct.
  • discovery:researcher and discovery:explorer agent definitions exist under
    plugins/discovery/agents/, preload their skill via skills:, and declare no
    hooks/mcpServers/permissionMode (ignored for plugin agents).
  • A research run over a bounded corpus produces research-checklist.md enumerating every corpus
    item with a per-item depth criterion and a completion mark, and the outcome gate fails when any
    item is unmarked.
  • RESEARCH.md and EXPLORE.md are indexes with per-sidecar abstracts and a section → file+anchor
    table, regardless of total size.
  • Sidecars carry a machine-readable header.
  • discovery:setup detects CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH and recommends setting it;
    absence degrades rather than blocks.
  • research-deep still dispatches Tier 1 from main context (Workflow is subagent-unavailable).
  • Open questions a dispatched agent surfaces are re-surfaced by the parent, since the agent cannot
    call AskUserQuestion.

Captured assumptions

  • Downstream consumers of RESEARCH.md/EXPLORE.md tolerate an index-plus-sidecar shape without
    code changes. Unverified — consumers across planning, implementation, and session-flow have
    not been audited. Flagged for the plan step.
  • .claude/topic-docs.yaml stays absent in this repo, so slice defaults hold.
  • Dispatch-by-default carries no cost ceiling the user wants enforced (no per-invocation token cap
    was requested).

Out of scope

  • Changing where discovery artifacts land (memory tier, .work/<slug>/) — unchanged.
  • Giving discovery agents contract-tier write access — unchanged.
  • Editing vendored skills under any vendor/ directory — upstream-owned.
  • The setup skills across the marketplace (39 of them) — install/config surface, never dispatch
    candidates.
Phase 0 — measured facts, and why the version moved twice

CLOSED 2026-07-25. #1260 merged at 22:58:13Z as squash 002bb380; this branch rebased onto it,
ahead 10, git merge-base --is-ancestor origin/main HEAD exits 0 after a fetch.

Second scope-change note — step 1 reverts from wait for MERGED to drive it myself, on the
user's instruction to complete the work rather than poll.
The babysit lane and this session both
pushed to feat/research-primary-source-ladder; the lane's last contribution was fdb0599717.
Every push after that is this session's. Racing was avoided by re-fetching immediately before each
push and re-reading the PR head before judging any finding — the discipline the prior session's
stale-head mistake earned.

#1260 took seven more Codex rounds to close, all fixed rather than waved through, because each
described a defect that changes what an agent following the recipe does. In order: the download
path's uniqueness was non-portable (BSD mktemp replaces only trailing Xs, so …-XXXXXX.<ext>
is an invalid template on macOS and && chaining meant curl never ran); a discovered URL was
interpolated into a double-quoted curl line, where $() and backticks expand — a P1 command
injection
in shipped guidance; the output path was named by content type before the response that
reveals the type existed; confirmation treated a content-type match as necessary, discarding a valid
PDF served as application/octet-stream; it also rejected any page carrying a cookie banner, which
ends in "unreachable" for a document already read; criterion 9 graded a rung off a probe while the
protocol demanded a fetch; the resulting three-outcome vocabulary could not express a rung that
legitimately does not exist, and the added probed-and-not-existing outcome then needed an evidence
bar of its own — it is now earned only against an exhaustive first-party surface, never a search
miss or a deliberately-partial llms.txt. curl -g was added alongside: {} and [] are legal URL
characters curl expands regardless of shell quoting.

Recorded facts, measured post-rebase — these govern, not the plan's projections:

Fact Projected Actual
jq -r .version plugins/discovery/.claude-plugin/plugin.json 0.8.3 0.8.5
Outcome-gate table rows (scoped awk) 10 10
Five artifact-protocol.md md5s collapse to 1 1
Tier-3 subagent-return sentence :148 :150

The version moved twice, not once. #1260 shipped 0.8.4 at plan time, but main released its
own 0.8.4 (the setup schema-reference and byte-identity notes) while #1260 was open, so resolving
that conflict moved #1260's entries to 0.8.5. Phase 6's target is therefore 0.9.0, derived
from 0.8.5 exactly as the plan requires — never hardcoded.

Two premises re-verified against the rebased tree, both unchanged:

  • feat(skill-quality): fresh-eyes delegation doctrine + conformance gate (check 21) #1096 has still not merged. docs/PLUGIN-PHILOSOPHY.md on main carries no
    Delegation mechanics, named-agent bar, or fresh-eyes text; check-skill.sh has no check 21;
    plugins/skill-quality/skills/check/ still holds only SKILL.md and evals/. The grounding
    table's aspirational, not grounded marking stands, and Phase 2 item 7's "do not write the skill
    text as though the exemption is claimable" stays binding.
  • Both preload targets remain preloadableresearch/SKILL.md:6 and explore/SKILL.md:6 are
    each disable-model-invocation: false. Amendment 2 makes this load-bearing: true would block
    preload and the Skill tool, and it fails silently, which is the whole reason C2 carries
    preload_token.

One sanity check in a later phase is stale and is corrected here rather than left to fail.
Phase 2 asserts bash scripts/check-skill-portability.sh --all exits 0. It exits 1 on unmodified
main
, on three pre-existing origin/(main|master) couplings in plugins/playbooks and
plugins/work-items — none in discovery, none introduced here. CI does not run --all: it runs
scripts/check-skill-portability.sh "origin/$BASE_REF" (.github/workflows/ci.yml:678), the
changed-paths form. The base-ref form is the binding gate for this branch; the three standing
couplings are someone else's surface and go to Phase 7 as a tracker candidate, not fixed here.

Design contracts C1–C5 — the cross-boundary shapes this PR implements

Contract spec — discovery-subagent-dispatch

The five cross-boundary contracts this design owes. Companion to
design-threads.md; the ratified decisions live in ../PLAN.md
and are not restated.

Upstream harness facts are pointed at, never restated — per issue #1225's locked pointer-not-copy
rule, the link index is docs/OFFICIAL-DOCS.md and the canonical page
for every frontmatter field below is https://code.claude.com/docs/en/sub-agents.

C1 — Agent-definition frontmatter contract

Two definitions under plugins/discovery/agents/. Shape follows the seven already shipped in
plugins/review/agents/ and plugins/plugin-quality/agents/ — quoted tools string, memory: local,
explicit effort and maxTurns — with one addition none of them use: skills:.

Amendment 2026-07-25 (adversarial stress-test, findings F1/F2 + assumption 5). The frontmatter
below is the corrected form. Three changes from the ratified version, each forced by a verified
upstream fact:

  1. memory: local REMOVED from both. Verified verbatim at
    https://code.claude.com/docs/en/sub-agents: "Read, Write, and Edit tools are automatically
    enabled so the subagent can manage its memory files." Declaring memory therefore re-enables
    Edit regardless of tools, which falsified this contract's own "Edit is absent by
    design
    " claim and destroyed the tool-cage argument entirely. Corroborated in-repo:
    plugin-quality:auditor — the one shipped agent that carries Write — declares no memory;
    the six review/ agents that do declare it are all read-only. Removing it also resolves the
    independent objection that an accumulating MEMORY.md is a Tier-3-recall laundering channel
    inside a skill whose core rule is that training-data recall is inadmissible.
  2. skills: is a YAML list of bare names, not a quoted scoped string. The only documented
    example is a list (skills:\n - api-conventions). The scoped-string form was never
    documented, and a wrong value form fails silently (see 3). The exact resolvable name is an
    empirical question closed before Phase 1, not during it.
  3. A missing or disabled preload is silent. Verified verbatim: "If a listed skill is missing or
    disabled, Claude Code skips it and logs a warning to the debug log." Nothing surfaces at
    runtime. C2 therefore gains a mandatory preload-liveness sentinel.
---
name: researcher
description: "…dispatched by /discovery:research; not intended for direct ad-hoc use."
tools: "Read, Grep, Glob, Bash, WebFetch, WebSearch, Write, Skill"
skills:
  - research          # exact resolvable form verified empirically before Phase 1
model: inherit
effort: high
maxTurns: 40
---
---
name: explorer
description: "…dispatched by /discovery:explore; not intended for direct ad-hoc use."
tools: "Read, Grep, Glob, Bash, Write, Skill"
skills:
  - explore           # exact resolvable form verified empirically before Phase 1
model: inherit
effort: high
maxTurns: 30
---

Field rationale:

  • toolsEdit is absent, and that absence is real only because memory is not declared
    (see the amendment above). What the cage actually buys: no single-call in-place mutation of an
    existing repo file. What it does not buy: read-only status, or mechanical enforcement of the
    memory-tier invariant — Bash and Write both write, per the doctrine's own tool-cage wording.
    State it that way in the agent body; do not call the agent read-only. Skill stays regardless of
    skills: preload, per Amendment 8.
  • skills — Decision 12. research/SKILL.md was verified preload-suitable this session: 187
    lines, one trivial ! precompute (git branch --show-current), zero allowed-tools, zero
    ${user_config.…}. Amendment 9b's measured behavior applies — the SKILL.md body only, with
    ${CLAUDE_PLUGIN_ROOT} expanded, so context/discipline.md stays lazy.
  • model: inherit — stated explicitly rather than omitted. A research verdict is consequential,
    and the doctrine's ladder requires session tier or above; inherit guarantees exactly session
    tier and never below. A hard pin would fight CLAUDE_CODE_SUBAGENT_MODEL and the doctrine's own
    dated tier table, which carries a recheck trigger this design should not duplicate.
  • hooks / mcpServers / permissionMode — omitted. Plugin-shipped agents ignore all three.
  • memoryomitted deliberately, and the omission is load-bearing. See the amendment above.
  • background — omitted; background is the default from v2.1.198 and is wanted here.

Named-agent bar conformance (PR #1096): multi-site is satisfied — the same worker dispatches
from /discovery:research and from the marketplace's DISPATCH-DEFAULT rows. The second conjunct is
argued from the tool cage, not met — the cage narrows the write surface without enforcing the
memory-tier invariant, and the F1 amendment makes clear how close that argument came to collapsing
outright. The genuinely load-bearing capability is skills: preload, which the bar does not
enumerate; the upstream proposal to admit it as a third qualifier rides on #304 / #1096. Do not
overstate this conjunct anywhere downstream.

C2 — Return payload (verification request)

An instance of issue #496's return-payload contract: identifiers plus verdict plus parked payload,
never the transcript. The agent returns one fenced YAML block followed by at most one paragraph of
prose.

preload_token: <sentinel>     # F2: echoed verbatim from the preloaded skill; absence = hard failure
status: complete              # complete | truncated — F3: written before the turn budget runs out
artifact: .work/<topic-slug>/RESEARCH.md
sidecars: 3
coverage: complete            # complete | partial — mirrors the C4 ledger's gate
verification: pending         # pending | not-required
verification_request:
  target: .work/<topic-slug>/RESEARCH.md
  criterion: "every accepted claim is HIGH confidence"   # the gate row the producer may not self-grade
  worker: fresh-context subagent
open_questions:               # the agent cannot call AskUserQuestion; the parent re-surfaces these
  - "<question>"

preload_token — the preload-liveness sentinel (amendment 2026-07-25, finding F2). A missing or
disabled skills: entry is skipped silently, logging only to the debug log — verified verbatim
at https://code.claude.com/docs/en/sub-agents. Without a liveness signal, a preload miss produces
an undisciplined run that still writes an artifact and still self-reports coverage: complete: the
failure mode is indistinguishable from success at every seam this design builds, which is precisely
the guarantee Decision 12 exists to provide. The preloaded skill therefore carries a sentinel string
the agent must echo verbatim into this payload. The parent treats a missing or mismatched
preload_token as a hard failure and discards the run
— it does not downgrade, warn, or accept the
artifact. This is the one seam that makes "the mandate arrived" observable rather than assumed.

status — the truncation contract (amendment 2026-07-25, finding F3). maxTurns has no
documented partial-return semantics: the docs define it only as "Maximum number of agentic turns
before the subagent stops." Because this design writes the ledger and sidecars incrementally, a
turn-limit stop otherwise leaves a half-marked ledger, orphan sidecars, and an index naming files
that were never written — with no payload at all, so the parent never learns the run died. Rules:
the agent writes status: truncated plus a partial payload before its budget is exhausted; a
dispatch that returns no payload is treated as truncated-without-warning; in both cases the
parent discards the partial slice rather than resuming it, because a half-run ledger cannot be
distinguished from a complete one by the coverage script alone.

verification: pending is Decision 9's non-negotiable: the producing agent renders no verdict on
its own confidence criterion. The orchestrator dispatches the verifier as a sibling, which is
why nesting stays an optimization rather than a correctness prerequisite.

Outcome-gate row assignment

Decision 13 splits the gate three ways but does not enumerate it. The split rule: a row the producer
can read off an artifact stays with the producer; a row where the producer would judge the
quality of its own choices
goes to the sibling verifier; a row needing the consuming project's
conventions stays with the parent, which alone holds them.

Row Criterion (abbreviated) Owner
1 Tier 0/1 source captured this turn producer
2 No claim row is all Tier-2 producer
3 Every Phase 2/3 query traces to a numbered gap producer
4 ≥2 independent corroborators, not one upstream pool verifier
5 Falsification query ran and is recorded producer
6 Recency gate satisfied producer
7 Every accepted claim is HIGH confidence verifier
8 Project fit against the consuming project's conventions parent
9 Fetch log shows the topmost existing rung (PR #1260) producer
10 Every absence names checked and unchecked sets (PR #1260) producer
Coverage ledger fully marked (C4, new) producer, script-verdict

Row 4 lands with the verifier rather than the producer despite looking mechanical: "independent"
is a judgment over the source set the producer itself assembled, which is the same self-grade class
as row 7.

Amendment 2026-07-25 (finding F7) — row 4 was not gradeable as specified. The verifier reads the
persisted artifact and has never seen the run, and the skill's own gate demands every criterion be
read off an artifact. Row 7 clears that bar: C3's header carries claims[].confidence. Row 4 —
"≥2 independent corroborators, not one upstream pool" — did not, because C3 persisted only
tiers: [0, 1], which encodes neither independence nor pool provenance. C3's header is therefore
extended (below) to carry per-claim source URLs with independence attribution. Without that
extension the only honest alternative was to move row 4 back to the producer as a declared
self-grade; the extension is preferred because row 4 is the corroboration bar the whole discipline
rests on. Rows 9 and 10 are counted here because PR #1260 is changing this table's row count before
this design is implemented — the assignment is written against the post-merge table deliberately.

Open tension this contract must resolveresearch/SKILL.md:148 currently reads: "Subagent
returns are Tier 3 (synthesis), not corroborators, until their cited primaries are fetched this
turn."
Read literally, dispatch-by-default demotes every research run to Tier 3, because the
orchestrator's view of the work IS a subagent return. The rule was written against an ad-hoc
subagent summary
, not against a dispatched agent that ran the full discipline and captured every
primary URL into the artifact. The tier belongs to the artifact and its captured sources, not to
the transport. The rule needs a scoped exception naming the discipline-running dispatched case;
without it, Decision 1 contradicts the skill it is modifying. This is a required edit to
research/SKILL.md and therefore lands in PR #1260's collision surface.

C3 — Sidecar header schema

YAML frontmatter, chosen over an HTML comment block: it parses with tooling the repo already runs
and matches the skill/agent frontmatter convention used marketplace-wide. Vocabulary is reused,
not invented
HIGH | MEDIUM | LOW and Tier 0..3 are the research skill's own
(SKILL.md:42, context/discipline.md:9-12).

---
topic: <topic-slug>
section: <stable kebab-case id, matches the index anchor>
abstract: <one line, mirrored verbatim into the index>
claims:
  - claim: "<one-line claim>"
    confidence: HIGH          # HIGH | MEDIUM | LOW
    tiers: [0, 1]             # source tiers backing this claim
    sources:                  # F7: row 4 is ungradeable without provenance
      - url: "<url fetched this turn>"
        tier: 1
        pool: "<publisher/org — two sources sharing a pool are NOT independent>"
produced_by: <phase id>
---

The index (RESEARCH.md / EXPLORE.md) carries a task restatement, the per-sidecar abstract
verbatim, and a section → file+anchor table. A consumer greps headers, then reads exactly one
sidecar.

Amendment 5 scopes this: index-plus-sidecars applies to artifact-producing skills, not to every
dispatched skill.

C4 — Coverage ledger (research-checklist.md)

Written in Phase 0, before any query. One row per corpus item.

| # | Corpus item | Depth criterion | Done |
|---|-------------|-----------------|------|
| 1 | <item>      | <what counts as covered for THIS item> | [ ] |

The depth criterion is fixed at enumeration time and is per-item, not global — that is what
Decision 6 buys by giving enumeration its own phase.

Gate is a script, not a model read. A check-coverage-complete.sh returns non-zero when any
Done cell is unmarked; the outcome-gate row cites the script's exit status. Two consequences:
the verdict is deterministic and survives a context that wants to be done, and the row earns check
21's deterministic-gate exemption class honestly rather than needing a delegation declaration.

C5 — Execution-site declaration

Decision 13 already fixed the shapes — an execution-site column on an action-router table, and
per-step annotation plus a handoff contract. No new frontmatter. This contract fixes only the
enforcement.

Extend the existing declaration contract owned by
plugins/skill-quality/skills/check/reference/fresh-eyes-declarations.md (PR #1096, check 21)
rather than standing up a second scanner. Check 21 declares who judges; this adds where a phase
executes
— an adjacent proposition over the same greppable, deterministic substrate:

  • Closed value set for the execution-site column: dispatch | inline | either.
  • Exemption directive reuses the established grammar and comment form, with its own closed class
    set and a mandatory -- <reason>, per the ESLint-description precedent that contract cites.
  • Spec ownership stays with skill-quality; discovery is a consumer, not a second spec owner.

This is an upstream contribution to #304's program, sequenced after #1096 merges.

Related

No linked issue.

Context this work touches, none of which it closes:

Builds directly on #1260, merged earlier today; this branch is rebased onto it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QExBnEHWTRCzUUxFY4t5Dz

kyle-sexton and others added 18 commits July 25, 2026 19:52
Locks the task contract for moving /discovery:research and
/discovery:explore off the orchestrator's context window, and records the
harness constraints that decide the mechanism.

Six decisions locked: dispatch-by-default with an inline escape hatch;
custom plugin subagents over context: fork; nested fan-out as an optional
detected capability; index-plus-sidecars from the start rather than past a
size threshold; a mandatory coverage-ledger discipline; and a Phase 0
corpus enumeration ahead of any query.

Constraints verified against official docs this session — the two subagent
tool filters, Workflow and AskUserQuestion being unavailable in every
subagent, the Task tools not surviving the background filter, and that a
context: fork skill is a regular agent type on the narrow background set
rather than a conversation fork.

Six amendments pending confirmation come from a completed sweep of all 138
non-setup marketplace skills: prefer runtime Skill invocation over skills:
preload (no shipped agent uses preload, and whether !-precompute fires under
it is unverified); disable-model-invocation blocks both dispatch paths; a
fourth mechanism exists for transcript-bound skills; optional nesting has a
correctness edge, not only a speed one; index-plus-sidecars is not universal;
and the interactive blocker is the absence of a user, mid-flow, rather than
of a tool.

The sweep's evidence is memory-tier under .work/ and is not committed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BiZPDNivFJorpvhbDJDwt2
…nt review

Six agents reviewed the brief and the marketplace sweep independently: an
empirical probe of preload semantics, a documentation research pass, two
blind re-audits of 49 skills, an adversarial audit, and a re-test of 15
rows a coverage gap had left unchecked.

Records a version floor of 2.1.219. Every Constraints claim is
version-gated upstream and several are false on versions still in the
field: background-as-default needs 2.1.198, without which the
background-filter premise collapses; forked-skill background: false needs
2.1.218; and from 2.1.172 through 2.1.216 subagents nested by default with
an unchangeable limit, making the spawn-depth setting inert there.

Corrects four overstated claims. The tool filters do not apply to
conversation forks, which receive the parent's exact pool - so every "in
every subagent" quantifier is scoped to non-fork subagents, and the
history-fork mechanism no longer contradicts the constraints block. The
Task-tool blocker gains its agent-teams carve-out. Plugin agents lose
permissionMode override under a default-mode parent, not only an inline
MCP server. The CLAUDE_CODE_FORK_SUBAGENT rollout gate has not existed
since 2.1.161.

Settles three preload behaviors empirically. Shell precompute fires on the
preload path, byte-identical to normal invocation, retiring the largest
part of the risk. allowed-tools grants do not fire there, nor on the Skill
tool path - only on slash invocation, so any skill relying on its own
grant under model invocation is already degraded. user_config substitution
remains inconclusive for want of a positive control.

Replaces the preload-versus-runtime binary, which was false twice over:
the documented supporting-files pattern is better than either, and the one
agent held up as prior art uses neither path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BiZPDNivFJorpvhbDJDwt2
Measured against installed discovery v0.8.2 through plugin-agent
frontmatter, not inferred.

Preload injects the SKILL.md body only; supporting files under the skill
directory stay on demand rather than being pulled into startup context.
That is the property the recommended delivery shape depends on - the
mandate arrives guaranteed while the heavy reference stays lazy, so
preload and progressive disclosure are not in tension.

CLAUDE_PLUGIN_ROOT expands on the preload path, so a preloaded skill's
pointer to its sibling reference file arrives as a working absolute path
at turn zero with no resolution step asked of the agent.

Two behaviors remain untested: whether preload fires when Skill is absent
from tools, and whether preloaded content survives auto-compaction.
Neither is load-bearing for the chosen shape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BiZPDNivFJorpvhbDJDwt2
… questions

Each open question was assigned a dedicated subagent, blind to the
orchestrator's own recommendations. All eight returned verdicts that
overturned or improved on them; in five of eight the defect was the
framing rather than the facts. Decisions 7-14 record the outcomes.

Dispatch becomes the default posture and INLINE-ONLY the claim needing
justification. That inversion re-derived 15 of the original 57 inline
verdicts, and the dominant cause was one systematic defect: multi-action
skills graded on their heaviest action, so a mutating action set the
verdict while a read-only sibling in the same skill carried no blocker and
held nearly all the context volume.

The orchestration boundary is now stated at both ends. The parent owns the
pre-dispatch envelope and also verification hand-off; a dispatched agent
never verifies its own work. Three agents converged on that second half
independently. It supersedes the nesting-degradation clause: independence
is a property of context provenance, not spawn parentage, so a verifier
reading the artifact off disk is fresh regardless of who spawned it, and
the spawn-depth variable reverts to an optimization.

Interactive gates get a three-cell test replacing the positional
mid-flow wording that had caused batches to split on identical shapes.
Dispatch granularity moves to phase-level with the skill verdict as a
roll-up. Discipline reaches a dispatched agent by preloading the existing
skill rather than authoring a new one, since the thin-mandate over
heavy-reference split already exists in the file tree.

The -deep retirement resolves as a split rather than the symmetric answer
first proposed: research-deep is kept because its heaviest tier is not
runtime-selectable from any dispatched context, and explore-deep retires
only once its replacement agent reproduces the behavior it would delete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BiZPDNivFJorpvhbDJDwt2
Thirty-two inline verdicts had been upheld on inherited batch rationale
rather than read against the skills' own text. All thirty-two are now
opened in full: three changed, twenty-eight upheld with their blocker
restated in the criteria's own terms, and one tested as a challenge and
withdrawn. Every inline verdict in the ledger has now been read against
primary text.

The close-out also found a second bookkeeping gap in the coverage claim:
the normalization buckets account for forty-seven of fifty-seven rows, and
one batch of ten sits in none of them. Those ten read as though the amended
criteria were applied - they cite the fourth mechanism by name and invoke
its cost-inversion clause - and the ledger's own transcription note records
that batch revising a row after the amendment. Worked post-amendment,
never credited. Recorded rather than papered over.

Final distribution after ratification and close-out: dispatch-default 22,
dispatch-optional 44, inline-only 39, no-change 33. Inline-only fell from
fifty-seven to thirty-nine under the inverted burden of proof.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BiZPDNivFJorpvhbDJDwt2
…n contracts

The Plan section lands beneath the existing Brief: eight phases with
mechanically-verifiable Sanity Checks, blast radius, test strategy,
execution shape with scope fences and a sequential fallback, and the
dispositions for all twenty-eight findings from the two review passes.

The design contracts are tracked rather than left as worktree-local
scratch. contract-spec.md carries three amendments forced by upstream
facts verified against the official subagent docs: memory is dropped
from both agent definitions because declaring it re-enables Edit
regardless of the tools list, which falsified C1's tool-cage claim; C2
gains a preload_token sentinel and a status field because a preload miss
is silent and would otherwise produce a confident undisciplined run; and
C3's header gains per-claim sources with url, tier, and pool, without
which the sibling verifier cannot grade the independence criterion from
the persisted artifact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both gaps closed against a live rig rather than argued: a throwaway probe
plugin, three agent files with identical bodies and no tools, sentinels
absent from every dispatch prompt so nothing but preload could supply them.

Both skills: reference forms resolve for an agent preloading a skill in
its own plugin - the bare leaf name and the plugin-scoped string alike -
so C1's frontmatter is valid as written. The scoped form ships, being the
one with evidence on the same-plugin and cross-plugin paths both.

$ARGUMENTS is substituted with the empty string on the preload path, not
left literal. That is worse than the plan assumed: research/SKILL.md line
24 reaches a dispatched agent as an instruction with nothing after the
colon, invisible rather than recognizably unfilled. The topic has to
arrive in the dispatch prompt, and the agent body's refuse-to-guess rule
is what turns a missing one into a stop instead of an invention.

The negative control earned its place. An agent declaring a skill that
does not exist started anyway and reported no skill content, with nothing
surfaced to the dispatching parent. F2's silent-miss premise is now
reproduced on demand, which moves C2's preload_token sentinel from a
precaution against a documented behavior to the detector for an observed
one.

Phase 0 step 1 also changes from merge to wait: a standing babysit_loop
worker lane is driving #1260 and pushed three commits during this session.
Racing it would truncate its work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s beyond explore

Ten behaviors, read against the skill file rather than recalled, with the
site for each. Pulled forward out of Phase 5 because Decision 14's
retirement gate asks whether explorer.md reproduces them and explorer.md
is authored in Phase 1. Finding the list only at Phase 5 would have meant
either rewriting the agent or blocking retirement on work Phase 1 could
have done in the first pass.

Two of the ten are the gate proper: path-scoped rules loaded explicitly,
since a subagent does not auto-load them, and the sidecar diversion when
an unrelated EXPLORE.md already occupies the slot.

Three do not transfer as written. The empty-scope orientation pass is a
mode that only makes sense when scope arrives through $ARGUMENTS, which
work item 0 showed is empty under preload; a dispatched agent without
scope is a parent-envelope failure, so the refuse-to-guess rule replaces
it. The precompute block stays behind under Amendment 9c. And the
read-only note has to name two permitted write targets, not one - the
artifact and the memory root's self-ignoring .gitignore guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ot touch

The mechanical sweep replaces hand enumeration for every discovery skill
outside research/. All four are absent from #1260's five files, so their
line numbers survive the Phase 0 rebase and the sweep did not have to
wait on the merge.

explore carries thirteen hits: eleven edits, one correct under dispatch,
and line 108 - a second $ARGUMENTS site that no hand pass had found,
including this plan's own. That is the third time the method has caught
what enumeration missed in this file.

setup returns nothing on any of the six pattern classes, so its phase is
purely additive. research-deep's every hit is the skill arguing its own
inline requirement, which is what acceptance criterion 9 asks to survive
rather than something to reconcile. blindspot's intake question is a
genuine elicitation gate under the three-cell test, so its NO-CHANGE
verdict now rests on a reading instead of on inheritance.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…on evidence

The Brief carried "downstream consumers tolerate an index-plus-sidecar
shape without code changes" as explicitly unverified. Both pre-flights
have now run.

No parser of the outcome-gate table exists anywhere in scripts/ or the
workflows, so Phase 2's new row and the ownership column break no gate.
Across forty EXPLORE.md references and every cross-plugin RESEARCH.md
reference, nothing reads either artifact's section structure - they are
naming references, changelog history, sibling hand-off pointers, and two
test fixtures using the filename as an arbitrary path token.

The one coupling with teeth turned out already handled: the documented
.worktreeinclude recipe globs EXPLORE-*.md and RESEARCH-*.md alongside
the index, and *-checklist.md alongside those. Had it globbed the index
alone, Decision 4 would have shipped a worktree copy of pointers to files
that never came along - strictly worse than the self-contained artifact
it replaces.

One shape is recorded as unaffected but fragile: youtube-digest's
check-research-complete.js asserts a minimum length on a RESEARCH.md in
its own slice. Discovery does not write that slice, so nothing breaks
here, but a min-length gate is precisely what an always-an-index artifact
fails. It belongs to the sweep topic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#1260 merged as 002bb38 and this branch is rebased onto it. Records the
four post-rebase values the later phases derive from, all measured
rather than projected: version 0.8.5 (not the 0.8.3 the plan expected —
main shipped its own 0.8.4 while #1260 was open, so #1260's entries
moved up), gate table 10 rows, artifact-protocol md5s collapsing to one,
Tier-3 sentence at :150.

Also records the second scope change (this session drove #1260 rather
than polling for it), the seven Codex rounds it took to close and why
each was fixed rather than waved through, the two re-verified premises
(#1096 still unmerged, both preload targets still preloadable), and one
stale Phase 2 sanity check corrected at source: --all exits 1 on
unmodified main, and CI runs the base-ref form instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI lints **/*.md, so these would have failed the hygiene job the moment
this branch opened a PR. All five predate this session's edits.

Phase 2's work items numbered 1,2,3,4,3,4,5..12 — a duplicated pair that
left every item after it labelled two lower than its position. Phase 3's
started at 0 with 0b and 3b interleaved. Both are renumbered to run
straight, and the four cross-references that named the old labels move
with them rather than being left pointing at a different item.

Also: a paragraph opening with "#1260" parsed as an ATX heading, a
trailing blank line, and a stray space inside a code span in Phase 1's
frontmatter assertion — rewritten to describe what the check actually
asserts, since the inline snippet was never runnable as written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two workers /discovery:research and /discovery:explore dispatch to,
so exploration and research execution leave the orchestrator's context
window. Frontmatter is C1 as amended: the plugin-scoped skills: preload
form settled empirically in Phase 1 work item 0, model: inherit stated
rather than omitted, and no memory: — declaring it would auto-enable
Edit regardless of tools and falsify each agent's own tool-honesty note.

Both bodies carry the pre-dispatch envelope contract and refuse to guess
an unresolved scope, the preload-liveness sentinel protocol, the C2
return payload with verification: pending non-negotiable, the
truncation contract, open questions returned as text because
AskUserQuestion is filtered out of every non-fork subagent, and the
hoisting rule. explorer.md additionally reproduces the behaviors
Decision 14 makes explore-deep's retirement conditional on.

Tool honesty is stated at its true strength in both: Bash and Write are
not read-only, and Edit's absence buys no in-place mutation of an
existing repo file — not read-only status, and not enforcement of the
memory-tier boundary, which holds by instruction.

PLAN.md records why the end-to-end runtime probe moves to after Phase 3:
it grades a sentinel the preloaded skills do not carry until Phases 2
and 3 author it, so running it here would fail against agents behaving
exactly as specified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two changes to /discovery:research that share one premise — the run
should not be the sole judge of itself, and the orchestrator should not
pay for its reading.

Dispatch posture. From the main conversation the skill now dispatches
discovery:researcher; the main conversation gains a file pointer and a
summary rather than the research transcript. Three documented conditions
send it inline instead — turn-by-turn steering, cost on a lookup too
small to be worth an envelope, and an invoking context that is already a
subagent (hoisting: the outer dispatch supplied the fresh context, so
the inner hop only spends the inner window). Inline relaxes nothing.

The three statements that preferred inline are overturned rather than
softened, because dispatch-by-default contradicts them outright: the
research-deep framing at :18, the direct-context-web preference at :65,
and the main-context-vs-agent tradeoff at :126. Two of them were making
a real point badly — the run that judges a claim should be the run that
read the source, and summarization loss is bounded by what the artifact
persists — so both are restated in terms that hold in either posture.

Coverage. A new mandatory Phase 0 enumerates a bounded corpus into
research-checklist.md before the first query, with a per-item depth
criterion fixed at enumeration time, and gate criterion 11 cites the
exit status of check-coverage-complete.sh rather than a reading of the
table. This is distinct from the numbered gap list: gaps chase what
searching surfaced, the ledger enforces what was knowable up front. The
enumeration surface must be exhaustive by construction — a ledger built
from search results certifies the blind spot it exists to close.

The gate also grows an Owner column. Criteria 4 and 7 ask the run to
judge the quality of its own choices, so they move to a sibling verifier
that never saw the run; criterion 8 needs the consuming project's
conventions and stays with the parent. Sidecar headers carry per-claim
sources[] with url, tier, and pool, which is what makes criterion 4
gradeable from the artifact at all — independence is a property of
publishing pools, and a bare tier list encodes neither.

T8 is resolved with a scoped exception: the Tier-3 rule for subagent
returns targets an ad-hoc summary with no captured primaries, and does
not reach a run that executed this discipline and wrote every primary
URL into the artifact. The tier attaches to the artifact and its
captured sources, never to the transport.

check-coverage-complete.sh is TDD'd against 19 cases and fails closed:
exit 2 for a ledger it cannot parse — missing, empty, no table, no Done
column, a row whose width does not match the header, or a Done cell that
is neither marked nor unmarked. Two bugs the tests caught: awk's exit
inside a rule still runs END, which was overwriting a fail-closed exit 2
with a pass, and a row with extra columns shifted the Done position
undetected.

Both pre-existing skill-quality warnings are cleared, and the reference
material displaced to keep the body under the soft line target lands in
four progressive-disclosure spokes rather than being deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… back

Mirrors the research posture on /discovery:explore, against the 13-row
mechanical sweep recorded in PLAN.md rather than a hand list — the sweep
is what found the :108 site that three hand passes over this file had
missed.

The routing section inverts: discovery:explorer is the default from the
main conversation, with the same three inline conditions, and the
built-in Explore subagent and /explore-deep kept as named alternatives.
The old text's claim that the main session synthesizes and persists the
artifact was true only of built-in Explore agents, which cannot write
one; discovery:explorer writes its own, so the sentence now says which
is which instead of asserting the general case.

Four sites instructed something a dispatched run cannot do, each
reworded to keep the rule and change only the hand-off:

- Open questions were "surfaced to the USER" in the output format and
  the outcome gate. AskUserQuestion is filtered out of every non-fork
  subagent, so they now return as open_questions for the parent to
  surface. The anti-pattern being guarded — silent downstream
  resolution — is unchanged.
- Missing files required asking the user before git archaeology. A
  dispatched run cannot ask, so proceeding would silently violate the
  rule that protects a deliberate deletion; it records the question
  instead.
- Plan mode is scoped to the inline path, since EnterPlanMode and
  ExitPlanMode are Filter-1 tools.
- $ARGUMENTS at :40 and the exploration-modes table at :108 both assumed
  a value that is empty under preload; scope now arrives in the dispatch
  prompt, with no unscoped orientation mode — a general sweep would
  answer a question nobody asked.

EXPLORE.md becomes an always-index with sidecars, pointing at the
artifact-shape spoke rather than restating the schema, and keeps the
sidecar-on-collision behavior explore-deep carried.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`check` gains a fifth row reporting what the session can support behind
the new dispatch posture, as PASS/INFO only — absence degrades, never
blocks, because inline is always available.

Three signals: the harness version against the 2.1.219 floor, naming
which recorded behaviors are false below it rather than just reporting a
number; CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH, whose absence costs
throughput and not coverage, since the one control needing a context
that has not seen the work is the outcome-gate verifier and the parent
dispatches that as a sibling; and fork availability stated as a control
rather than a gate, because forks have defaulted on since 2.1.161.

The sibling-skill audit recorded both other skills as NO-CHANGE on
their merits, and that stands: research-deep still argues its own inline
requirement (Workflow is unavailable in every non-fork subagent, and its
multi-topic path needs Agent, which errors even in a fork), and
blindspot's intake question is a genuine elicitation gate whose answer
IS the deliverable.

Their skill-quality warnings are cleared without touching those
dispositions — research-deep gains a Gotchas surface and Use-when
trigger phrasing, blindspot's triggers become single-quoted so the
drop-regression check can track them, and setup gains a Gotchas surface.
All five discovery skills now pass with zero warnings, from three
carrying one or two. setup's five tracked trigger phrases and explore's
one are preserved verbatim.

blindspot/evals/evals.json is deliberately untouched: its explore-deep
references belong to Phase 5's retirement gate, and deciding them here
would pre-empt it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BREAKING CHANGE: /discovery:explore-deep is removed. Callers use
/discovery:explore, which now dispatches discovery:explorer by default
and provides the same isolation — project memory loaded, artifact
persisted by the worker, only a bounded summary returning.

Decision 14 made this conditional on explorer.md reproducing what
explore-deep carried beyond /explore. Graded against the ten-behavior
table: seven carry directly, and the three the table said to handle
differently were handled rather than copied. The empty-scope
orientation pass is deliberately dropped — a dispatched agent with no
scope is a parent-envelope failure, not a mode, so it stops and says so
instead of answering a question nobody asked. The ! precompute is
deliberately not reproduced: it fires at every spawn before the agent
knows it needs the data, and multiplies under fan-out. Both Decision-14
gate conditions are met explicitly — path-scoped rules Read at Step 0,
and sidecar-on-collision with the chosen filename surfaced.

The gate also rests on a live result rather than a reading alone. The
end-to-end probe, run once Phases 2-3 had authored the preload sentinel,
confirmed on a real dispatch that preload lands, Edit is genuinely absent
from the agent's tool set, the payload returns verification: pending, and
the artifact set is written to the slice the envelope named.

That probe earned its keep twice over: it caught two contract defects
static checks could not. The run wrote its coverage ledger with a Status
column of prose rather than the Done column of checkboxes the gate
parses, and improvised a sidecar header with one document-level
confidence instead of per-claim claims[]. Both because the detail lived
only in spokes it never opened. The gate correctly failed closed on the
drifted ledger; SKILL.md now carries the exact ledger shape inline and
makes the sidecar-schema read imperative before the first sidecar.

Seven live references are re-pointed or removed. The eighth is
deliberate: MIGRATION-PLAYBOOK.md still names explore-deep, in a
sentence recording that it was retired and why. The naming doctrine
needed the converse of its execution-tier rule stated — a tier the base
skill can reach at runtime does not earn a sibling — and this is the
precedent. PLUGIN-PHILOSOPHY.md's -deep example moves to
research/research-deep, the pair that genuinely cannot collapse, since
its heavy tier needs Workflow and its multi-topic path needs Agent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…gate

Minor, not patch: two new agent components, a new mandatory research
phase, a changed default execution posture, and a removed user-invocable
skill. The base is 0.8.5 rather than the 0.8.3 the plan projected —
main shipped its own 0.8.4 while #1260 was open, so resolving that
conflict moved #1260's entries up one.

The manifest description drops the stale "inline or in an isolated
forked subagent" framing, which named a mode that no longer exists and
missed the one that now governs. The root README carries that string
verbatim at catalog generation time, so it is regenerated rather than
hand-edited — validate-plugins.sh runs generate-catalog.mjs --check and
would have failed this phase's own gate otherwise.

Serialized last on purpose, per issue #464: same-plugin version bumps
plus top-inserted CHANGELOG entries serialize concurrent PRs by
construction, and this branch already paid that cost once against #1260.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown

Warning

Automated review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-running the job, or pushing a new commit, will retry the review.

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown

Warning

Automated security review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-running the job, or pushing a new commit, will retry the review.

…ry tier

The 138-skill dispatch-posture audit was written into
.work/discovery-subagent-dispatch/, whose .gitignore is `*`. Every file
was worktree-local: it survived a session but not a worktree cleanup,
and it was invisible to any other clone or session picking the sweep up.
Filing a tracked issue that pointed there would have handed the next
session a dangling reference to evidence it could not read.

Promoted without re-derivation — the verdicts are unchanged from the run
that produced them, because rewriting them here would create a second
source of truth for work already done. Relative links are rewritten for
the new depth, and the ledger takes the conventional LEDGER.md name.

The README states what a consuming topic still owes rather than
presenting the sweep as finished: the 32 INLINE-ONLY rows upheld without
an independent read, the 10 rows in no normalization bucket, and the
unresolved conflict with playbooks:fable-5, whose core doctrine encodes
a stricter fan-out floor than the signals this sweep used.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

…ger header

Three review findings; the first is a convention-level consequence of a
change this branch made, not a discovery-local defect.

topic-docs 2.3.0: the .worktreeinclude template matched one level
(.work/*/RESEARCH.md) while the sub-slice mechanism introduced here
writes .work/<slug>/<sub-slug>/. Globs do not descend, so a spawned
worktree carried the top-level index and silently dropped every nested
index, sidecar, and ledger — a partial set the receiving session cannot
distinguish from a complete one, which is worse than carrying nothing.
Additive: five nested patterns added, nothing existing changes meaning,
and no visibility guarantee moves, since a sub-slice was always inside
the slice and merely unreachable by the template.

The coverage gate treated ANY table with a Done column as the ledger, so
an unrelated status table satisfied criterion 11 without a single corpus
item ever being enumerated. It now requires the full mandated header —
Corpus item, Depth criterion, Done — and keeps scanning when a table
does not match, so a real ledger later in the same document is still
found. Three cases added, 25 total.

The explore eval still expected the retired rename-on-collision
behavior. The skill and the agent were fixed in the previous commit;
this is the residue that would have asserted the old contract back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@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: dce80e568d

ℹ️ 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/discovery/skills/research-deep/SKILL.md Outdated
Comment thread plugins/discovery/agents/researcher.md
…ariant

The post-dispatch contract told the parent to dispatch the verifier and
apply project fit, but never to write either result back. Since the
producer returns verification: pending by construction — it may not
self-grade — an index left saying pending after the parent verified it
permanently understates what is known, and a later reader cannot
distinguish an unverified run from a verified one whose result was never
recorded. That negates the artifact's central promise: resume from the
artifact alone.

The parent now updates the outcome-gate result once the verifier returns
and project fit is applied, and a FAIL routes back to the phase the row
names rather than shipping an artifact annotated with its own failure.
Stated explicitly: the verifier writes nothing itself. It never saw the
run, holds no envelope, and giving a second worker write access to the
slice reintroduces the one-writer-per-slice problem the sub-slice rule
exists to prevent.

setup still described placement as governing explore, research, "and
their -deep variants" — advertising a command retired in this same
branch. Phase 5's live-reference sweep grepped the literal string
explore-deep and could not see the paraphrase; both sites now name
/discovery:research-deep, the only -deep variant that still exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@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: 70bee93ad5

ℹ️ 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/discovery/scripts/check-coverage-complete.sh Outdated
Comment thread plugins/discovery/scripts/check-coverage-complete.sh
Four review findings.

research-deep's multi-topic path spawned workers that run /research from
inside a subagent, and the gate assigns its independent-corroboration
and HIGH-confidence rows to a fresh verifier precisely because a
producing context may not grade its own choices. Those workers generally
cannot dispatch that verifier — Agent is filtered out of every non-fork
subagent unless the nesting variable is set — and the parent path only
synthesized. The root index could therefore present claims as
gate-passed that nobody ever graded. This session now owns each topic's
post-dispatch boundary: verifier, project fit, results written back, and
only then synthesis.

The envelope named the slice path but not the memory root. From a nested
slice path an agent cannot tell which ancestor is the configured root,
and the root is where the self-ignoring .gitignore guard belongs — so it
would either leave the real root unguarded or write a `*` into the wrong
directory, both silently. The root is now its own envelope field on both
agents and in both routing summaries.

The coverage gate's header check accepted Corpus item + Depth criterion
+ Done without the mandated `#` column, so a near-miss table still
exited 0 as proof a ledger was completed. All four columns are now
required, each exactly once — the exactly-once half also rejects a
duplicated header, where the column a value lands in is ambiguous.

The cell parser split on every pipe, so a backslash-escaped pipe inside
a cell — a union type, an alternation — inflated the count and made the
row ungradeable. A real ledger over a real corpus could never pass
criterion 11 unless rewritten to say something less accurate. Escaped
pipes are now content. 28 cases, shellcheck clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@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

before returning. Write the RESEARCH.md artifact per the skill's Final step.
Return ONLY a one-paragraph summary + the artifact path + any unresolved questions."

P1 Badge Verify the single-topic Tier 2 artifact

When a heavy single-topic request has no Workflow path, this prompt runs /research inside a general-purpose subagent, where the skill requires the independent-corroboration and HIGH-confidence rows to be graded by a fresh context; however, Agent is unavailable in this fork and the parent path only surfaces the returned summary and artifact path. Although the multi-topic branch now performs sibling verification and persists it, this Tier 2 branch can still present a RESEARCH.md whose load-bearing gate rows were never verified; perform that same parent-side verification and write-back before handoff.

ℹ️ 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/discovery/skills/explore/SKILL.md Outdated
kyle-sexton and others added 2 commits July 25, 2026 20:54
docs/topics/<slug>/ is the contract tier: committed on the task branch
as contracts lock, pruned before merge, with the contract pasted into
the PR body in its place. The contract-slice-prune-gate enforces exactly
that, and it caught this branch carrying two slices that were never
meant to merge.

The second one was my own error, not just an omission. The sweep
evidence was promoted into docs/topics/skill-dispatch-posture-sweep/ to
stop issue #1426 pointing at a gitignored path — a real problem with the
wrong fix, since that destination is branch-only, and the convention's
redaction bar keeps raw captures in the memory tier regardless. The
eleven batch artifacts, the decision rounds, and the verification passes
are raw audit captures; they stay memory-tier and are not carried.

Graduated instead, per the convention's own edges:

- Ticket edge: the full 138-row ledger now lives in #1426's body, which
  is where actionable follow-ups belong and is self-sufficient — it
  states its own audit criteria, so a verdict can be re-derived rather
  than looked up. #1427 carries the fable-5 delegation conflict.
- PR body: the Brief, Phase 0's measured facts, and the C1-C5 design
  contracts, in details blocks. PLAN.md is 113 KB against a ~64 KB cap,
  so this is the convention's "paste the contract, reference the rest" —
  the eight phase bodies and the two review passes stay in branch
  history.

No ADR and no new docs/specs/ tree: the vault edge is for architectural
decisions and specs, and an audit result feeding a tracked work item is
neither.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The spell-check gate rejects "unparseable". Only a comment is affected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

The research path gained this in an earlier round and explore did not —
a parallel omission, not a different judgement. The explorer always
returns verification: pending because it may not grade its own work, so
an EXPLORE.md left saying pending after the parent verified it cannot be
told apart from one whose verifier never ran. That artifact is the whole
handoff a fresh session resumes from, which is exactly where the
ambiguity costs something.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@kyle-sexton
kyle-sexton merged commit 78e89e1 into main Jul 26, 2026
27 checks passed
@kyle-sexton
kyle-sexton deleted the feat/discovery-subagent-dispatch branch July 26, 2026 01:02

@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: 8a19e84592

ℹ️ 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".

## Dispatch decision (multi-topic check, then three tiers)

**Multi-topic check — run FIRST, before any tier.** Count the independent sub-topics in the ask (numbered list, enumerated questions, separable subjects that share no claims). **N ≥ 2 separable topics → do NOT dispatch an engine on the combined blob.** An engine decomposes ONE question into generic research *angles*; fed a multi-topic blob, every broad agent researches all N topics shallowly — N× the wall-clock and tokens for worse depth. Instead: spawn **N parallel topic agents** (Agent tool, `general-purpose`, one per topic, each running the full `/research` discipline; instruct each to cite primary sources by URL — a subagent return without citations is ungrounded synthesis). Each agent writes its own artifact as a sibling `RESEARCH-<topic>.md`; the main session then synthesizes `RESEARCH.md` from the per-topic artifacts. An engine is for a SINGLE contested or deep question that needs falsification rounds and adversarial claim-checking.
**Multi-topic check — run FIRST, before any tier.** Count the independent sub-topics in the ask (numbered list, enumerated questions, separable subjects that share no claims). **N ≥ 2 separable topics → do NOT dispatch an engine on the combined blob.** An engine decomposes ONE question into generic research *angles*; fed a multi-topic blob, every broad agent researches all N topics shallowly — N× the wall-clock and tokens for worse depth. Instead: spawn **N parallel topic agents** (Agent tool, `general-purpose`, one per topic, each running the full `/research` discipline; instruct each to cite primary sources by URL — a subagent return without citations is ungrounded synthesis). **Give each agent its own sub-slice** — `<memory_dir>/<slug>/<topic-slug>/`, assigned by this session in the dispatch envelope, never chosen by the worker (two workers choosing independently can choose the same one). Each writes the normal `RESEARCH.md` index, its sidecars, and its own `research-checklist.md` inside that sub-slice; those filenames are fixed, so N agents pointed at one slice root would overwrite one another's index and ledger rather than producing separable artifacts. **This session owns each topic's post-dispatch boundary — synthesis is the last step, not the only one.** The `/research` gate assigns its independent-corroboration and HIGH-confidence rows to a fresh verifier precisely because a producing context may not grade its own choices, and a topic worker generally cannot dispatch that verifier: `Agent` is filtered out of every non-fork subagent unless `CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH` is set. So for **each** topic, this session dispatches the sibling verifier, applies project fit, and writes both results back into that topic's index — before synthesizing. Skipping it produces the worst available artifact: a root `RESEARCH.md` presenting claims as gate-passed when the rows that matter were never graded by anyone. Then synthesize the slice-root `RESEARCH.md` from the per-topic indexes. An engine is for a SINGLE contested or deep question that needs falsification rounds and adversarial claim-checking.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Verify single-topic Tier 2 artifacts before handoff

When no workflow is available for a heavy single topic, the Tier 2 path spawns a general-purpose worker that runs /research inside a subagent, but the new sibling-verifier, project-fit, and write-back obligations are applied only inside this N >= 2 branch. Fresh evidence after the multi-topic fix is that lines 48–68 still return the single worker's summary and artifact directly; without nested Agent, that producer cannot complete outcome-gate criteria 4, 7, or 8 itself, so the main session can present an unverified artifact. Apply the same parent-side verification and persistence boundary to the single-topic Tier 2 return.

Useful? React with 👍 / 👎.

Comment thread docs/conventions/topic-docs/README.md
Comment thread plugins/discovery/skills/research-deep/SKILL.md
kyle-sexton added a commit that referenced this pull request Aug 3, 2026
…and #1425 (#1886)

## Summary

Eight review threads were posted on #1322 and #1425 **after** those PRs
merged, so the merge gate
never saw them and they stranded unresolved. All eight were verified
against `origin/main` and all
eight were still real — none had been fixed by a later PR.

Two of them, though, were not design gaps. They were the design doc
(`docs/topics/context-engineering-claude-5/design/rerun-contract.md`)
having gone **stale against a
shipped contract that already solved them** —
`plugins/claude-config/skills/audit-pass/reference/run-contract.md`
already carries both
`fix-comparable` and the widened `scan-baseline state digest`. Those two
adopt the shipped
vocabulary and semantics rather than minting a parallel set beside it.

## Findings and disposition

| # | Finding | Verdict | Fix |
|---|---|---|---|
| 1 | P2 vacuous under the shared unchanged-tree precondition | Real;
solved upstream | Adopt shipped `fix-comparable` (attribution-based) |
| 2 | External instruction surfaces missing from the run basis | Real;
solved upstream | Adopt shipped `scan-baseline state digest` |
| 3 | Judging model config missing from the run basis | Real; uncovered
in both | Precondition on P4/P4a only, not the shared list |
| 4 | Sweep machinery version missing from the run basis | Real;
uncovered in both | Added to comparability and the resume digest |
| 5 | `PLAN.md` acceptance gate demands a strictly smaller derived set |
Real | Disappearance in the tier that reported it |
| 6 | `/research-deep` single-topic Tier 2 returns an ungraded artifact
| Real | One boundary section every dispatching tier cites |
| 7 | Root `.worktreeinclude` missing nested sub-slice patterns | Real |
Materialized; verified empirically |
| 8 | Multi-topic eval encodes the retired artifact layout | Real | All
six evals realigned |

## Notable design decisions

- **F3 is deliberately narrower than the finding asked.** The finding
said "record the judging model
configuration in comparability and lane digests". Putting it in the
*shared* precondition would
make P1/P3/P3a unfalsifiable across a model swap while asserting nothing
extra, since `D` contains
no model judgement — P1 is assertable precisely *because* nothing in the
derived tier passes
through a model. It conditions P4's tolerance and P4a only, plus the
resume digest for judged lanes.
- **F1's fix carries two bounds the finding did not name**, both
surfaced by review: an
identity-machinery change is never attributable (it recomputes every
`finding_id`, so the pair is
not fix-comparable at all), and attribution requires a recorded
applied-set the design doc does not
yet name. Phase 6 owes that capture; until it exists P2 abstains on
operator-applied routed
  findings rather than claiming them.

## Verification

- Two independent fresh-context audit rounds with rationale withheld.
Round 1 found the shipped
contract I had missed and the parallel-vocabulary problem; round 2 found
nine defects in the
revision, of which the ones this diff caused are fixed here (lane-digest
scope falsifying Assertion
5.2, the judging-configuration clause sitting in P4's consequent, the
sweep-version attribution
hole, the orphaned containment half, an over-reaching user-scope claim,
and PLAN.md collateral).
- `.worktreeinclude` verified empirically, not by reading: `git ls-files
-o -i --exclude-from`
intersected with `git check-ignore` in a throwaway repo. Before: nested
sub-slice files silently
dropped. After: carried, with baselines and raw scratch still excluded.
The tracked file now
matches the documented block in `docs/conventions/topic-docs/README.md`
byte for byte.
- Gates: `check-changed-skills.sh origin/main` PASS (0 errors, 0
warnings — a pre-existing warning
cleared as a side effect), `check-changelog-parity.sh` `--check` /
`--check-order` /
  `--check-bump` all clean, markdownlint clean, `evals.json` valid.

## Known gaps, recorded not fixed

- The design doc and the shipped contract now diverge in **both**
directions: the design has
`sweep version`, the shipped has `behavior-affecting arguments` and an
`observable detection
version` (a form it adopted after finding the design's triple
unestablishable from outside a
delegate plugin). Recorded in `PLAN.md` as Phase 8 reconciliation work,
because changing either
  predicate changes what the shipped pass reports.
- The design doc's new basis inputs land as prose without named
assertions. So do the four incumbent
inputs — this is a pre-existing structural gap, not one this diff
introduces.
- `PLAN.md` marks Phases 6, 8 and 9 TODO while `audit-pass` ships a more
advanced run contract.
  Implementation ran ahead of the plan; flagged, not reconciled here.

No linked issue

## Related

- Refs #1322 — five stranded threads (findings 1-5)
- Refs #1425 — three stranded threads (findings 6-8)
- Adopts vocabulary from
`plugins/claude-config/skills/audit-pass/reference/run-contract.md`

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant