Agents
There are two different things an agent can do with your codebase.
It can write code in it — read some files, follow the conventions it finds, open a diff. Most tooling supports this, because it only requires the repository to be readable.
It can operate it — know what a change affects before making it, run the right jobs, read the result as data, tell you what is not production-ready and why. That requires the system to state what it is, in a form something other than its author can parse.
Putnami is built for the second one. This page is the contract that makes it possible. For the practical setup — assistant context files, prompt patterns — see Develop with AI assistants.
Orientation: read the system, not the files
When you run putnami init, the workspace gets shared assistant context:
.AI/context.md (generated framework and workspace patterns, disposable),
.AI/constraints.md (your durable rules), and thin per-tool entrypoints. Adding
an extension later? putnami context generate rewrites the generated half.
That teaches an assistant your conventions. Two commands teach it your system.
putnami context map — where things are
putnami context map # regenerate
putnami context map --print # render to stdout, write nothingThe workspace orientation map, rebuilt from committed artifacts — project
manifests, schema/openapi.json, schema/config.jsonschema.json, READMEs, and
the docs tree. It answers "which project owns X", "what depends on Y", "where
are the APIs, the config keys, the docs" without shell exploration.
It is byte-deterministic on an unchanged tree, lives gitignored under
.putnami/context-map/, and is never committed. putnami build refreshes it
automatically outside CI, so it does not go stale behind the code.
putnami context pack — how one project is composed
putnami context pack --project <project-id>It writes one deterministic agent-context document per project to
<project>/.gen/agent-context.json:
- identity and graph — id, name, path, type, tags, languages, and the ids of dependencies and dependents;
- composition roots — the application main, the describe entrypoint, and other roots, each with a path and a provenance tag;
- capabilities, contracts, infra, migrations — references carrying a path
and a
sha256:digest, pointing at the committed artifacts; - representative sources — ordered ranges (
path,startLine,endLine, a reason, a token estimate). Ranges only; - tests — the policy, referenced conformance packs, and fixture digests — or, when there are none, a machine-readable reason for their absence;
- docs — adjacent documentation, each marked checked or unchecked;
- config — a config-schema reference and refs-only hints about the operational surface;
- provenance — the workspace revision, the generator, and the aggregation method.
Three properties are worth stating plainly, because they are what make the document safe to hand to a tool you don't control.
It aggregates by reference. Paths, digests, and line ranges. Never file content. The types have no slot for content, and the safety gate rejects any string long enough to look like it anyway.
It is ephemeral. It lives under the gitignored .gen/ root and is never
committed — it embeds the workspace revision and content digests, so it would
churn on every commit. Determinism is pinned byte-for-byte by tests at a fixed
tree, not by committing the artifact.
Publication fails closed. Before a document can leave the workspace for an authorized index, a redaction gate runs and reports as hard errors: a reference to a sensitive path that is not flagged sensitive, anything resembling embedded content, any path that is absolute or escapes the workspace, and any duplicated reference. An empty document fails the gate, so it can never pass vacuously.
You can adjust the document, narrowly. <project>/schema/agent-context.overrides.json
adds or removes representative-source and doc entries and force-flags paths as
sensitive. It cannot override identity, capabilities, or provenance — those are
framework-owned facts, and an author who could rewrite them could make the
document lie.
In CI or pre-flight:
putnami context pack --check # re-derives, exits non-zero on drift, writes nothingThe MCP server
putnami mcp install # writes the putnami entry into .mcp.json, merge-aware
putnami mcp # run the server over stdio (your client spawns this)putnami init, putnami install, and putnami context generate register the
entry already; mcp install is there to add it by hand or repair an edited one.
Other servers in the file are preserved.
The server speaks JSON-RPC 2.0 on stdin/stdout, exits with the session, and exposes the workspace as typed tools instead of shell exploration:
| Tools | What they answer | |
|---|---|---|
| Orient | list_projects · describe_project · agent_context · workspace_map |
What is in this workspace, how is this project composed, where should I read first, which project owns X |
| Blast radius | impacted · why_impacted · deps · find_owner · topo_sort |
What does my change affect, and by what path |
| Intent | list_features · feature_context · list_specs · spec_context |
What was this built for, what were the non-goals, what did the team agree to |
| Execute | run_jobs · get_diagnostics |
Run lint / test / build, then read every failure as file:line:col with severity and code |
Plus the workspace://context resource, so a client that prefers resources over
tool calls gets the same facts.
Every tool declares its own contract
Each tool carries a putnami.dev/contract block in its metadata: an access
value (read or mutating), a readOnly flag, whether it supportsDryRun,
and — when it mutates — a mutates list naming exactly what it touches.
run_jobs is the only mutating tool. Everything else is read-only. It runs
jobs through the same planner and engine as the CLI, declares that it mutates
the workspace and the cache, supports dryRun to return the selected dependency
plan without starting a subprocess, and refuses long-lived serve mode and any
job declared to mutate external systems.
An agent doesn't have to guess which call is safe. The tool says so.
Optional request provenance
Off by default. Set PUTNAMI_AGENT_IDENTITY=1 and explicitly set
PUTNAMI_AGENT_MODEL in the MCP server environment, and requests carry bounded
harness and model headers — so what an agent did is attributable after the fact,
by the same principle that makes every other action in Putnami reviewable.
Blast radius before action
putnami lint,test,build --impacted--impacted resolves the projects affected by the current changes against a
baseline, transitively through the dependency graph. The baseline is resolved in
a fixed order — workspace config, nearest configured epic branch, trunk, local
main or master, then the upstream tracking ref — and never the current branch
itself, so it cannot quietly resolve to "nothing changed".
When an agent needs to justify the result rather than trust it, why_impacted
returns the shortest dependent path from the changed project to the impacted
one, and find_owner maps a file path back to the project that owns it using
the same logic.
A surface that parses the same way every time
putnami build --output=json
putnami test --output=jsonlExit codes, output modes, the reserved global-flag registry, and the machine
documents are the cli protocol — versioned, and validated
by one conformance corpus executed from both the Go and the TypeScript
implementation. An agent that learned the shape does not have to relearn it
because a release changed a log line.
Every job also emits one JSONL event stream — logs, progress, phases, diagnostics, metrics, artifacts, results — so the agent watching a build and the human reading the log are parsing the same lines.
Verifying the work
Generating a change is the easy half. The useful half is telling whether it is safe.
putnami doctor --profile productiondoctor is a read-only production-readiness preflight. It derives findings from
committed manifests — incomplete capabilities, missing required config, invalid
committed schemas, config shadowing — and grades them by deployment profile.
Under --profile production a high or critical finding exits 2; dev and
test stay advisory. Config values are never read or emitted, so running it
in a pipeline leaks nothing.
Each check carries a remediation baked into the check itself, and the code
taxonomy is frozen. Findings you have consciously accepted go into a committed
doctor.waivers.json — a waiver is a reviewable decision in git, not a flag
someone remembered to pass.
Alongside it: get_diagnostics re-reads the complete failure set from the last
executed run without rerunning the work, conformance packs test declared
capabilities against shared fixtures, and putnami context pack --check keeps
the orientation document from going stale behind the code.
The documentation is part of the surface
Stable URL prefixes per section, Copy as Markdown on every page, and
/llms.txt as a machine-readable index of the whole tree. The
structure an agent reads is the structure you read.
What this deliberately does not do
- The agent-context document is per project, and it references rather than embeds. It is an orientation artifact, not an index of your repository — an agent still reads the files it decides to read.
- The MCP server runs workspace jobs. It does not deploy, does not reach external systems, and refuses jobs declared to do so.
- Author overrides are narrow on purpose. If you could rewrite identity or capabilities, the document could lie, and every guarantee above would be worth nothing.
- None of this makes an agent correct.
That last one matters. What the contract buys you is that an agent's actions are bounded, inspectable, and reversible — which is the precondition for letting one run at all.
Read next
- Protocols — the contracts underneath everything on this page.
- Develop with AI assistants — the practical setup and prompt patterns.
- Write a feature spec — how intent gets recorded so
spec_contexthas something to return. - Principles — in particular, automation is a first-class user.