Guide · Keystone v4.0.0

Get started with Keystone.

This is a hands-on tutorial. By the end you will have installed Keystone, authored a real charter for a repo, written a guide and a sensor, wired a signal, and understood how vendored policies cascade into your project. Work through it top to bottom — each section builds on the last.

What a charter is

A charter is the set of standards you author to constrain your coding agents — per repository. It lives at .charter/ as plain, versioned markdown. CHARTER.md is the single entrypoint; every host file (CLAUDE.md, AGENTS.md, Cursor rules, Continue rules) is a thin pointer to it. Once the charter is in place, any agent that can read .charter/INDEX.json and the files it points at can operate the project. Keystone the toolchain authors and maintains the charter, but is not required at runtime.

Harness vs. charter

Keep two roles straight, because they are easy to conflate:

  • Harness — the engine that runs the model: the coding agent (Claude Code, Cursor, Codex, opencode, …), the orchestrator, the runner. If you are the engine, you are building a harness.
  • Charter — the standards you author to constrain whatever harness runs, so each repo gets reliable, quality output. If you author the spec, you are writing a charter.

Keystone is not a harness. It is the agent charter framework: charter engineering (a.k.a. constraint engineering) at the repository level.

Note Earlier releases called this a "harness." At 4.0 the vocabulary changed to "charter" and the root moved to .charter/. This guide uses 4.0 vocabulary throughout.

Why per-repo discipline

AI coding agents are productive, and they drift. They forget your conventions between sessions; each repo reinvents the same ad-hoc prompt hacks; org-wide discipline can't be shared, versioned, or audited. A charter fixes that at the level where code actually lives — the repository. Every unique repo gets its own authored standards, and org discipline ships in as vendored policies that every repo pulls, pinned and hash-verified. Swap agents, keep the rules.

authoring

CLI

The keystone binary authors and maintains the charter — init, index, project, verify, and the primitive generators.

dispatch

MCP server

Dispatches the same charter to host agents over the model-context-protocol. Convenience, not required at runtime.

observe

Dashboard

A localhost operator view at http://localhost:4773 — coverage, signals, prune, eval runs.

Remember All three tools are convenience. The charter alone — plain files under .charter/ — is enough for any agent to operate the project.
Guide · Step 2

Install Keystone

You'll install the binary, scaffold a charter into a repo, and wire your agent to the MCP server. Three commands, minimum friction.

1. Install the binary

Grab it from the Homebrew tap (or download a release binary):

$ brew install tacoda/tap/keystone
● keystone installed
~/my-project
$ brew install tacoda/tap/keystone
keystone v4.0.0 installed
$ cd my-project
$ keystone init
? Select your agent claude-code
installing charter to .charter/
installing claude-code target CLAUDE.md → @CHARTER.md
wrote: .charter/INDEX.json
wrote: .charter/lockfile.json
charter installed for claude-code.
$ keystone mcp install --agent claude-code
wrote .mcp.json (keystone MCP server wired)
Next: run /keystone-bootstrap in claude-code.
$ claude
> /keystone-bootstrap
Thinking… (3s · esc to interrupt)

2. Scaffold the charter

From inside a repo, run keystone init. It writes .charter/, a per-host menu, and asks exactly one question — your agent target. Pass --agent to skip the prompt entirely (zero questions):

$ keystone init                     # one question: pick your agent
$ keystone init --agent claude-code  # zero questions

init is minimum-friction on purpose. Anything that needs a decision at install time is an opt-in flag, never scaffolded silently. It writes the charter root and emits the descriptor index (keystone index runs as part of init; you can re-run it any time).

3. Wire your agent to the MCP server

The MCP server lets your host agent pull the same charter Keystone authored. One line writes the host's MCP config:

$ keystone mcp install --agent claude-code   # writes .mcp.json
# other hosts: --agent cursor → .cursor/mcp.json, etc.
Optional Run keystone web serve to open the localhost dashboard at http://localhost:4773. It's an operator view, not a requirement.
Guide · Step 3

Your first charter

Now look at what init gave you. You'll learn what lives under .charter/, why CHARTER.md is the entrypoint, and the two commands you'll run constantly: index and project.

What .charter/ contains

The charter root holds your primitives, the descriptor index, and the state that anchors everything. Here's the shape:

.charter/
├─ CHARTER.mdthe single agent entrypoint (full orientation)
├─ INDEX.jsondescriptor index — every primitive, read first
├─ lockfile.jsonper-file policy hashes
├─ guides/ambient, glob-scoped directives
├─ corpus/the reasoning (why), loaded on demand + state/
├─ sensors/checks that react to a signal or host phase
├─ agents/roles spawned as subagents
├─ commands/ playbooks/ skills/units of work + composed sequences
└─ tools/ patterns/ postures/ documents/ evals/the rest of the 13 kinds

keystone.json stays at the repo root — it holds your policy pins and signal declarations. Everything else lives under .charter/.

The 13 primitive kinds

A charter is built from 13 primitive kinds. Each carries canonical frontmatter (kind, id, description, plus per-kind fields) and lives at a canonical path.

KindWhat it isFires / activatesProjects to
guide
ambient, glob-scoped directive — inferential or computational
globs match
.claude/rules/, .cursor/rules/
sensor
a check → verdict (exit / HTTP status) or agent review
on: signal / phase
host bridge / dispatch
agent
a role spawned as a subagent
spawned by id
.claude/agents/
command
a unit of work / lifecycle step
invoked by name
.claude/commands/
skill
a single composed capability
triggers: match
.claude/skills/
playbook
a composed sequence of commands with gates:
invoked by name
corpus
the reasoning / why, loaded on demand
guide's corpus:
pattern
a reusable documentation pattern (Diátaxis)
applied in prose
document
a governed output (plan / review / adr / retro / feature)
gate transitions
.charter/work/
concern
a composition mixin
included
posture
tool / permission posture
projection
.claude/settings.json
tool
author-defined external callable (cli | http | mcp | plugin); on-demand or a side-effect with on:
on-demand / on:
MCP register
eval
the eval harness
keystone eval run
4.0 There is no hook kind. It was retired at 4.0 — a reaction is now a sensor (a check), a tool (a side-effect), or an agent (a review) that self-subscribes to a signal or host phase via on:. Pick the kind by what the primitive is, not how it fires.

CHARTER.md, the entrypoint

CHARTER.md is the single canonical agent entrypoint — full orientation for the repo. Every host file is a thin pointer to it: Claude Code imports it natively (@CHARTER.md); other hosts get an imperative "read CHARTER.md" plus a per-host capability delta (which subagents / slash commands / skills the host supports). You author one entrypoint; each host reads it its own way.

The two commands you'll run constantly

Every time you add, move, or delete a primitive, two commands keep the charter coherent:

  • keystone index — walks .charter/, parses frontmatter, and emits .charter/INDEX.json. Agents read this descriptor index first and open bodies only when activation conditions match.
  • keystone project — regenerates the host projections (.claude/, .cursor/, …) from your canonical sources.
$ keystone index      # regenerate .charter/INDEX.json
$ keystone project    # regenerate host projections
Tip keystone watch runs an fsnotify loop that re-runs index + project + lint on every save under .charter/. Keep it in a side terminal while you author.
Guide · Step 4

Your first guide

A guide is an ambient, glob-scoped directive — the workhorse primitive. You'll author an idiom guide, pair it with a corpus entry, scope it with globs, and watch it project into .claude/rules/ and other hosts.

Say your repo has a Rails backend and you want migrations to stay small and reversible. You'll capture that as an idiom guide. Scaffold it:

$ keystone new guide idioms/rails/migrations

That writes a guide file and a paired corpus stub. Author the guide body with canonical frontmatter:

# .charter/guides/idioms/rails/migrations.md
---
kind: guide
id: idioms/rails/migrations
description: Migrations are reversible and small.
globs: ["db/migrate/**"]
severity: should
corpus: corpus/idioms/rails/migrations
---

Keep each migration reversible and scoped to one change. Prefer
`change` over `up`/`down` when the operation is reversible. Never
mix a schema change and a data backfill in the same migration.

How globs scope activation

globs: narrows a guide — it can only remove the guide from activation, never expand it. The rule is:

activates ⇔ (topic default fires) ∧ (globs match, or no globs declared)

With globs: ["db/migrate/**"], the guide activates only when the ambient topic already applies and a touched file is under db/migrate/. Globs reflect real code structure — a glob that matches no files is a learning signal, not a feature. The bootstrap flow seeds initial globs from the region map in corpus/state/CODEBASE_STATE.md.

Pair it with corpus

A guide states what; the corpus states why. Corpus is loaded on demand — never auto-loaded — so a guide body can stay terse while the reasoning lives one hop away. Author the paired entry the generator stubbed:

# .charter/corpus/idioms/rails/migrations.md
---
kind: corpus
id: corpus/idioms/rails/migrations
description: Why migrations stay small and reversible.
---

An irreversible migration can't be rolled back cleanly in an
incident. A migration that mixes schema + backfill locks tables
for the duration of the backfill. Small, reversible migrations
keep deploys safe and reviewable.
Golden rule A new guide gets its paired corpus entry in the same change. A guide with no corpus link, or a corpus with no guide, is debt the charter-debt review flags.

Project it to your hosts

Re-index and project. For Claude Code and other pointer-style hosts, the guide becomes a rule shim under .claude/rules/, and idiom loading is gated through corpus/state/GLOBS_INDEX.md. For Cursor, keystone project writes a .cursor/rules/<id>.mdc with the globs: mirrored verbatim.

$ keystone index && keystone project
 .claude/rules/idioms-rails-migrations.md
 .cursor/rules/idioms-rails-migrations.mdc
Guide · Step 5

Your first sensor

A sensor is a check that reacts to a signal or a host phase. It comes in two flavors: computational (a script → exit / HTTP status verdict) and inferential (an agent review → a structured verdict). You'll write one of each.

A computational sensor

Computational sensors run a command and turn its result into a verdict. A non-zero exit (or a failing HTTP status) blocks the gate. Subscribe it to a host phase or a signal with on:, and give it a run::

$ keystone new sensor lint
# .charter/sensors/lint.md
---
kind: sensor
id: lint
description: Block the gate if the linter fails.
mode: computational
on: pre-verify
run: golangci-lint run ./...
---

Runs the project linter. A non-zero exit blocks verification.

Here on: pre-verify subscribes the sensor to a host phase. When that phase fires, Keystone runs run: and reads its exit status — zero passes, non-zero blocks. A sensor can equally subscribe to your own signal (see the next section).

An inferential sensor

Inferential sensors are reviews. Instead of a script, they're dispatched as an agent that reads the diff and returns a structured verdict. Declare the shape with returns::

# .charter/sensors/security-review.md
---
kind: sensor
id: security-review
description: Flag OWASP-top-10 patterns in the current diff.
mode: inferential
on: pre-verify
returns:
  verdict: pass | warn | block
  findings: list
---

Read the diff. Report any injection, auth, or unsafe-default
patterns. Return `block` on a clear vulnerability, `warn` on a
smell, `pass` otherwise.
Why two modes Keystone can't invoke an LLM itself — an inferential sensor emits a dispatch manifest and the host spawns the agent. A computational sensor runs a script directly. Same subscription model (on:), different verdict source.
Guide · Step 6

Signals

A signal is a Keystone framework event the host can't see — the extensible counterpart to a host hook phase. You'll learn the closed set of host phases, how to define your own open signals, and how reactions self-subscribe via on:.

Host phases vs. signals

There are two kinds of on: value, and the distinction is the whole idea:

  • Host phases are a closed set bridged into the host — PreToolUse, Stop, and friends. You subscribe to them; you don't invent them.
  • Signals are open. Any on: value that isn't a host phase is a signal, so you define your own — declare them in keystone.json under signals:.
// keystone.json
{
  "signals": [
    "on-migration-added",
    "on-release-cut"
  ]
}

Fire and list signals

Fire a signal by name; list the declared ones to discover what's available:

$ keystone signal fire on-migration-added
$ keystone signal list

Reactions self-subscribe

There is no hook kind to route signals — reactions subscribe to themselves via on:, exactly like a skill declares its triggers:. When a signal fires, the subscribers react:

  • sensor — runs a check → a verdict (exit / HTTP status); gates.
  • tool — fires a side-effect (transport cli | http | mcp | plugin).
  • agent — runs an inferential review → a structured returns:.

So to react to your on-migration-added signal with a check, you write a sensor with on: on-migration-added — no separate wiring. The primitive's own frontmatter is the subscription.

# .charter/sensors/migration-guard.md
---
kind: sensor
id: migration-guard
description: Check new migrations are reversible.
mode: computational
on: on-migration-added        # self-subscribes to your signal
run: script/check-reversible.sh
---
4.0 keystone hook fire is kept as an alias for keystone signal fire, but there is no hook primitive — the firing layer is the signal, and every reaction self-subscribes.
Guide · Step 7

Policies & the cascade

Policies are how org discipline ships into every repo — vendored charter fragments, version-and-hash-pinned. You'll add one and learn the cascade rules that decide who wins.

A policy is a vendored charter fragment declared in keystone.json. It's pinned by version, hash-verified, and drift-reset on keystone verify. A policy can ship any primitive kind. Add one:

$ keystone policy add tacoda/tacoda-org@v2.0.0

The cascade

When the same item exists in more than one layer, the cascade decides which body loads. The rules, in order:

  • Project wins by default. Your files at .charter/<kind>/<id> override any policy's version of that item.
  • Nested policies refine outer ones. Among policies, a policy nested deeper in keystone.json refines the outer policy it's nested in.
  • strict: locks absolutely. A policy can mark an item strict — then nothing else, project or other policy, can override it. This is how a security team makes a rule non-negotiable.

Project always wins by default; nested policies refine; strict locks absolutely

flowchart TB subgraph project ["project layer (you own · edit freely)"] pg["guides / sensors / agents
under .charter/"] end subgraph policies ["vendored policies (read-only · pinned + hashed)"] outer["outer policy"] inner["nested policy
refines outer"] strict["strict item
locked absolutely"] end pg -->|wins by default| effective["effective charter"] outer --> effective inner -->|refines| outer strict -.->|overrides everything| effective classDef proj fill:#13161d,stroke:#4ade9a,color:#4ade9a classDef pol fill:#1a1e28,stroke:#60a5fa,color:#60a5fa classDef lock fill:#1a1e28,stroke:#f59e0b,color:#f59e0b class pg proj class outer,inner pol class strict lock

See the effective set

To see who actually wins after the cascade resolves, ask for the effective roster:

$ keystone charter show --effective   # the post-cascade winning set
$ keystone verify                     # cascade + policy-drift check
Depth limit Some kinds (sensors, for example) are only allowed at the project layer and at top-level policies. A deeply nested policy that ships them fails keystone verify with a DepthViolation.
Guide · Step 8

Operating the charter

A charter is a living thing. You'll run the learn → synthesize → ratify flywheel, check coverage and conformance, explain any primitive, and pick a pacing mode.

The flywheel: learn → synthesize → ratify

The charter improves through a governed loop. You capture surprises, incidents, and review findings into the learning inbox; you synthesize accepted candidates into the right guide / corpus / sensor layer; you ratify the amendment (governance, provenance, hash-pinning). A parallel pruning wheel removes content that no longer earns its keep.

◴ Learning

01Capture a surprise, incident, or review finding into .charter/learning/inbox/.
02Synthesize accepted candidates into a guide, corpus entry, or sensor.
03Ratify — amend the charter with provenance + hash-pinning; re-index.

◵ Pruning

01Surface content that isn't earning its keep — dead guides, orphan corpus.
02Triage against the debt ledger; each entry names trigger + impact + next step.
03Archive retired content into .charter/archive/; re-index.

Coverage and conformance

Two commands tell you whether the charter is healthy against the actual repo:

  • keystone charter coverage — which project files no guide governs ("uncharted territory").
  • keystone charter conformance — a rubric across cascade, validity, pairing, and coverage, resolving to CONFORMANT, DRIFTING, or NON-CONFORMANT.
$ keystone charter coverage      # files no guide governs
$ keystone charter conformance   # CONFORMANT | DRIFTING | NON-CONFORMANT

Explain any primitive

When you want to know how a primitive activates, what it links to, and where it projects — plus any uncommitted changes — ask keystone explain:

$ keystone explain idioms/rails/migrations

Pacing modes

The charter runs at a pacing mode that controls how much autonomy the agent has through the lifecycle — paired (tight human gates), solo, and autopilot. Switch modes as trust in the charter grows; the iron laws (sensitive files, dangerous actions, explicit acceptance criteria) never loosen regardless of mode.

Guide · Step 9

Next steps

You've installed Keystone, authored a charter, written a guide and two sensors, defined a signal, and learned the cascade. Here's where to go deeper.

Reference documentation

Every primitive kind, its canonical frontmatter, path convention, cascade behavior, and projection targets — the port contracts — live in the reference. When you need the exact required fields for a kind, that's the page.

Source, changelog & contributing

Keystone is open source under the MIT license. The 4.0.0 changelog records the charter rename, the retirement of the hook kind, and the signals model. Contributions welcome — see CONTRIBUTING.md for the local dev loop and the bar for adding a primitive kind.

author

keystone init / index / project

Scaffold the charter, emit the descriptor index, regenerate host projections.

verify

keystone verify / charter conformance

Cascade + policy-drift check; rubric across cascade / validity / pairing / coverage.

signals

keystone signal fire | list

Fire or list the extensible framework events your reactions subscribe to via on:.

explore

keystone explain / charter show

Explain any primitive; show the roster, --effective for the post-cascade winning set.

Built with Keystone · the agent charter framework Home · Reference · GitHub ↗