Repository navigation
Description
Summary
Agents Shipgate does not reliably distinguish its own defects from the user's configuration errors in adopter-facing output. A first-contact adopter cannot tell "this repository is unusual and needs configuration" from "this tool is broken", and will reasonably assume the former — then spend time fixing something that was never theirs.
Invariant 4 of #327.
Evidence
Two cases, both observed on real external repositories.
#307 (now fixed) — a product defect routed as user recovery. The duplicate tool_sources[].id collision was a bug in our ID derivation. In agent mode it surfaced as internal_error with a next action of init --minimal — a recovery step the user performs, which discards the detection work and hands back a CHANGE_ME template. The message pointed the user at their repository; the fault was ours.
#321 — a product defect typed as an input error. Sharing one FunctionTool across several LlmAgents raises InputParseError. That exception name and exit code mean "the input you supplied could not be parsed", but the input is a valid, canonical ADK pattern and the defect is in our catalog identity model. An adopter reading InputParseError will look for the malformed file.
The pattern in both: an internal failure inherits an error kind and a next action that imply user fault.
Why this matters more during first contact
Adoption is exactly when the user has the least basis for judging which side the fault is on, and the most willingness to conclude the tool does not work on their repository and stop. A misattributed error at this point is not a cosmetic problem — it is an abandonment path. Every open bug in this tracker was found by pointing the tool at an external repository, and several presented this way.
Requested
A taxonomy, applied consistently:
- Classify every adopter-facing failure as ours (a defect, a limitation, an unsupported-but-valid shape) or theirs (a genuinely malformed or missing input).
- A defect must never emit a user-remediation next action. The honest action is to report the failure, name it as ours, and — where possible — offer a route that does not require the user to fix anything.
- Exit codes and error kinds should follow the classification rather than cutting across it.
InputParseErrorshould mean the input was bad. - Unsupported-but-valid is a third state and should say so: the repository is fine, the shape is real, we do not handle it yet. This overlaps the
relevant_but_unsupportedrouting in feature(trigger): route Ruby/RubyLLM agent PRs as relevant but unsupported #309 — the vocabulary should be settled once.
Acceptance criteria
- A documented classification covering every adopter-facing error kind and exit code.
- No failure classified as ours emits a next action that asks the user to change their repository.
- bug(google-adk): shared FunctionTool bindings collide as duplicate tool observations #321's failure, once fixed, cannot recur as a user-facing parse error for any valid framework pattern.
- Agent-mode
next_actioncarries the classification, so an automated caller can route on it rather than on message text. - A test asserting that at least one injected internal defect is reported as ours, with no user-remediation action. (Report init generation defects without prescribing repository repair (#328) #546)
Non-goals
Suppressing or softening genuine configuration errors. The point is accurate attribution in both directions.
First implementation slice, still reproducible on main — 2026-09-06
This is not only an error-catalog task. On 20968551, the generated-manifest validation failure still reports internal_error and then routes the coding agent to init --write --minimal --json, expecting the user to fill placeholders. The current test pins that fallback while checking invocation fidelity.
Reproduced through the real CliRunner, injecting only _validate_manifest_text raising ValueError("synthetic schema failure") and running init --workspace <fixture> --write --json:
exit_code: 4
error: internal_error
control.next_action.actor: coding_agent
control.next_action.kind: initialize
next_actions[0].command: agents-shipgate init … --write --minimal --json
next_actions[0].expects: shipgate.yaml renders with placeholder fields you fill in manually.
manifest_written: false
Proposed PR order
- Repair this generator-failure route first. Use an existing diagnostic/review route to identify an Agents Shipgate defect. Do not prescribe rewriting the manifest or automatically discarding discovery for a minimal template.
- Preserve the nonzero exit, all-false setup permissions, structured error/envelope and stream contract.
--minimalremains an explicit user choice, not the canonical remedy for a product exception. - Update the existing regression to preserve invocation/envelope assertions while requiring no repository-edit action and no manifest, workflow or kit writes. Add the counterexample: genuinely malformed user YAML still names its file/field for repair.
- Then apply the defect / valid-but-unsupported / malformed-input distinction across detect, doctor and scan. Avoid renaming every exception/exit code before the demonstrated wrong route is fixed; any public grammar change follows
STABILITY.md.
#520's reader-coverage misses are the other major instance: name a product extraction limitation as such, without asking users to repair valid source or invent reviewed evidence. #538 provides a concrete binding whose known facts must survive the missing-leaf case.
The init fix can proceed with #533, #516 and #536; coordinate only shared-file edits. It does not wait for a broad decomposition of init or a general taxonomy rewrite.
Implemented first slice — #546
Merged after one GitHub review/address round, full local regression and green CI. Init rendering and validation failures now emit the existing internal-error/setup envelope with details.failure=manifest_generation_failed, details.origin=agents_shipgate, and one report-defect route with no edit or fallback command. Four injected cases prove no manifest/workflow/kit writes; malformed user YAML still points to its file for repair. The remaining cross-command taxonomy acceptance criteria stay open. #547 separately records the newly observed loss of host-inventory failure detail.
Activity
pengfei-threemoonslab commentedon Aug 20, 2026
A concrete instance from a first-time adoption walk of google/adk-samples#1745 on
0.16.0b7, in the one field shipgate requires a human to hand-write.The user filled
agent.declared_purpose[0]with a one-line description that happened to contain a colon:The unquoted
:makes YAML parse the item as a mapping, so the list entry is a dict.scanreports:This is correctly classified as a user configuration error, and the "fix in place, do not re-run init" instruction is genuinely good. The gap is that the message names the schema violation but not the cause: a user who typed a colon into a prose sentence is told their string is not a string, with no hint that the colon turned the item into a key/value pair. The fix is not discoverable from the message without knowing YAML semantics.
Worth special-casing because it lands on the highest-traffic hand-edited field in the product, and prose descriptions attract colons. When a scalar field receives a
dictwith exactly one key, the loader has enough information to say so: "line 13 parsed as a mapping, not a string — an unquoted:splits a YAML scalar. Quote the value."pengfei-threemoonslab commentedon Aug 31, 2026
Removed from v0.18.0 in the milestone re-cut: #440-cluster gap work, not a four-flags commitment.
pengfei-threemoonslab commentedon Sep 4, 2026
Evidence to add:
insufficient_evidenceis the largest instance of this pattern. #520 establishes that the value is emitted when source warnings cross a threshold — a statement that shipgate's reader could not enumerate the source — but it is rendered as a release decision the adopter reads as "your repository is unknowable." An adapter gap wearing a verdict's clothes, on the same first contact this issue is about. #393 was one repository-family-wide instance; the corpus round found it on 13 of 48 real-history cases from one rater family.Route init generation defects to product review instead of repository…
pengfei-threemoonslab commentedon Oct 7, 2026
Carrying #440's unticked row here: production-rendered regressions for the error taxonomy (see #440 for the original list).