Skip to content

One verify per turn: reuse a verification the agent already ran, without trusting workspace artifacts #295

Description

@pengfei-threemoonslab

Context

check routes the agent to run agents-shipgate verify; the Stop hook then runs an identical verify again at turn end. Measured on a small fixture that is ~1.2s of pure duplicate work per turn (0.9s vs 2.1s), and it grows with repo size.

A first attempt shipped in #294 and was reverted in the same PR after review found two defects. Recording both so the next attempt does not repeat them:

  1. It trusted a workspace-writable artifact. The hook read agents-shipgate-reports/verifier.json, which the agent can write. A forged (or interrupted) artifact with control.state=complete skipped verification entirely, and an agent_action_required artifact could inject an arbitrary "exact" command into the hook's block reason.
  2. The identity binding was insufficient. Comparing the recorded verification-input.diff to the hook's own snapshot diff is not a binding to the current verification request: committing an uncommitted change reproduces the same diff text, which was used to reproduce an outcome-changing stale pass — an old passed result reused while a fresh verify returned review_required for permissions.scopes: ["*"].

Also noted: on a feature branch with worktree edits, the hook hashes only the worktree diff while the verifier evaluates base...HEAD plus that diff, so the optimization would silently miss anyway.

What a correct design needs

  • Binding to the verifier's own content-addressed request identity (request_id / input_set_id / the terminal receipt), not to a re-derived diff. A hook script cannot compute those, so the producer has to hand the consumer something checkable.
  • A trust story for the handoff. Any path the agent can write is not evidence; the plausible shapes are the verify CLI recording a consumable marker outside the workspace, or the hook invoking a cheap "is this result current?" CLI subcommand that re-derives identity in the engine.
  • Correct behavior on a feature branch, where the hook's snapshot and the verifier's evaluated range differ.

Non-negotiable

No design may let a reused result be more trusted than a fresh one. The Stop hook is advisory, but "advisory" must not mean "skippable by writing a file".

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions