Instructions for AI coding agents (and humans) working in this repository.
PlanGuard reviews Terraform pull requests and publishes GitHub Checks covering security, availability, cost, and deployment risk. Full product spec: docs/PRODUCT_PLAN.md.
Risk scores, policy pass/fail, and cost numbers are computed by deterministic code, never by an LLM. LLMs may only summarize, explain, and connect findings that deterministic code already produced. See docs/PRODUCT_PLAN.md #3.1 before adding any AI-generated value to a code path that affects the Check verdict.
Concretely, when adding a tool or agent under .agents/:
- A
@Tool()method may only read and return data that was already computed elsewhere (parsed plan, Checkov output, rule-engine score, cost estimate). It must not itself decide severity, risk grade, or pass/fail. - Any agent-generated claim that isn't a direct pass-through of a tool result must be labeled "Needs verification" in the output, per docs/PRODUCT_PLAN.md #6.2.
Two directories, two different jobs: packages/* is deterministic analysis code (the actual
source of risk scores and findings). .agents/ contains both PlanGuard's product AI code (the
type-chain agent that explains what packages/* already computed) and development-only guardrails.
The development harness is never runtime code and is never shipped.
This is an npm workspace (packages/*). From the repo root: npm run typecheck and npm run test build and check every workspace package before checking/testing the root harness. A single
package: npm run test -w @planguard/terraform-parser.
This directory holds the type-chain
(@theorvane/type-chain, a decorator-first authoring layer over LangChain JS) agent(s) used for
the one AI-reasoning node in the review pipeline (generate_explanation in
docs/PRODUCT_PLAN.md #11). Everything upstream of
that node (plan parsing, Checkov, policy checks, risk scoring) is plain deterministic TypeScript
and does not belong in .agents/.
types.ts— sharedReviewContextshape (resource changes + findings by category).review-explanation-agent.ts— the@Agent()-decorated class; each@Tool()method exposes one category of deterministic findings to the model.harness.ts— runnable entry point. Wires the agent, prints registered tool names, and (only ifPLANGUARD_MODELis set) invokes it live againstfixtures/review-fixture.json.
Run it:
npm install
npm run typecheck
npm run harnessnpm run harness always verifies tool wiring without any API key. To actually invoke the model,
copy .env.example to .env, set PLANGUARD_MODEL (a LangChain init-string like
anthropic:claude-sonnet-5) and the matching provider API key, then export them before running.
- Extend
ReviewContextin.agents/types.ts. - Add a
@Tool()method toreview-explanation-agent.tsthat returns the new field — do not compute anything inside the tool. - Update
fixtures/review-fixture.jsonwith a representative example. - Run
npm run typecheck && npm run harnessbefore committing.
Dev-tooling only — guards AI coding agents working on this repo, not PlanGuard's product code.
claude-code-settings.json— Claude Code settings template that allow-lists the harness's own commands (npm run harness,git status,gh pr *, ...) and denies reading.env,*.pem,*.tfstate,*.tfvars, and.aws/credentialsoutright. To activate it in Claude Code, install this file as that tool's project settings file; its hook already points at the canonical.agents/script.hooks/block-secrets.sh— aPreToolUse(Bash)hook that tokenizes commands withpython3/shlex(not raw regex, to resistrm -r -f/git add .-style bypasses) and blocks destructiverm -r, exposure (cat/curl/...) of.env/*.pem/id_rsa*/*.tfstate/*.tfvars/.aws/credentials, andgit addthat would actually stage one. This exists because PlanGuard's own product handles exactly this kind of sensitive data (see docs/PRODUCT_PLAN.md #15) — the repo enforces on itself what the product promises to enforce for users.reviewers/risk-boundary-reviewer.md— a review-only subagent for the rule above: run it after any change to product-agent TypeScript under.agents/to check no@Tool()method or prompt string computes a severity, score, or pass/fail itself.
Do not add a tracked .claude/ configuration directory back to this repository. A developer's
ignored .claude/settings.local.json may remain locally, but the shared source of truth for the
development harness is .agents/.
- No commits directly on
main. Every change: open a GitHub issue first, branch offmainas<type>/<issue-number>-<short-slug>(e.g.feat/9-cost-estimator), open a PR againstmainwithCloses #<issue-number>in the body, and merge viagh pr merge --merge --delete-branch. - Commit messages follow Conventional Commits:
<type>: <subject>, imperative mood, no period. Types used in this repo:feat,fix,docs,chore,refactor,test. Body explains why when it isn't obvious; footer carriesCloses #<issue-number>. - Merge, don't rebase/squash, once a PR is up — keeps
git logmatching the PR history 1:1.
- TypeScript, ESM (
"type": "module"), standard (Stage 3) decorators —experimentalDecoratorsmust stayfalse(type-chain requirement). - Don't commit
dist/,node_modules/, or.env(see.gitignore). - Prefer editing existing files under
.agents/over creating new top-level agent directories; the repo layout in docs/PRODUCT_PLAN.md #22 is the target shape once this grows beyond one agent.