Repository navigation
Description
Summary
suggested-declarations.yaml — the single highest-value onboarding artifact we ship — is only emitted once the binding layer is already closed. During the two scans where the user is most stuck, it is not written at all, and the binding gap that is reported carries declaration_template: null.
Evidence (reproduced 2026-08-10, CLI 0.16.0b7, contract 21)
Target: google/adk-samples PR #1917 (Google ADK agent; 6 tools, all imported symbols — 4 from an external package).
| Stage | Manifest | Decision | suggested-declarations.yaml |
|---|---|---|---|
| 1 | bare init output |
insufficient_evidence |
not written |
| 2 | + google_adk.tool_inventories |
insufficient_evidence |
not written |
| 3 | + agent_bindings |
insufficient_evidence (12 semantic gaps) |
✅ written |
| 4 | + action_surface.actions |
blocked (4 CRITICAL) |
✅ written |
At stage 2 the binding gap is correctly reported and correctly routed:
{"kind": "declare_agent_bindings",
"path": "shipgate.yaml#agent_bindings.declarations",
"accepted_values": ["agent", "complete:true", "tools", "handoffs", "reason"],
"declaration_template": null}accepted_values names the keys; the user still has to hand-author the block. By contrast every semantic gap at stage 3 carries a full declaration_template and a merge-ready YAML block with a # merge into: shipgate.yaml#… pointer.
Cost of the asymmetry: hand-authoring inventories/function-tools.json (98 lines) plus the agent_bindings block, with one round wasted on an invalid enum (auth.mode: api_key; accepted set is none|scoped|unscoped|ambient) that the scaffold would have pre-filled correctly.
Impact
Binding is the most mechanical layer — the six tool names are already extracted from the tools=[...] list and sit in tool_catalog[] at stage 2. Nothing about scaffolding them requires human judgment; only complete: true does. We ask the user to retype data we already hold, at exactly the point where they have the least context.
Proposal
- Emit
suggested-declarations.yamlwhenever any gap has a next action, including the first scan — not only when semantic gaps exist. - Populate
declaration_templateformissing_binding_evidence, pre-fillingagent,tools[](fromtool_catalog[]), andhandoffs: [], leavingcompleteandreasonas<REVIEW_REQUIRED>. - When a
google_adksource yields zero observations but the agent'stools=[...]names are extractable, also scaffold atool_inventoriesJSON stub with names pre-filled and schemas/enums left as<REVIEW_REQUIRED>.
Before / After
Before (stage 2, stdout): one Improve evidence: line pointing at a YAML path the user must author from the docs.
After (stage 2, stdout):
Improve evidence: Add a reviewed closed-world binding declaration.
Scaffold written: agents-shipgate-reports/suggested-declarations.yaml
6 tool names pre-filled; set `complete:` and `reason:` and merge into shipgate.yaml.
Acceptance criteria
- Stage-1 and stage-2 scans in the repro emit
suggested-declarations.yaml. - The stage-2 scaffold contains an
agent_bindingsblock listing all 6 tools with correctsource_ids, and no inventedcomplete/reasonvalues. - Merging the scaffold verbatim after replacing
<REVIEW_REQUIRED>closesbinding_coverage.gap_countin one iteration. - Existing stage-3/4 scaffold output is unchanged (golden test).
Scope / non-goals
Scaffolds stay inert: <REVIEW_REQUIRED> placeholders only, never auto-applied, never auto-merged. Effect, authority, and complete: true remain human assertions — this issue only removes retyping, not judgment.
Headline metric
Cold-start iterations to first gating verdict: 5 → 2. No new CLI command, no schema version bump — this populates an existing optional field and changes when an existing artifact is written.
Activity
fix(scaffold): carry the vocabulary, and reach the scans that need it (…