Skip to content

insufficient_evidence reason ranks symptom over action, and first_recommended_action contradicts it #362

Description

@pengfei-threemoonslab

Summary

On an insufficient_evidence verdict, the headline reason counts source warnings (the symptom) while the one actionable gap is demoted to a secondary line — and the Next action: line printed immediately below actively contradicts it, telling the user no machine-actionable fix exists and to "gather deeper evidence."

Evidence (reproduced 2026-08-10, CLI 0.16.0b7)

Stage-2 stdout, verbatim, three consecutive lines:

Reason: Evidence coverage below threshold (6 source warning(s)); scan results are not
        trustworthy enough to gate release.
Improve evidence: Add a reviewed closed-world binding declaration and rerun verification.
        Target: shipgate.yaml#agent_bindings.declarations.
Next action: Evidence coverage below threshold (6 source warning(s)); ... Surface this to
        the user and gather deeper evidence (e.g. MCP/OpenAPI inputs, eval traces,
        additional source files) before re-running the scan; applying patches does not
        clear an evidence verdict, so no machine-applicable fix is available.

Line 2 is specific and correct. Line 3 is generic, contradicts line 2, and is the field a coding agent is contractually told to read (agent_summary.first_recommended_action). The six warnings named in lines 1 and 3 are not independently fixable — they are the same fact ("ADK static analysis cannot resolve imported tool symbols") restated six times, and they persist unchanged after the binding gap is closed (still 6 at the final blocked verdict).

Secondary evidence — the wrong lever gets no correction signal. Declaring tool_identity.bindings against a source that produced zero observations raised source_warning_count from 6 to 12 with six copies of:

Invalid tool binding 'bind_spraay_batch_eth': member source_id='adk_crypto_payroll_agent',
tool='spraay_batch_eth' matched 0 observations

No message states the actual rule (a source with no observations cannot be a binding member; agent_bindings is the surface for this).

Impact

The gating engine is right; the ranking is wrong. A consumer that reads reason and first_recommended_action — which is exactly what docs/agent-contract-current.md instructs — gets "no fix available, go gather more evidence" when a precise one-block fix is available and already computed. For a coding agent this is a dead end, and the cheap escape routes from a dead end are the ones enumerated in forbidden_actions.

Proposal

  1. Rank the reason by actionability: when at least one gap has a concrete next_action.path, lead with that gap; report source-warning counts as context, not as the headline cause.
  2. Make agent_summary.first_recommended_action agree with Improve evidence: — same gap, same path. Never emit "no machine-applicable fix is available" while a gap carries a non-null path.
  3. Deduplicate source warnings by mechanism: one warning naming the cause and the fix, with the affected symbols listed.
  4. On a binding member matching zero observations, state the rule and the correct surface.

Before / After

Before: Evidence coverage below threshold (6 source warning(s)); ... no machine-applicable fix is available.

After: Insufficient evidence: the agent's tool bindings are unproven. Declare them at shipgate.yaml#agent_bindings.declarations (scaffold: agents-shipgate-reports/suggested-declarations.yaml). Context: 6 ADK tool symbols are imported and not statically resolvable.

Acceptance criteria

  • In the repro, reason names the binding gap; the warning count appears as context.
  • first_recommended_action and Improve evidence: reference the same gap and path.
  • "no machine-applicable fix is available" cannot be emitted when any gap has a non-null next_action.path (invariant test).
  • The six identical ADK warnings collapse to one, listing all six symbols.
  • Declaring tool_identity.bindings on a zero-observation source yields one message naming agent_bindings.

Scope / non-goals

Verdict logic is untouched — insufficient_evidence stays exactly as strict, and source warnings still count toward the IE threshold (_MAX_TOLERATED_SOURCE_WARNINGS = 3, frozen). This is ranking and copy only. Deduplication must not change source_warning_count semantics used by evidence_below_ie_threshold; dedupe at render time, or count distinct mechanisms and update CALIBRATION.md if the counted value changes.

Headline metric

Cold-start iterations to first gating verdict: 5 → 2, and eliminates the contradiction on the field agents are contractually routed to. No schema bump; string and ordering changes plus one consistency invariant.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Next after P0; blocks other work or ships a misleading resultarea:agent-modeAgent-mode contract: control envelope, next actions, command stringsbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions