Repository navigation
Description
Completed with #325 (2026-09-04), consistent with the owner's closure scope. #326 shipped in #486; #323's subprocess adoption fixture in #459 proves that the emitted actions compose, including human-owned declaration review and setup/release authority separation. Revalidated the 16-test adoption suite. #309 remains deferred relevance work; #328 remains the independently tracked output taxonomy problem and #329 is complete. #506/#485/#497/#498 and #521 are follow-on distribution/activation work, not unfulfilled requirements of this delivered epic.
Why this epic exists
Every issue below was found in a single walk through the adoption flow against external repositories — usestrix/strix, google/adk-python#6605, google/adk-samples#1745. None was found by a test. They are filed as separate defects, but they share one root cause pattern:
Each step's output does not quite compose with the next step's input.
Preview identifies a diff but not the framework in it. Init writes a manifest but names the wrong agent and then routes past its own unresolved placeholders. Scan fails on a binding shape the adapter itself understands. Each defect is individually small; together they are the reason a first-contact evaluation does not complete without a human reading source code.
Fixing them one at a time produces seven local fixes and no guarantee the flow composes at the end. This epic defines the flow those fixes must add up to.
Shared control-plane dependency: #333 defines the v0.17 envelope; #323 owns its v0.18 rollout. #322 is the invocation child of #334.
Target experience
The adoption walk should collapse to three clear stages.
verify --preview
→ identify the framework (e.g. ADK) and the actual changed files
→ explicitly require initializing a local evaluation config
init --local-review
→ generate the manifest
→ pause and require confirmation of the root agent and declared purpose
verify
→ scan shared tool bindings
→ produce receipt, handoff, report
→ complete / agent_action_required / human_review_required
Note what is absent: there is no user-facing scan stage. scan remains the engine; verify is the command an adopter runs.
Invariants
Every stage must satisfy all five.
- Exactly one executable next action. Not a ranked menu, not a sentence containing a command, not a prerequisite and a command joined by "then".
- Suggested commands preserve the current invocation style. A flow entered through
python -m agents_shipgatestays runnable through it. - Unreadable evidence is reported as unknown or insufficient — never as "no change". An input the tool could not read must never be rendered as a finding about the repository.
- Product defects are distinguished from user configuration errors. A crash in our extractor must not be presented as something the user misconfigured.
- Proceeding must not require understanding the internal identity model. No
source_type/source_id/native_locatorvocabulary in anything an adopter is expected to act on.
Issue map
Stage 1 — verify --preview
- bug(trigger): recognize Google ADK LlmAgent tools=[...] changes as tool-surface changes #315 — recognize ADK
LlmAgent(tools=[...])as a tool-surface change (identify the framework) - bug(verify-preview): distinguish unreadable diff from trigger no_match #308 — distinguish an unreadable diff from trigger
no_match(identify the changed files; also invariant 3) - bug(trigger): prompts/policies globs are top-level only, diverging from trust-root coverage #311 — nested
prompts/andpolicies/globs (identify the changed files) - feature(trigger): route Ruby/RubyLLM agent PRs as relevant but unsupported #309 — deferred follow-on, not closure-blocking: distinguish relevant-but-unsupported complete inputs (unmilestoned until its public-state metric is approved)
Stage 2 — init --local-review
- feat(init/verify): add a side-effect-free local-review mode for external PRs #326 — the local-review mode itself
- bug(init/google-adk): infer App root_agent instead of selecting the first literal sub-agent #324 — infer the ADK
App(root_agent=...)coordinator instead of the first literal sub-agent - bug(init): avoid selecting one-character literals as the agent name #320 — do not select one-character literals as the agent name
- bug(init): unresolved placeholders should route to human manifest review before verify #325 — unresolved placeholders must route to manifest review (this is the "pause and confirm" requirement)
Stage 3 — verify
- bug(google-adk): shared FunctionTool bindings collide as duplicate tool observations #321 — shared
FunctionToolbindings no longer collide; the adapter preserves one tool with multiple bindings - bug(artifacts): prevent a prior verifier handoff from surviving a later scan #317 — later runs retire prior verifier handoffs
- input_set_id omits adapter-read inputs (prompt files, framework sources) #299 —
input_set_idcovers every input actually read (receipt correctness) - bug(reporting): render the decision engine’s framework-aware remediation #318 — terminal-state remediation must name the framework-correct next step
Cross-cutting
- bug(agent-mode): preserve python -m invocation in emitted next-action commands #322 — invariant 2, directly
- rollout(agent-mode): apply the shared control envelope across commands #323 — invariants 1 and 5: the
complete / agent_action_required / human_review_requiredvocabulary in stage 3 is this envelope, and stage 2 needs it too
Gaps this epic exposes
The two previously unowned invariants now have explicit owners:
Invariant 4 — #328. Today the distinction is inverted in at least two places. #307's ID collision surfaced as internal_error routing the user to init --minimal — a product defect presented as a recovery the user should perform. #321 raises InputParseError, which reads as "your input is malformed" for what is a defect in our extractor. A first-contact adopter cannot tell "this repository is unusual" from "this tool is broken", and will reasonably assume the former. Needs a taxonomy: which exit codes and error kinds mean ours versus yours, and a rule that a product defect never emits a user-remediation action.
Invariant 5 — #329. #321's error message is the clearest violation — it prints source_type=, source_id=, and native_locator= at an adopter — but the principle is broader than that one message and nothing tracks it. Needs an audit of adopter-facing strings for internal-model vocabulary.
Conflicts to resolve before the member issues land
#325's routing target is now aligned. Placeholder resolution advances to verify, not a user-facing scan stage.
#318 targets a surface this flow demotes. It is written against the scan console output. If scan stops being an adopter-facing stage, the framework-aware remediation belongs on verify's terminal output instead. The underlying defect — renderers overwriting what release_decision already decided — is unchanged either way.
#326's surface justification is now settled. It was held unmilestoned pending a headline metric under the CONTRIBUTING.md gate. This flow makes local-review a named stage rather than an optional mode, and the metric is time-to-first-verdict / activation rate: it is the step that lets an external PR be evaluated at all without mutating the target worktree. The security boundary in that issue still governs the design — an ephemeral manifest must not be able to produce something shaped like reviewed release evidence.
Stage 2 implies a default change. Making init --local-review the stage-2 command means the evaluation path is local-review and init --write is the durable-adoption path chosen deliberately. That is a reasonable split, but it should be stated, since init --write is what preview routes to today.
Definition of done
A single end-to-end fixture walks an unadopted external repository through all three stages using only the emitted next action at each step — no source reading, no manual command construction, no internal vocabulary — and reaches a terminal control state. The same fixture must be able to reach human_review_required honestly: the goal is a flow that completes, not a flow that always says yes.
Non-goals
- Weakening
insufficient_evidencewhere the diff is a capability change. - Letting an agent author binding, effect, or authority declarations (
do_not_auto_assert). - Making local-review artifacts indistinguishable from repository-reviewed evidence.
- A second verdict: every control state stays a projection of
release_decision.decision.
Activity
pengfei-threemoonslab commentedon Aug 7, 2026
Milestone update: this epic moves to v0.18.0, with one member staying in v0.17.0.
Why. v0.17.0 was holding 24 issues, which is a backlog with a label rather than a release. It is now scoped to the control plane and deadlock removal — the foundation this epic and #338 both consume:
Everything else in this epic — #311, #315, #320, #324, #325, #326, #318, #322, #328, #329, and the fixtures #310/#316 — lands in v0.18.0 on top of that foundation.
#323 is merging into #333. They are the same surface from opposite ends: #323 horizontal (every command speaks one vocabulary), #333 vertical (compact, unambiguous about what is permitted). Both edit
core/agent_control.py. They ship as one design in v0.17.0, and this epic's stage-3 terminal states (complete/agent_action_required/human_review_required) become a consumer of it rather than a separate definition.One open question moves with it. The #325 triage raised that
initanddoctorhave norelease_decisionto project a control state from, so their state derives from setup facts instead. That question is now an input to the v0.17.0 envelope design, not something #325 settles on its own — otherwise the envelope ships withcontrol_statemeaning "the gate says" in one command and "setup says" in another. #325 then implements the answer in v0.18.0.Sequencing inside v0.18.0 is unchanged from the triage on each issue: #311+#315 as one PR after PR #340 merges (it holds
docs/triggers.json), #320+#324 as one PR (same function insignals.py), fixtures after their trigger dependencies, and #312 last so the numbers describe what actually shipped.fix(agent-mode): adopter-facing output stops naming the internal iden…
fix(agent-mode): adopter-facing output stops naming the internal iden…
fix(agent-mode): adopter-facing output stops naming the internal iden…
fix(agent-mode): adopter-facing output stops naming the internal iden…
fix(agent-mode): adopter-facing output stops naming the internal iden…
fix(agent-mode): adopter-facing output stops naming the internal iden…
fix(agent-mode): adopter-facing output stops naming the internal iden…
fix(agent-mode): adopter-facing output stops naming the internal iden…
pengfei-threemoonslab commentedon Aug 31, 2026
Unmilestoned in the re-cut: epics span milestones. Remaining members are #326 (v0.18.0) and #325 (v0.19.0); #323 closed via PR #459 with the composing-walk fixture. The epic closes when the two members land.
pengfei-threemoonslab commentedon Sep 2, 2026
Checklist updated: #323 shipped via #459 (envelope rollout + the composing-walk fixture), #326 shipped via #486 (side-effect-contained local review). Remaining: #325 (v0.19.0) and #309 (unmilestoned stretch, gated on its metric decision). The epic closes with #325.