feat(cli): complete machine-readable output and exit-code contract (#171, #160, #150)#189
Merged
Merged
Conversation
Extend the CLI's structured-output convention (already used by check,
explain, and audit summarize) to the remaining subcommands, so every gate
a CI pipeline runs can be consumed structurally while preserving exit codes.
- policy test: add --output text|json; JSON emits a per-case report
({id, tool, expect, got, pass, reason}) plus totals. (#171)
- audit verify: add --output text|json; JSON emits a combined
{chain, signatures?, anchor?} object with a stable status enum. The text
path is refactored into compute + printText helpers and is byte-for-byte
unchanged. (#160)
- check: add --summary <file|->; writes a compact JSON gate summary
(per-decision counts, top denied tools/reasons, fail-on match, failed
flag) independent of --output, before the --fail-on exit. (#150)
Tests cover JSON shapes, exit codes, stdout-purity, unknown --output, and
the gate-summary file/stderr destinations. README and CHANGELOG updated.
Fixes #171
Fixes #160
Fixes #150
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2YUNXPiwtyXJ6Javs4WxU
There was a problem hiding this comment.
Pull request overview
Completes AgentFence CLI’s machine-readable output and exit-code contract by extending --output json support to additional subcommands and adding a separate check --summary JSON artifact for CI gate reporting (without parsing human-oriented text).
Changes:
- Add
check --summary <file|->to emit a compact JSON gate summary independent of--output. - Add
policy test --output text|jsonwith per-case results + totals, preserving failure exit semantics. - Add
audit verify --output text|jsonwith a combined{chain, signatures?, anchor?}JSON report while keeping text output and exit-code behavior consistent.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
cmd/agentfence/main.go |
Implements check --summary, policy test --output json, and audit verify --output json (plus helper result structs/functions). |
cmd/agentfence/main_test.go |
Adds tests covering JSON shapes, exit semantics, stdout purity, and summary destinations. |
README.md |
Documents check --summary and the new JSON modes for policy test / audit verify. |
CHANGELOG.md |
Adds [Unreleased] entries describing the new CLI contracts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address PR review: the I/O-failure and otherwise-unclassified verification paths in computeChainResult/computeAnchorResult returned a zero-value result, emitting an empty chain.status / anchor.status in --output json and breaking the advertised stable status-enum contract. Set status "error" with a detail message in those paths; the text presenter is unaffected (it has no "error" case and stays silent, as before). Document "error" on auditChainResult. Refs #160 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2YUNXPiwtyXJ6Javs4WxU
Address PR review: --summary - writes to stderr, which on a --fail-on gate failure also carries the human "matched --fail-on" line and the root "error:" line, so it is not pure JSON in that case. Clarify in the flag help and README that a file path is the clean machine-readable artifact (produced even on gate failure) and that - is a logging convenience. Behavior unchanged. Refs #150 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2YUNXPiwtyXJ6Javs4WxU
…acts The audit verify --output json work added `signatures` and `anchor` sub-objects as public machine-readable contracts, but only the `chain` sub-object was asserted in JSON mode. Add JSON-mode tests that exercise: - `--pubkey`: the signatures sub-object (status=ok, verified/unsigned counts) on a signed tamper-evident log, with no SIGNATURES: prose leak. - `--anchor`: the anchor sub-object (status=ok, signature_status= not_checked for an unsigned anchor) with no ANCHOR: prose leak. - a plain (non-chained) log: the chain `no_chain` status enum, with no PARSED: prose leak. Tests drive the real CLI subcommands (keygen, anchor, check --sign-key) to match the existing helper style. No production code changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LRaLVqBWb8kbyXbm8LBn43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes AgentFence's machine-readable output & exit-code contract across the CLI.
check,explain, andaudit summarizealready supported--output json; this adds the same convention to the remaining subcommands so every gate a CI pipeline runs can be consumed structurally without parsing prose.What changed (file by file):
cmd/agentfence/main.gopolicy test#171 —policy test --output text|json. JSON emits a per-case report ({id, tool, expect, got, pass, reason}) plus{total, passed, failed}. Text path (PASS/FAIL,--verbose) unchanged. Non-zero exit on any failure preserved in both modes.audit verify --jsonmachine-readable verification result #160 —audit verify --output text|json. JSON emits one combined{chain, signatures?, anchor?}object with stable status enums (ok/no_chain/partial/corrupt/failed, etc.). The three report functions were refactored intocompute*Result+printTexthelpers; the text output is byte-for-byte unchanged (existingstrings.Containstests stay green). The JSON object is emitted even on failure, and exit codes are identical to text mode.check --summary <file|->. Writes a compact JSON gate summary (total,parse_errors,by_decision,top_denied_tools,top_denied_reasons,fail_on,matched,failed,dry_run) independent of--output, before the--fail-onexit so the artifact exists even when the gate fails.-sends it to stderr to keep--outputstdout clean.cmd/agentfence/main_test.go— 7 new tests: JSON shapes, exit codes, stdout-purity (no prose leak), unknown--outputrejection, and gate-summary file + stderr destinations.README.md— CI section documents--summaryand thepolicy test/audit verifyJSON modes.CHANGELOG.md—[Unreleased]entry.Why
These three issues are one surface — extending the existing
--output jsonconvention to the subcommands that lacked it, plus a CI gate summary — all incmd/agentfence/main.go. They share a module, a category, and a test pass, so they ship cleaner together than separately (this was the recommended combined group from the triage).Scope note (Mode B): the repo norm is one-concern-per-PR; bundling these three was explicitly requested. They are tightly cohesive (same file, same convention, no cross-dependencies).
How verified
make ci(fmt-check + vet + test-race + coverage) — green; total coverage 81.1%.go test ./cmd/agentfence/ -run 'PolicyTest|AuditVerify|CheckGateSummary|CheckOutput' -v— all pass.policy test --output json→{total:21, passed:21, failed:0, cases:[…]}, exit 0.audit verify --output json→{"chain":{"status":"no_chain","events":6,…}}.check … --fail-on deny --summary -→ JSON summary on stderr (matched:3, failed:true), exit 1.Related issue
Fixes #171
Fixes #160
Fixes #150
Checklist
make cilocally; green)Tradeoffs / risks
audit verifyrefactor is the largest change; mitigated by keeping the text output byte-for-byte identical and covered by existing tests.audit summarizestyle.🤖 Generated with Claude Code
https://claude.ai/code/session_01E2YUNXPiwtyXJ6Javs4WxU
Generated by Claude Code