Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## Unreleased

- **Zero-base verification now fails closed.** Omitting `--base` no longer
silently turns an unresolved default branch into a head/worktree-only scan.
The resolver prefers an authoritative `origin/HEAD`, accepts one
unambiguous `origin/main`/`origin/master` commit, and uses local
`main`/`master` only to prove that HEAD is already at the default commit —
never as an implicit comparison base. A missing remote ref routes
`control.state=agent_action_required` with `next_action.kind=fetch_base`;
ambiguous or divergent candidates route `human_review_required`. Both exit
2 before `run_scan`, remove stale scan/receipt artifacts, and retain failed
verifier, plan, unit, verify-run, handoff, and PR-comment evidence with no
release decision. Only an explicit `--no-base` requests intentional
head/worktree-only verification. Shallow missing-history failures now route
to fetch, while unrelated non-shallow histories remain human-owned.
- **A first adoption no longer reads as a policy weakening.** Adding the
manifest to a repository that had none is the first verdict every new adopter
sees, and it said "This PR weakens the release policy that evaluates it",
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,21 @@ agents-shipgate verify --workspace . --config shipgate.yaml \
--ci-mode advisory --format json --base origin/main --head HEAD
```

For local, uncommitted work, omit `--base`/`--head` so your working-tree edits
are scanned instead:
For local, uncommitted work, omit `--base`/`--head`. Verify scans the worktree
only after it safely selects a remote default or proves HEAD is already at the
default commit:

```bash
agents-shipgate verify --workspace . --config shipgate.yaml \
--ci-mode advisory --format json
```

If that committed comparison scope cannot be proved, verify exits 2 before
the head scan and returns nonterminal control: fetch missing remote history or
ask a human to select `--base <ref>`. Use `--no-base` only when a person or
calling workflow intentionally chooses head/worktree-only verification; a
coding agent must not add it merely to clear the failure.

If a repo is not configured yet, use the verify flow's preview entry point:

```bash
Expand Down Expand Up @@ -654,7 +661,7 @@ artifacts — in read order:
| Code | Meaning |
|---|---|
| `0` | Pass (advisory mode or strict-no-blockers) |
| `2` | Manifest config error |
| `2` | Configuration/input-scope error, including an unresolved comparison base |
| `3` | Input parse error (file missing, malformed, path traversal blocked) |
| `4` | Other Agents Shipgate error |
| `20` | Strict-mode gate failure |
Expand Down
7 changes: 7 additions & 0 deletions STABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,13 @@ changes only by bumping `contract_version` and updating this file.
| `agents-shipgate self-check` | `--json` |
| `agents-shipgate agent handoff` | `--from`, `--report`, `--verify-run`, `--out`, `--json` |

For `agents-shipgate verify`, omitting `--base` means safe auto-resolution,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Version and synchronize the breaking CLI behavior. Omitting --base can now change from a scan with exit 0 to a pre-scan failure with exit 2, while contract_version remains 19 despite this document promising breaking changes require a bump. Canonical instructions and exit-code tables also remain inconsistent in AGENTS.md, bundled skills/recipes, llms-full.txt, and .well-known. Bump the runtime contract and synchronize the generated and machine-readable surfaces.

not an implicit head-only scan. The verifier selects an available trustworthy
remote default, proves HEAD already equals the default, or exits 2 before
scanning with nonterminal control. Only explicit `--no-base` selects
head/worktree-only verification. This is a fail-closed clarification of the
stable flags, not a command-surface or top-level schema change.

### Provisional CLI command surface

The org/fleet governance commands are preview surfaces in the current
Expand Down
12 changes: 11 additions & 1 deletion docs/agent-contract-current.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,17 @@ Markdown/HTML/PDF rendering. Read
`verifier.json.base_status` to understand whether base diff enrichment ran;
do not use it as a release verdict. The release gate is still
`report.json.release_decision.decision`. `verify` never fetches, so CI callers
must make the base ref available before invocation. Supplying `--head` makes
must make the base ref available before invocation. When `--base` is omitted,
verify resolves one safe default-branch state: `selected`, `head_at_default`,
`fetch_required`, or `selection_required`. The first two may proceed; the last
two exit 2 before the head scan with no `release_decision`. Fetchable missing
history produces `control.state="agent_action_required"` and
`next_action.kind="fetch_base"`; ambiguous selection produces
`human_review_required` with no allowed command. `verification-plan.json`
records the additive audit options `base_mode`, `base_resolution`, and
`resolved_default_ref`. Only explicit `--no-base` sets intentional
head/worktree-only scope; coding agents must not invent it as recovery.
Supplying `--head` makes
verify scan an isolated archive of that ref; omitting it scans the checked-out
workspace. If an explicit `--base` ref or PR diff cannot be inspected, verify
skips a head-only scan; `verifier.json.merge_verdict` is `unknown` and the
Expand Down
12 changes: 11 additions & 1 deletion llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1308,7 +1308,17 @@ Markdown/HTML/PDF rendering. Read
`verifier.json.base_status` to understand whether base diff enrichment ran;
do not use it as a release verdict. The release gate is still
`report.json.release_decision.decision`. `verify` never fetches, so CI callers
must make the base ref available before invocation. Supplying `--head` makes
must make the base ref available before invocation. When `--base` is omitted,
verify resolves one safe default-branch state: `selected`, `head_at_default`,
`fetch_required`, or `selection_required`. The first two may proceed; the last
two exit 2 before the head scan with no `release_decision`. Fetchable missing
history produces `control.state="agent_action_required"` and
`next_action.kind="fetch_base"`; ambiguous selection produces
`human_review_required` with no allowed command. `verification-plan.json`
records the additive audit options `base_mode`, `base_resolution`, and
`resolved_default_ref`. Only explicit `--no-base` sets intentional
head/worktree-only scope; coding agents must not invent it as recovery.
Supplying `--head` makes
verify scan an isolated archive of that ref; omitting it scans the checked-out
workspace. If an explicit `--base` ref or PR diff cannot be inspected, verify
skips a head-only scan; `verifier.json.merge_verdict` is `unknown` and the
Expand Down
12 changes: 6 additions & 6 deletions src/agents_shipgate/cli/verify/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,17 @@ def verify(
"omitted, verify auto-detects the default branch (origin/HEAD, "
"origin/main, origin/master) if it points at a different commit "
"than the head. Local main/master are used only when passed "
"explicitly; --no-base disables auto-detection."
"explicitly. If trustworthy comparison scope cannot be proved, "
"verify exits 2 without running a head-only scan."
),
),
no_base: bool = typer.Option(
False,
"--no-base",
help=(
"Disable base auto-detection when --base is omitted; scan only "
"the working tree or explicit head."
"Explicitly choose intentional head/worktree-only verification "
"when --base is omitted. This is the only opt-out from safe "
"auto-detection."
),
),
head: str | None = typer.Option(
Expand Down Expand Up @@ -201,9 +203,7 @@ def verify(
raise ConfigError("--ci-mode must be advisory or strict")
for label, value in (("--base", base), ("--head", head)):
if value is not None and (
not value
or value.startswith("-")
or any(char in value for char in "\0\r\n")
not value or value.startswith("-") or any(char in value for char in "\0\r\n")
):
raise ConfigError(
f"{label} must be non-empty, must not begin with '-', "
Expand Down
Loading