Skip to content

test: guard agent surface parity - #361

Merged
YoungJinJung merged 6 commits into
mainfrom
feature/issue-360-agent-surface-parity
Sep 14, 2026
Merged

YoungJinJung merged 6 commits into
mainfrom
feature/issue-360-agent-surface-parity

Conversation

@YoungJinJung

@YoungJinJung YoungJinJung commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • require every domain.Catalog() feature to map to both a registered unic resources command and MCP tool, or carry a one-line exemption
  • verify registered CLI and MCP resource endpoints map back to catalog features
  • document the agent-surface wiring step in contributor and architecture guidance
  • leave the separate inspect exposure as follow-up work in feat: stop TUI browsers from silently skipping the agent-facing surface #360

Related Issues

Part of #360

Validation

  • make test
  • make build
  • go test ./internal/mcp -run TestCatalogFeaturesHaveAgentSurfaceDecision -count=1

Checklist

  • Scope is focused
  • Branch name follows docs/branch-naming-harness.md
  • Documentation harness reviewed
  • README reviewed; no user-facing behavior changed
  • Relevant architecture and contributor docs updated
  • Tests and validation included
  • No breaking changes

Summary by CodeRabbit

  • Documentation

    • Updated extension guidelines to require new features to be represented across command-line and MCP agent surfaces, or to include a documented exemption.
    • Added equivalent guidance to the Korean architecture documentation.
  • Tests

    • Added automated consistency checks to verify that catalog features have exactly one valid agent-surface mapping or an approved exemption.
    • Checks also identify missing, duplicate, stale, or unlinked surface registrations.

Require a documented agent-surface decision for every catalog feature.

Verify the CLI and MCP resource registries and document the contributor wiring step.
@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Warning

Review limit reached

Next included review available in 4 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 61147e32-5c4a-4036-ab24-22da9b442655

📥 Commits

Reviewing files that changed from the base of the PR and between 9eb2cde and beeeb98.

📒 Files selected for processing (2)
  • internal/cli/resources.go
  • internal/mcp/agent_surface_test.go

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: cacad8a2-aa9d-48df-b0c6-8b3c75a54c8e

📥 Commits

Reviewing files that changed from the base of the PR and between a563207 and 9eb2cde.

📒 Files selected for processing (5)
  • AGENTS.md
  • CLAUDE.md
  • docs/architecture.en.md
  • docs/architecture.ko.md
  • internal/mcp/agent_surface_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: test
🧰 Additional context used
📓 Path-based instructions (5)
Check that tests cover API errors, mapping edge cases, and navigation state transitions, not only happy paths.

⚙️ CodeRabbit configuration file

Files:

  • internal/mcp/agent_surface_test.go
Documentation must match implemented behavior.

⚙️ CodeRabbit configuration file

Files:

  • docs/architecture.ko.md
  • docs/architecture.en.md
For Go reviews, look beyond compilation and prioritize nil pointer risks, context propagation, AWS SDK pagination, error wrapping, deterministic sorting, and stable table/detail rendering.

⚙️ CodeRabbit configuration file

Files:

  • internal/mcp/agent_surface_test.go
For every feature added to `domain.Catalog()`, update `internal/mcp/agent_surface_test.go`: map it to both a `unic resources` command and MCP tool, or add a one-line `agentSurfaceExempt` reason.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/mcp/agent_surface_test.go
For every feature added to `domain.Catalog()`, update `internal/mcp/agent_surface_test.go`: map it to both a `unic resources` command and MCP tool, or add a one-line `agentSurfaceExempt` reason

📄 CodeRabbit inference engine (CLAUDE.md)

Files:

  • internal/mcp/agent_surface_test.go
🔇 Additional comments (5)
AGENTS.md (1)

30-33: LGTM!

CLAUDE.md (1)

17-17: LGTM!

docs/architecture.en.md (1)

314-320: LGTM!

docs/architecture.ko.md (1)

314-320: LGTM!

internal/mcp/agent_surface_test.go (1)

16-129: LGTM!


Walkthrough

The pull request documents an Agent Surface Parity requirement and adds a test that validates catalog features against resource commands and MCP tools, with explicit exemptions for unsupported features.

Changes

Agent surface parity

Layer / File(s) Summary
Parity requirement guidance
AGENTS.md, CLAUDE.md, docs/architecture.en.md, docs/architecture.ko.md
The contributor guidance and extension procedures require each catalog feature to map to a unic resources command and MCP tool, or record an agentSurfaceExempt reason.
Catalog parity validation
internal/mcp/agent_surface_test.go
The test declares feature mappings and exemptions. It validates unique catalog entries, exclusive decisions, registered references, non-catalog references, and unmapped resource surfaces.

Priority: ⚪ Pending latest changes

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Other

Merge Risk: ⚪ Minimal · up to 9eb2c

The new guard and guidance consistently require each catalog feature to have both agent surfaces or a documented exemption, with no concrete regression identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (4 skipped: 4 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the required test: conventional prefix and clearly describes the agent-surface parity test change.
Description check ✅ Passed The description includes all required sections, explains the change, identifies issue #360, lists validation commands, and provides a completed checklist. Part of #360`` differs from the template's `C…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (4 skipped: 4 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-360-agent-surface-parity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This PR successfully establishes agent surface parity guardrails by adding documentation and a test that enforces explicit decisions for every catalog feature. The implementation is well-structured: the test validates bidirectional mappings between features, CLI commands, and MCP tools, ensuring no catalog feature is accidentally excluded from agent surfaces without a documented reason. The documentation updates in contributor guides and architecture docs properly communicate the new workflow step. No defects found that would block merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@YoungJinJung YoungJinJung left a comment

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.

Reviewed head: 9eb2cde

Findings

  1. Medium — Validate the CLI contract, not only the command name (internal/mcp/agent_surface_test.go:65-68; current counterexample at internal/cli/resources.go:65-116)

    The new guard treats every direct child of unic resources as a valid agent-facing command based only on its name. At this head, that makes backup-vaults pass even though unic capabilities --json and unic schema resources backup-vaults --json advertise it as read_only: false and output_version: "unversioned" because the command has no discovery annotations. Agents relying on the advertised contract can therefore reject or mishandle a surface that this parity test declares present. Please require mapped commands to carry the read-only and v1 output annotations (and retain a JSON-output flag), and annotate backup-vaults accordingly.

Require mapped resource commands to advertise read-only v1 JSON output.

Annotate the existing backup-vaults command with its documented contract.
@YoungJinJung

Copy link
Copy Markdown
Contributor Author

Addressed in 5f047aa: the parity guard now requires every mapped CLI resource command to advertise read-only and v1 output and to provide a JSON flag. backup-vaults now carries the missing discovery annotations. Validation passed: go test ./internal/cli ./internal/mcp -count=1, make test, and make build.

@YoungJinJung

Copy link
Copy Markdown
Contributor Author

/q review

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This PR successfully implements automated enforcement of agent surface parity through test-driven governance. The implementation correctly validates that every catalog feature maps to both CLI commands and MCP tools, or carries an explicit exemption. All documentation updates accurately reflect the new requirement. The test logic is sound and will prevent drift between the catalog and agent surfaces. No blocking issues identified.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@YoungJinJung YoungJinJung left a comment

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.

Reviewed head: 5f047aa

Findings

  1. Medium — Verify that each MCP tool corresponds to its mapped CLI resource (internal/mcp/agent_surface_test.go:80-85,122-126)

    The guard records resource tools only by name, so the command and tool in an agentSurface are validated independently. For example, mapping FeatureRDSBrowser to rds-instances and list_ec2_instances would still pass: both names are registered, but the MCP side exposes EC2 rather than RDS. That false positive defeats the parity guarantee for the exact copy/paste or wiring mistake this test is intended to catch. Keep the registered tool metadata by name and require its output contract to match the paired command (for example, unic.resources.<command>.v1); also retain the existing registration check.

@YoungJinJung

Copy link
Copy Markdown
Contributor Author

Addressed the latest review finding in 68c002b: the parity test now retains each MCP resource tool output contract and requires it to match unic.resources..v1. Validation passed: focused MCP parity test, make test, and make build.

@YoungJinJung YoungJinJung left a comment

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.

Reviewed head: 68c002b

Findings

  1. Medium — Reject duplicate feature-to-surface mappings (internal/mcp/agent_surface_test.go:128-129)

    mappedCommands and mappedTools are used as sets, so assigning an already-mapped command or tool silently succeeds. For example, a new catalog feature can copy the existing rds-instances / list_rds_instances mapping; all registration and contract checks pass, and the reverse-coverage loops still see those surfaces as mapped, even though the new feature added no agent surface. That permits the exact silent coverage gap this guard is intended to prevent. When recording each mapping, fail if either command or tool already belongs to another feature (tracking the owning feature will also make the diagnostic actionable), unless shared surfaces are explicitly supported and documented.

- Track the catalog feature owning each CLI command and MCP tool.\n- Fail parity validation when a surface is reused by another feature.
@YoungJinJung

Copy link
Copy Markdown
Contributor Author

Addressed in 8e330c0: the parity guard now tracks the catalog feature owning each mapped CLI command and MCP tool, and reports duplicate reuse with both feature names. Validation passed: focused MCP parity test, make test, and make build.

@YoungJinJung YoungJinJung left a comment

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.

Reviewed head: 8e330c0

Findings

  1. Medium — Reject duplicate names in the registered surfaces (internal/mcp/agent_surface_test.go:67-79)

    Both registry scans assign directly into maps keyed by command/tool name, so a second registration with the same name silently overwrites the first. Cobra's AddCommand accepts duplicate child names and dispatches the first match, while tools/list publishes the tools slice verbatim; therefore a copy/paste registration can make the CLI ambiguous or expose duplicate MCP definitions while this parity guard and both reverse-coverage loops still pass. Please fail while building registeredCommands when a command name is already present, and check every MCP tool name for uniqueness before filtering resource output contracts.

@YoungJinJung YoungJinJung left a comment

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.

Reviewed head: 8e330c0

Findings

  1. Medium — Validate the MCP read-only contract too (internal/mcp/agent_surface_test.go:80-84,119-126)

The guard now verifies that each mapped CLI command advertises read-only v1 JSON output, but registeredResourceTools retains only OutputContract and ignores registered.Annotations.ReadOnlyHint. A resource tool with the correct name and unic.resources.<command>.v1 contract would therefore pass even if MCP advertises it as potentially mutating, which can cause safety-aware clients to reject or mishandle the surface. Retain the mapped tool annotation and require ReadOnlyHint to be true alongside the existing output-contract check.

Reject duplicate CLI and MCP registrations and require mapped MCP resource tools to advertise read-only.
@YoungJinJung

Copy link
Copy Markdown
Contributor Author

Addressed both current-head review findings in 8c9cff9: the parity guard now rejects duplicate direct resource-command names and duplicate MCP tool names before filtering, and mapped MCP resource tools retain their metadata and must advertise read-only alongside the paired output contract. Validation passed: focused MCP parity test, make test, and make build.

/q review

@YoungJinJung YoungJinJung left a comment

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.

Reviewed head: 8c9cff9

Findings

  1. Medium — Verify that mapped MCP tools are actually callable and dispatch to the paired command (internal/mcp/agent_surface_test.go:132-141; runtime dispatch at internal/mcp/server.go:290-297,348-460)

    The parity check now proves that a mapped name exists in tools and that its metadata names the expected output contract, but tools/call uses the separate toolArgs switch. A new resource can therefore add a correctly named tool entry, mapping, read-only hint, and output contract while omitting its toolArgs case (or routing that case to a different resources subcommand); this test still passes, yet clients receive -32602 unknown tool or data from the wrong resource. Extend the mapping/test with minimal valid arguments for each mapped tool, call toolArgs, and assert that it succeeds and returns a command beginning with resources, surface.command.

- Exercise every mapped MCP tool through the runtime dispatcher.
- Require dispatch to target the paired resources command.
@YoungJinJung

Copy link
Copy Markdown
Contributor Author

Addressed in beeeb98: the parity guard now calls every mapped MCP tool through the runtime dispatcher with minimal valid arguments and requires it to target the paired resources command. Validation passed: focused MCP parity test, make test, and make build.

/q review

@YoungJinJung YoungJinJung left a comment

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.

Reviewed head: beeeb98

Findings

  1. Medium — Validate the complete generated CLI invocation (internal/mcp/agent_surface_test.go:155-159; runtime path at internal/mcp/server.go:290-308)

    The new dispatch check accepts any argument vector whose first two entries are resources and the mapped command. A toolArgs case can therefore keep that prefix while emitting a renamed or unknown flag, adding an invalid positional argument, or omitting --json; this parity test remains green, but the real ExecuteAutomation path either fails Cobra parsing or returns human output that callTool rejects as invalid JSON. This is a different failure mode from the earlier missing/wrong-command finding. Please parse and validate the full returned argument vector against a fresh CLI command tree (without running the AWS loader), including required flags/positional arguments, and explicitly require JSON output before considering the MCP surface callable.

@YoungJinJung

Copy link
Copy Markdown
Contributor Author

Reviewed and verified rather than read — this goes further than what I sketched in #360, in the right directions.

Verified the guard can actually fail. A parity test that cannot fail is decoration, so I added a Fake Browser feature to domain.Catalog() with no decision and ran it:

--- FAIL: TestCatalogFeaturesHaveAgentSurfaceDecision
    agent_surface_test.go:112: catalog feature "Fake Browser" must have exactly one agent-surface decision

Accounting is exact: 38 catalog features, 38 entries across the two maps (7 mapped + 31 exempt).

The parts I did not propose and think are the best of this PR:

  • Bidirectional. Orphan commands and orphan MCP tools are flagged too, so the surface cannot drift in either direction.
  • exposed == exempt XOR. Both-or-neither fails. Cleaner than checking membership twice.
  • Contract assertions, not just existenceread-only, output-version: v1, a --json flag, ReadOnlyHint, and the unic.resources.<cmd>.v1 naming convention. Plus toolArgs proving the tool actually dispatches to the command it claims. That last one closes the gap where a tool exists but points somewhere else.

Two non-blocking observations for the follow-up work, not this PR:

1. Inspector is outside the guard entirely. grep -c Inspector internal/domain/{model,catalog}.go → 0. Inspector is a workflow, not a catalog feature, so the parity test never sees it. It is also the highest-value thing to expose — RunSecurityScan returns a ready-to-serialize SecurityScanReport{Findings, ScannerCount, Warnings, ScannedAt} and 10 rule packs collapse into one call. Worth a line in the docs noting the guard covers catalog features only, so this does not get quietly forgotten.

2. Four exemptions lean on "mutations exist", but FeatureECSExec disproves the reasoning.

FeatureAutoScalingBrowser: "capacity changes are mutation-gated and no separate read-only contract exists yet"
FeatureEventBridgeRules:   "rule mutations are confirmation-gated and ..."
FeatureLambdaBrowser:      "function invocation is mutation-capable and ..."
FeatureSQSBrowser:         "queue mutations are confirmation-gated and ..."

FeatureECSExec is the most mutation-capable feature in the catalog — it opens an interactive shell — and its read path was still split out cleanly as ecs-rollout. So "the browser has mutations" is not really why these are exempt; "nobody has written the read-only contract yet" is, which the trailing yet already says. SQS backlog triage in particular seems like a strong candidate given deepest-backlog-first ordering is exactly the curated view a raw API bridge cannot reproduce.

Both are notes for later. This is good as-is — merging.

@YoungJinJung
YoungJinJung merged commit 8deced7 into main Sep 14, 2026
3 checks passed
@YoungJinJung
YoungJinJung deleted the feature/issue-360-agent-surface-parity branch September 14, 2026 00:26
@YoungJinJung

Copy link
Copy Markdown
Contributor Author

Addressed the current-head review finding in follow-up PR #362 because #361 merged while the fix was being validated. The guard now resolves each generated invocation through a fresh CLI tree, parses all flags, validates positional/required/group constraints, and requires --json without running AWS loaders. Validation passed: focused MCP parity test, make test, and make build.

YoungJinJung added a commit that referenced this pull request Sep 14, 2026
Adds `unic inspect --json` and the `run_security_inspector` MCP tool,
the first fill from #360's coverage list. Ten rule packs collapse into
one call, which is the curated view a raw AWS API bridge cannot
reproduce.

Returns the shared v1 envelope; `data` carries scanned_at,
scanner_count, finding_count, severity_counts, and findings. Per-scanner
failures land in `warnings` instead of being dropped, so a partially
denied scan is never reported as a clean one. Findings serialize as an
empty array rather than null so agents need not branch on both.

`--checklist` is a root persistent flag, so it is inherited here and
advertised by `unic schema inspect`. Running the security packs while
silently ignoring it would hand back the wrong report, so it is rejected
explicitly until the checklist output contract is defined.

Inspector is a workflow rather than a catalog feature, so it sits
outside `unic resources` and outside the catalog parity test added in
#361; the docs now say so.

Refs #360


Claude-Session: https://claude.ai/code/session_01P2bi5Xga4wU5hDPgNPd4mv

Co-authored-by: YoungJinJung <18644538+YoungJinJung@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants