codapult-guard
Official# @codapult/guard
[](https://github.com/codapult/codapult-guard/actions/workflows/ci.yml)
[](https://github.com/codapult/codapult-guard/actions/workflows/guard-fixtures.yml)
[](https://www.npmjs.com/package/@codapult/guard)
[](LICENSE)
[](package.json)
[](https://www.typescriptlang.org/)
[](https://registry.modelcontextprotocol.io/?q=io.github.codapult%2Fguard)
## Architecture guardrails for AI coding agents
`@codapult/guard` is local-first, model-agnostic **architecture guardrail software for AI coding
agents**. Use it to detect architectural drift, enforce approved software architecture rules, and
check code changes in JavaScript and TypeScript projects.
Guard learns how your codebase is structured, records project architecture as reviewable memory, and
checks future changes against the rules your team approves. It connects AI coding agents to those
rules and returns deterministic evidence when a change introduces a covered architecture
regression. Run the same project-specific checks locally and in CI.
### See the value in one change
Suppose a SaaS project has an established boundary:
```text
Client → Server Action → Service → Repository → Database
```
An AI agent can still produce code that is valid TypeScript but crosses that boundary:
```text
Client → Database
```
After the project has explicitly approved a matching import rule or deterministic contract, Guard
checks the changed files and reports matching violations deterministically:
```text
Codapult Guard (changed files)
Scanned 1 source file(s); suppressed 12 baseline finding(s).
src/app/settings/page.tsx:1 [client-no-persistence-import]
Client modules should not import persistence-layer modules.
import: @/lib/db/client
✗ 1 issue(s) found
Exit code: 1
```
The file path, rule ID, message, and counts depend on the project. This is the actual CLI output
shape: Guard does not infer that every `Client → Database` edge is forbidden, and it does not
activate proposals silently. The team approves the relevant policy first; then existing findings
can be baselined while newly matching violations fail the gate and give the agent bounded evidence
to repair.
> **Guard does not tell every project to use the same architecture.**
> It discovers the architecture that is already there, then lets the team decide what becomes policy.
The core is universal. The strongest first-class scenarios are Next.js SaaS and AI-assisted
development, including server/client boundaries, routes, persistence, authentication, billing,
background jobs, environment configuration, and AI integrations.
## Why Guard exists
AI agents can produce syntactically valid code that still violates the architecture of a real
project: a client component imports server-only code, a new action bypasses the established auth
boundary, a route writes to the database directly, or a change duplicates a service that already
exists.
Existing tools remain essential, but they solve different problems:
| Tool category | Primary question | Guard’s relationship |
| -------------------------- | -------------------------------------------------- | --------------------------------------------------------------------- |
| TypeScript | Is the code type-correct? | Uses the project’s typecheck as an optional gate. |
| ESLint / Biome | Does code follow language and style rules? | Does not duplicate their lint rules. |
| Tests | Does behavior match executable expectations? | Runs configured checks when enabled; does not replace tests. |
| SAST / dependency scanners | Is there a known security or dependency risk? | Can connect adapters; focuses on architecture and change impact. |
| PR review services | What semantic concerns should a reviewer consider? | Produces a bounded, redacted review packet for the selected AI host. |
| **Guard** | Is the project becoming architecturally worse? | Maintains project-specific architectural memory and regression gates. |
## Install
Requirements: Node.js `>=20.19` and a JavaScript or TypeScript project.
```bash
pnpm add -D @codapult/guard
# or
npm install --save-dev @codapult/guard
```
The package exposes the `codapult-guard` binary:
```bash
pnpm exec codapult-guard init
```
The npm package is scoped as `@codapult/guard`; the executable intentionally remains
`codapult-guard` for discoverability and consistency with the standalone product name.
## 60-second quick start
Run from the project root:
```bash
# 1. Build the initial project model and baseline
pnpm exec codapult-guard init
# 2. Inspect only new and changed architecture findings
pnpm exec codapult-guard check --changed
# 3. Prepare deterministic context for an AI review
pnpm exec codapult-guard review --requirement docs/acceptance.md
# 4. Run the configured completion gate
pnpm exec codapult-guard verify --json
```
`init` is protected and refuses to overwrite an existing baseline. Use `init --force` only when
deliberately replacing the project memory. Use `analyze` to refresh discovered facts without
resetting the baseline.
## The operating model
Guard separates facts, policy, verification, and decision:
| Layer | Contains | How it is produced |
| ---------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------- |
| **Facts** | AST, files, imports, dependencies, routes, calls, capabilities, graph, Git history | Deterministic local discovery |
| **Policy** | Rules, contracts, conventions, baseline | Observed proposals plus explicit team approval |
| **Verification** | Changed-file checks, impact analysis, contracts, project checks, review packet | Local CLI, MCP, CI, and existing project tools |
| **Decision** | Pass, fail, warning, needs-review, not-configured | Developer or AI host using Guard evidence |
Guard does not assume a fixed `UI → actions → services → repositories → database` architecture.
It can discover that shape when the project exhibits it, but observed patterns become enforceable
only after explicit approval.
Policy paths are validated as project-relative paths. Guard rejects traversal, absolute paths, and
symlinks escaping the project root; import-boundary checks include imports, re-exports, and literal
dynamic imports.
### Policy scope and state changes
Guard stores policy at the project level, while each policy item can target a narrower boundary:
| Policy item | Scope controls |
| ----------- | ---------------------------------------------------------------------- |
| Rule | `files`, import patterns, and rule kind |
| Contract | `scope`, `entrypoints`, import/package constraints, and required calls |
| Budget | `scope`, metric, limit, severity, and written reason |
| Baseline | Exact finding fingerprints already accepted as legacy debt |
| Waiver | One active finding fingerprint, with owner, reason, and expiry |
Routine checks do not change source code, rules, contracts, baseline, or waivers. Commands that
change policy are explicit: `init`, `rules approve`, `contracts approve|reject`, `baseline accept|remove`, and
`waiver add|renew|remove`. `analyze` refreshes derived project facts, while `verify` may run the
project's configured commands and records a local run manifest; neither changes approved policy.
For the complete mutation contract and host/CI controls, see
[Policy mutation control](docs/policy-mutations.md).
## What Guard discovers
The model is framework-aware without being framework-dependent:
| Area | Examples of discovered evidence |
| ----------------------- | ---------------------------------------------------------------------------------- |
| JavaScript / TypeScript | AST modules, declarations, calls, directives, aliases, re-exports, dynamic imports |
| Next.js / React | App Router routes, route methods, Server Actions, client/server boundaries |
| API boundaries | Route handlers, API modules, entrypoints, impact paths |
| Data | ORM packages, schemas, migrations, repositories, persistence boundaries |
| Capabilities | Auth, payments, email, AI/RAG, queues, storage, cache, search, analytics, content |
| Operations | Environment references, configs, deployment, observability, webhooks |
| Repository shape | Workspaces, package scripts, dependencies, cycles, import hotspots, Git history |
| Testing | Test files, test scripts, workspace checks, changed files |
Capabilities are evidence, not requirements. A Vite app, Express service, Hono project, Node
package, monorepo, or Next.js SaaS can all use the same Guard core.
## What Guard is — and is not
Guard is an architecture control plane for AI-assisted development. It protects project-specific
boundaries and change impact using local facts, approved policy, and deterministic verification.
Guard is not a replacement for ESLint, TypeScript, tests, SAST, or a general-purpose PR bot. Those
tools answer different questions; Guard connects their results with the architectural memory that
an AI coding agent needs before and after changing a repository.
## The normal loop
```text
init once → edit → check --changed → review → verify → commit / merge
↘ analyze after structural changes
```
1. `init` builds project memory and establishes the initial baseline.
2. `check` scans the current project; `check --changed` is the fast change-scoped architecture gate.
3. `review` prepares a bounded and redacted diff packet for an AI host; it does not call an LLM.
4. `verify` runs Guard policy, configured project checks, adapters, runtime diagnostics, and
contract validation.
5. `audit` inspects the complete current state, including findings accepted by the baseline.
For a focused change explanation, use `codapult-guard impact <file...>` or the equivalent
`codapult_guard_impact` MCP tool. It reports both directions of the dependency graph: what the
changed module uses and which transitive callers may be affected. `review` includes the same
impact packet plus typed Git changes (`added`, `modified`, `deleted`, and `renamed`) for the AI
host.
Guard state is stored in `.codapult/guard/`:
```text
.codapult/guard/
├── project.json discovered project model
├── architecture.json observed architecture and capabilities
├── conventions.json recurring project conventions
├── rules.json active and proposed deterministic rules
├── contracts.json project-specific boundaries and required calls
├── proposals.json evidence and approval history
├── baseline.json accepted pre-existing findings
├── agent.json AI-host completion-gate configuration
├── waivers.json time-bounded exceptions to active findings
└── history/ project snapshots and local verification run manifests
```
Commit policy and baseline files when the team wants shared guardrails. Treat cache artifacts as
disposable according to the project’s policy, and never commit secrets.
Baseline fingerprints do not include line numbers, so formatting and line movement do not recreate
the same finding. They do include the rule and affected file/import identity, so a rename or module
move can produce a new fingerprint and should be reviewed explicitly before accepting it into the
baseline.
### Temporary waivers
Baseline records legacy findings that existed when Guard was introduced. A waiver is different: it
temporarily suppresses one active finding and must have an owner, reason, and expiry date.
```bash
codapult-guard waiver add <fingerprint> \
--owner platform-team \
--reason "Migration in progress" \
--expires 2026-12-28
codapult-guard waiver list
codapult-guard waiver renew <id> --expires 2027-01-31 --reason "Migration continues"
codapult-guard waiver remove <id> --reason "Migration completed"
```
Guard warns when a waiver is within the configured warning window (14 days by default). Once it
expires, the original finding is active again. Protected policy can forbid waiver changes through
MCP so an agent can propose a waiver but cannot approve it itself. Waivers are intentionally
fingerprint-specific; changing the violating code invalidates the waiver.
Teams can optionally cap each new waiver or renewal with `waiverPolicy.maxDays` in `rules.json`:
```json
"waiverPolicy": { "warningDays": 14, "maxDays": 90 }
```
The cap applies to the requested grant from the current operation; it does not silently delete or
rewrite existing waivers. Boundary findings with severity `error` fail `check`; warning findings
and waiver-expiry reminders remain advisory.
Run `codapult-guard doctor` to detect an explicitly downgraded client or package boundary. Doctor
reports it as a warning and leaves the project policy unchanged.
### Optional scoped budgets
Budgets are policy, not a universal style rule. Add them only for a named risk boundary such as a
service, route handler, or dependency-heavy module:
```json
{
"id": "service-lines",
"description": "Services must remain reviewable.",
"metric": "lines",
"scope": ["src/services"],
"limit": 300,
"severity": "warning",
"reason": "Keep service changes reviewable by one owner.",
"status": "active"
}
```
Place budgets in the `budgets` array in `.codapult/guard/rules.json`. Supported metrics are
`lines`, `bytes`, and `imports`. Generated files, schemas, migrations, and other paths are not
checked unless they are explicitly included in `scope`; a wildcard such as `*` can intentionally
cover the project root. Invalid, absolute, parent-directory, or nonexistent scopes fail the Guard
gate. Changing a budget is a policy decision:
review the diff and update its written `reason` rather than silently increasing the limit.
## AI agents and MCP
Guard is deliberately model-agnostic. It does not send source code to a remote LLM and does not
edit source files by itself. The AI host owns the model call, permissions, repair loop, and final
decision.
Start the standalone MCP server over stdio:
```bash
pnpm exec codapult-guard mcp-server
```
Register that command in the MCP client from the project root. For example, Cursor can use
`.cursor/mcp.json`:
```json
{
"mcpServers": {
"codapult-guard": {
"type": "stdio",
"command": "pnpm",
"args": ["exec", "codapult-guard", "mcp-server"],
"cwd": "${workspaceFolder}"
}
}
}
```
For clients that accept a generic stdio server, use the same command and set its working
directory to the project root. Guard reads local files only; no API key or model provider is
required. Host-specific examples are in [`docs/integrations/`](docs/integrations/).
Recommended agent loop:
```text
task finished
→ codapult_guard_next_action
→ codapult_guard_context
→ codapult_guard_review(requirement, diff)
→ codapult_guard_verify
→ repair reported failures
→ repeat until pass or bounded iteration limit
```
`codapult_guard_context` keeps the project state explicit for the host: `observations` are
discovered facts only, `proposals.pendingRules` and `proposals.pendingContracts` are unapproved
policy suggestions, `policy` contains only active rules/contracts plus baseline and waiver state,
and `verification` reports whether a check has actually run. A context response with
`verification.status: "not-run"` and `findings: null` is not a passing check.
For Cursor, Claude Code, Codex, Gemini CLI, GitHub Copilot, and generic hosts, see
[`docs/integrations/`](docs/integrations/).
### MCP Registry
Guard is published in the official [MCP Registry](https://registry.modelcontextprotocol.io/?q=io.github.codapult%2Fguard)
under the server name `io.github.codapult/guard`. The Registry is the discovery layer for the
published `@codapult/guard` npm package and its stdio MCP server; it does not replace npm
installation or the host-specific configuration above. For deterministic setup, install the npm
package directly and use the MCP configuration shown below.
For workflows where the authoring agent must not approve its own policy proposals, set Guard to
protected mode in `rules.json`:
```json
{
"approval": {
"mode": "protected",
"allowMcpApproval": false,
"requireDistinctActor": true
}
}
```
MCP can then read and propose policy, while approval happens through the CLI or a protected CI/PR
process. With `requireDistinctActor`, CLI and permitted MCP approvals require `GUARD_APPROVER`,
and Guard rejects the same declared actor when a proposal was generated with `GUARD_PROPOSER`.
These variables provide declared provenance only; external branch protection or signed identity
remains responsible for proving who approved the change.
### Verifying against protected policy
In CI, verify pull requests against the policy from the protected base commit rather than the
copy carried by the branch:
```bash
codapult-guard verify --policy-base "$BASE_SHA" --json
```
Guard then evaluates the changed source with the base commit's rules, contracts, baseline, waivers,
and completion-gate configuration. The result reports `policy.source`, `policy.ref`,
`policy.revision`, `policy.fingerprint`, and whether the checked-out policy differs from that
base. A branch cannot relax its policy or disable the gate and use that relaxed copy for the same
verification run.
The supplied [`docs/guard-ci.yml`](docs/guard-ci.yml) fetches the full Git history and passes the
pull request base SHA automatically. Protect `.codapult/guard/` with CODEOWNERS or the equivalent
review control when policy changes require a separate approval. For example, a repository can add
`.codapult/guard/* @architecture-owners` to `CODEOWNERS`. Guard reports the policy change; the
hosting platform enforces reviewer authorization.
To make policy changes fail this invocation explicitly, add:
```bash
codapult-guard verify --policy-base "$BASE_SHA" --fail-on-policy-change
```
This is useful for a dedicated policy-review gate. It is intentionally separate from using the
base policy for source verification, so teams can choose how their protected review workflow
handles an approved policy update.
See [the extension map](docs/extensions.md) for the supported AI-host, CI/PR, policy-pack, tool
adapter, approval-governance, and observability integrations.
## CI
Copy the consumer workflow into a project that has installed and initialized Guard:
```bash
cp node_modules/@codapult/guard/docs/guard-ci.yml .github/workflows/guard.yml
```
Or copy it from [`docs/guard-ci.yml`](docs/guard-ci.yml). It runs project verification and exports
Guard findings as SARIF for GitHub code scanning. Pull requests install dependencies without
lifecycle scripts and use deterministic Guard checks without executing PR-owned scripts; trusted
pushes may run the configured project checks.
The package repository separately runs its own unit, typecheck, build, and packaging checks in
[`.github/workflows/ci.yml`](.github/workflows/ci.yml), while the fixture workflow tests Guard on
real project shapes.
## CLI surface
| Command | Purpose |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `init` | Create project memory and the initial baseline. |
| `analyze` | Refresh facts without changing policy or baseline. |
| `propose` | Generate evidence-based rule and contract proposals. |
| `check [--changed]` | Enforce active policy; `--changed` limits findings to changed and untracked files. |
| `audit` | Scan the complete current project, including baseline findings. |
| `review` | Create a bounded semantic-review packet for an AI host. |
| `verify` | Run the configured completion gate. |
| `governance` | Audit declared proposal/approval provenance and decision-to-run reachability. |
| `doctor` | Diagnose invalid or missing Guard artifacts. |
| `history` / `history-diff` | Inspect project model, module graph, and architecture-edge evolution. |
| `impact <files...>` | Explain dependencies, transitive dependents, capabilities, and contracts affected by files. |
| `policy explain <id>` | Explain a policy item, its evidence, and approval history. |
| `rules` / `contracts` | Approve rules; approve or reject contracts. |
| `baseline` | Review or intentionally accept existing findings. |
| `waiver` | Manage owner-attributed, time-bounded exceptions to exact findings. |
Use `analyze --refresh` after a structural change. `doctor --fix-cache` removes only the disposable
discovery cache; it does not change rules, contracts, baseline, or source files.
Guard state is safe for concurrent CLI/MCP readers and writers: derived facts are published as an
atomic generation, policy updates use revisions, and short write contention is waited out. A
process that dies while holding the state lock is detected by its local PID and the lock is
recovered; use `--no-wait` when an integration needs immediate contention feedback.
Run `pnpm exec codapult-guard <command> --help` for command-specific options.
Every `verify` run also writes a local manifest under
`.codapult/guard/history/runs/<run-id>.json`. It records stage durations, the outcome, and the
policy gate that decided the result. It also records the effective policy revision and fingerprint;
when a base ref was supplied, it records the ref and whether the working-tree policy differed.
When an approved policy decision caused the current policy, the manifest also retains its decision
ID, making the relationship traversable from approval to verification. The `governance` command
audits this metadata and reports missing or overlapping declared actors. It contains no source code
or external telemetry. These manifests make a failed run explainable without turning Guard into a
production tracing system or an identity/signature ledger.
The manifest also contains a `verification-receipt` object. It is a compact, machine-readable
record of the commit, policy base and fingerprint, decision IDs, check statuses, finding counts,
contract issues, waiver state, and Guard version. It intentionally excludes command output and
source code. The receipt describes what Guard decided; it is not a cryptographic signature or proof
of CI identity. When a policy base is supplied, it separates the base diff from dirty working-tree
files so local verification cannot silently present an incomplete change set.
## Security and data handling
- Deterministic discovery and checks run locally.
- Review packets are bounded and redact common secrets before they are returned to an AI host.
- Guard does not invoke an LLM or require a provider API key.
- Project commands and external adapters run only when enabled by the project configuration.
- Existing findings can be baselined, but new regressions remain visible.
## Verification and proof
The repository validates the product through multiple layers:
- unit and integration tests for discovery, policy, baseline, verification, MCP, and output;
- AST adversarial cases for aliases, re-exports, dynamic imports, and route boundaries;
- golden end-to-end flow from violation to repair;
- mutation regression checks across real Next.js, React/Vite, Node, Hono, monorepo, and Express
repositories;
- npm pack checks to ensure the published artifact contains only the intended build output.
Run the maintainer checks locally:
```bash
pnpm test
pnpm test:guard:golden
pnpm test:guard:fixtures
pnpm test:guard:fixtures:regression
pnpm release:check
```
## Documentation
- [Complete Guard guide](docs/guard.md)
- [AI-agent integration](docs/guard-agent-integration.md)
- [Host integrations](docs/integrations/)
- [CI consumer workflow](docs/guard-ci.yml)
- [Fixture matrix](docs/guard-fixtures.md)
- [Release process](docs/releasing.md)
## More from Codapult
Starting a SaaS from scratch? [Codapult](https://codapult.dev) is a production-ready Next.js SaaS
foundation built for AI-assisted development — with MCP context, built-in AI, modular
architecture, and the source code under your control.
## License
MIT. See [LICENSE](LICENSE).
TDQS
Scored across 14 tools
Most tools have distinct purposes (init, propose, proposal_decide, waivers, impact, explain), but the verification cluster—verify, check, and audit—overlaps significantly, with descriptions distinguishing them only subtly (build gate vs architecture rules vs full audit). Similarly, next_action, review, and analyze all produce or refresh bounded agent-facing context, creating some boundary confusion.
All 14 tools share a uniform lowercase snake_case `codapult_guard_` prefix, which is highly predictable. Slight inconsistency arises from mixing bare verbs (init, verify, propose, audit, check, review, explain, analyze) with noun forms (context, governance, waivers, impact, next_action, proposal_decide), but the pattern remains readable.
14 tools sits comfortably in the well-scoped 3-15 range and maps to discernible workflow stages (discover, init, propose, decide, verify, audit, review, manage waivers). No tool appears redundant enough to trim, and none of the surface feels padded.
The lifecycle is largely covered: discovery (context), initialization (init), rule proposal and approval (propose/proposal_decide), enforcement (verify/check/audit), review (review), exceptions (waivers), and introspection (impact, explain, governance, analyze). Minor gaps exist—no explicit reset/teardown of Guard artifacts and waiver management is a single bundled tool rather than discrete list/create/remove operations.