stele · docs

authored intent vs extracted truth

stele compiles a typed knowledge graph from three authored sources — the file tree, comment anchors, and typed blocks in AGENTS.md files — and reconciles it against a graph extracted from the code. CI asserts the two against each other in both directions: an undeclared dependency means the code broke the signature, an unbacked declaration means the doc lied. The same graph answers agent queries and materializes standard-compliant AGENTS.md files, so every harness gets the full content with zero engine.

Three authored sources — the file tree, comment anchors, typed blocks in AGENTS.md — build the declared graph.lock; the source files yield the extracted graph; stele check reconciles the two in both directions, flagging an undeclared dependency (the code broke the signature) or an unbacked declaration (the doc lied); the graph then answers agent queries and emits AGENTS.md.
Authored intent vs extracted truth: CI asserts the declared graph against the extracted one in both directions. Source: Claude Design canvas stele figures, board authored-vs-extracted.

stele /ˈstiːli/ — an inscribed standing stone that publicly declares the law. The territory is checked against it.

v0.3.0 · glyph notation SPEC Draft 0.10 — the contract self-hosting: this repo runs its own gate

Install

One static executable. The installer resolves a release, verifies the sha256, and drops the binary in ~/.local/bin.

curl -fsSL https://raw.githubusercontent.com/jgeschwendt/stele/main/scripts/install.sh | bash
# pin a version
curl -fsSL https://raw.githubusercontent.com/jgeschwendt/stele/main/scripts/install.sh | bash -s v0.3.0

# prereleases
curl -fsSL https://raw.githubusercontent.com/jgeschwendt/stele/main/scripts/install.sh | channel=canary bash
# from source
cargo install --git https://github.com/jgeschwendt/stele stele-cli

The degradation ladder

Files → CLI → MCP is a spec invariant, not an implementation detail. Each rung adds capability; none becomes a reading dependency.

Three rungs left to right: files, then CLI, then MCP, each adding capability; a line from the CLI and MCP rungs returns to the files rung, since every answer is derivable from the files.
The degradation ladder: files → CLI → MCP, and every rung's answer is derivable from the files. Source: Claude Design canvas stele figures, board degradation-ladder.

Adoption path

Bare repo to CI-enforced graph. The long form is GUIDE.md; every rule behind it is in the SPEC.

  1. Install — one binary, pinned in CI to the version you tested against.
  2. Scaffold — stele init from the repo root scaffolds AGENTS.md skeletons: a system node at the root, container nodes for candidate directories. Fill in purpose, real commands, and the claims worth enforcing; delete the skeletons that don't deserve a node. The graph follows interface boundaries, not the tree.
  3. Anchor your claims — every invariant and hazard names an anchor: path#symbol for a definition or markdown heading, or ※ <slug> for a named landmark comment (// ※ refund-cap) that survives renames and moves — the anchor: field quotes that token verbatim. Anchors are what make a claim checkable.
  4. Build — stele build compiles authored sources into .stele/graph.lock and .stele/index/. Build is the reconciliation point: it re-stamps each claim's verified mark against current code, so run it locally once you've reviewed that the claim still holds.
  5. Emit — stele emit renders the router regions into AGENTS.md, the transpose indexes, and the shim. Anything outside a marker region stays yours.
  6. Commit — the AGENTS.md files, the lock, the indexes. The lock is the canonical graph: queries and CI read it and never re-derive it. Paths the engine should never scan go in a root .steleignore, gitignore syntax.
  7. Enforce — CI checks out full history (freshness walks blame), then runs stele check followed by stele emit --check. CI never runs stele build — that would launder staleness.
  8. Query — agents and humans read the graph instead of grepping docs, over the CLI or MCP.
stele init                     # scaffold nodes
stele build && stele emit      # compile the lock, render the files
stele check                    # six assertion classes, exit 1 on violations
stele emit --check             # committed files vs what the lock renders
# the CI gate
- uses: actions/checkout@v5
  with:
    fetch-depth: 0        # full history — freshness checks use blame
- run: curl -fsSL https://raw.githubusercontent.com/jgeschwendt/stele/main/scripts/install.sh | bash -s v0.3.0
- run: ~/.local/bin/stele check
- run: ~/.local/bin/stele emit --check

Notation

0.3.0's glyph notation: four glyphs plus an ASCII region marker, one grammar. A glyph token is the glyph, one ASCII space, the payload — the same bytes in a comment, in anchor: and in decided_by:, so a node file quotes the code verbatim.

slottoken
landmark// ※ refund-cap
claim// ⊨ billing/refund-cap
anchor:※ refund-cap
decided_by§ 0007 — resolves against the repo's ADR directory
region<!-- @stele [name] --> … <!-- @end --> — name defaults to router; one per file, no nesting

The assertion suite

Six classes over lock vs. code. stele check exits nonzero with findings; --only <class> narrows it, --run-commands actually executes documented commands, --freeze accepts the current structure as the baseline, and --json gives the machine envelope.

classcatches
referentialanchors pointing at symbols, landmarks, or files that no longer exist
structuraldeclared dependencies the code doesn't have — and code dependencies never declared
exhaustivenessdirectories no node covers — unrouted context an agent can't navigate to
budgetnode docs exceeding their token budget
freshnessanchored code changed since the claim was last verified (stele blame <node>/<slug> shows who and when)
livenessdocumented commands: that no longer resolve or run

stele emit --check is the seventh gate in practice: it fails when committed AGENTS.md files diverge from what the lock would render.

Query surface

Plain subcommands and flags — no query grammar. The cross-cutting queries are the capability nested files cannot provide at any size: the transpose of the whole graph, materialized from one lock.

stele root                                  # the system node — start here
stele unfold payments --depth 2             # a node with its children
stele invariants --touching src/charge.rs   # claims governing the file you're editing
stele hazards                               # every active hazard
stele nodes --kind container                # the graph by kind
stele blame payments/idempotent-charges     # provenance of one claim

For MCP-capable harnesses, stele serve exposes the same read verbs as tools:

{ "mcpServers": { "stele": { "command": "stele", "args": ["serve"] } } }

Undercover mode

v0.2.0's headline. When you're the only stele user on a shared repo — a work, upstream, or open-source checkout whose collaborators must never see a stele artifact — run the graph privately.

stele init --undercover

Artifacts

The repository carries the whole design; this page is a summary of the first two.

artifactwhat it is
GUIDE.mdthe adoption path — install, scaffold, anchor, build, enforce, query, undercover
SPEC.mdthe contract — model, sources, assertion suite, query surface, process contract
EXAMPLE.mdworked example on a fictional monorepo: every artifact, an agent session with token accounting, a CI failure gallery
research/report.mdthe evidence base — cited survey the design decisions trace to
jgeschwendt/stelethe engine — a Rust single binary, self-hosting on its own graph

stele v0.3.0 · SPEC Draft 0.10 · SPEC.md and GUIDE.md are the contract.