Intent Continuity
An agent sees its task prompt and the workflow’s prompt templates, but not the standing constraints and decisions that govern the project — “never bump the major version”, “always review an agent’s changes before merging”. Those live scattered across CLAUDE.md, design docs, old transcripts, and commit messages, and an agent that doesn’t rediscover them re-litigates settled decisions or violates them.
Intent continuity extracts those durable statements once into plain files under .cloche/intent/ and injects the ones relevant to a given step into the agent’s prompt automatically.
A project with no .cloche/intent/ directory gets no injection, no warnings, and byte-identical prompts to one that never adopted the feature. By default a scan runs automatically after the first completed main orchestration task (intent.scan_after_tasks = true); set it to false in config.toml to keep the feature fully manual.
Concepts
| Concept | Description |
|---|---|
| Requirement | One durable statement of intent: a constraint (“never X”) or a decision (“we use Y because Z”). Has provenance, scope, status, and confidence. |
| Domain map | The project’s major architectural systems, each with a name, description, and path globs. Discovered by a scan; user-editable. |
| Provenance | Where a requirement came from: a source kind (doc, transcript, prompt, commit, user) plus a locator (file and heading, run ID, task ID, commit SHA). |
| Scope | What it applies to: project (always injected) or one or more domains, optionally narrowed by path glob or language. |
| Status | active, disabled (a user turned it off), or superseded (newer evidence replaced it; points at its successor). Only active requirements are injected. |
| Retrieval hints | Short phrases describing situations where a requirement applies, in the words someone would use when they hit the problem. Embedded for semantic search, never shown to an agent. |
Storage
Files are the source of truth — checked into git, reviewable in PRs, editable in any editor. The daemon parses them on demand.
.cloche/intent/
├── domains.yaml # discovered domain map
├── requirements/
│ ├── req-a3f8.md # one file per requirement
│ └── req-b91c.md
└── scan-state.yaml # extraction cursors (last scanned commit, docs, runs)
A sibling .cloche/intent-index/ holds the embedding index — a derived, gitignored artifact that re-embeds any requirement whose content hash or model ID no longer matches.
Requirement file format
Markdown with YAML frontmatter: the body is what the agent reads, the frontmatter is what the machinery reads.
---
id: req-a3f8
status: active # active | disabled | superseded
superseded_by: "" # req id, when status == superseded
scope:
level: domain # project | domain
domains: [versioning]
paths: [] # optional narrowing globs
languages: [] # optional, e.g. [go]
hints: # retrieval-only phrasings; embedded, never injected
- "when to bump minor vs build number"
- "is this change a breaking change"
confidence: high # high | medium | low
user_edited: false # true once a human touches statement/scope
provenance:
kind: doc # doc | transcript | prompt | commit | user
ref: "CLAUDE.md#versioning"
extracted_at: 2026-09-13T10:00:00Z
extracted_by: intent-scan
created: 2026-09-13T10:00:00Z
updated: 2026-09-13T10:00:00Z
---
Never bump the major version unless explicitly told to. Minor bumps are for new major
features and backward-incompatible changes; everything else bumps the build number.
**Why:** Major releases are batched manually at the maintainer's direction.
IDs are req- plus four hex characters and never change, so each requirement keeps its own git log --follow history across rewordings.
Domain map format
# .cloche/intent/domains.yaml
version: 1
domains:
- name: versioning
description: Version string management, release process, changelog.
paths: ["internal/version/**", "docs/plans/*release*"]
Edit it directly, through cloche intent, or in the web console. A scan proposes additions but never removes or rewrites a domain marked user_edited: true.
Extraction: cloche intent scan
Extraction is an agent job, run through Cloche itself. intent-scan is a built-in host workflow compiled into the cloched/cloche binaries, so it works in any project with no setup. cloche intent scan (an alias for cloche run intent-scan) dispatches its six steps:
- discover-domains — surveys the repo layout and proposes or updates
domains.yaml. - collect-sources — deterministic, no LLM. Gathers what changed since the last scan: docs (
CLAUDE.md,README*,docs/**/*.md,.cloche/prompts/**), unmined run transcripts and task prompts, and new commits. Emitsnonewhen nothing is new, so a quiet re-scan is a no-op. - extract — writes candidate requirements: statement, rationale, scope, confidence, provenance, and 2–5 retrieval hints each. Only durable, prescriptive intent qualifies — not task-specific instructions or facts derivable from the code.
- reconcile — decides per candidate: create (novel), merge (already tracked), supersede (contradicts an active requirement with newer evidence; a new file is created and the old one flipped to
superseded), or drop. - apply-reconcile — writes those decisions and enforces three hard rules regardless of what the agent proposed:
- A
disabledrequirement is never re-enabled or superseded away. - A
user_editedrequirement’s statement and scope are never rewritten in place — onlysupersedemay touch it, and only its status. - Nothing is ever deleted;
supersededis the terminal state.
- A
- commit — commits
.cloche/intent/and only that path (nevergit add -A), so a scan never leaves the worktree dirty. No-ops when nothing changed.
cloche intent scan # incremental: only material since the last scan
cloche intent scan --full # force a full domain re-survey
A project can override the built-in by defining its own intent-scan workflow in a .cloche/*.cloche file (to swap the agent or tune timeouts) — project-defined workflows always win over built-ins of the same name.
Incremental scans on task completion. With intent.scan_after_tasks = true (the default), an incremental scan is enqueued after each completed main orchestration run, covering just that task’s prompt, transcript, and commits. These are cheap, and a project is never scanned twice in parallel.
Injection
When assembling a prompt for an agent step, the daemon selects requirements in three stages:
- Status — only
activerequirements are visible. - Deterministic scope match —
level: projectrequirements are always included.level: domainrequirements are included when the domain matches the step’s context: domains whosepathsoverlap the workflow’s declaredrepos, or an explicitdomains = [...]step/workflow config key. - Semantic retrieval — the task description and step prompt are embedded and scored by cosine similarity against every active requirement (statement + rationale + hints). Anything above a per-model similarity floor joins the selection, whether or not deterministic scoping caught it — so a relevant requirement surfaces even with no keyword overlap.
Selected requirements are ordered project-level first, then by similarity score (deterministic-only matches rank by confidence, then recency), and rendered as:
## Standing project requirements
These are established constraints and decisions for this project. Follow them unless
the task explicitly overrides one. Requirement IDs like [req-a3f8] are for reference;
one you believe is wrong or outdated should be flagged in your output, not silently
ignored.
- [req-a3f8] Never bump the major version unless explicitly told to. ...
- [req-b91c] (container-runtime) Containers must be seeded from a clean per-run ...
A token budget (intent.token_budget, default ~2000 tokens) truncates from the bottom of the ranked list with a (N more requirements omitted; run cloche intent list) marker.
Where the block goes
- If the step’s resolved prompt contains
{{ $intent }}, the block is rendered there. - Otherwise (the default) it is auto-prepended to the prompt.
Both host and container agent steps receive the block. For container steps, the daemon seeds it into the run’s KV store as intent before dispatch, so uncommitted edits to .cloche/intent/ apply even though the container is seeded from a clean git snapshot. Injected requirement IDs are recorded per step in the run KV as <workflow>:<step>:intent, so a run’s detail view can show exactly what an agent was told.
Opting out
Set intent_tracking = false at the workflow or step level:
workflow "develop" {
intent_tracking = false
...
}
step implement {
prompt = file(".cloche/prompts/implement.md")
intent_tracking = false
results = [success, fail]
}
An opted-out step or workflow also has its logs excluded from collect-sources’ transcript mining, so sensitive output never seeds requirements. To disable injection project-wide, set inject = "off" in the [intent] table of config.toml.
Embedding backends
Semantic retrieval runs entirely locally and daemon-side — no tokens and no network calls at injection time. Adapters resolve in this order (first available wins):
| Adapter | Description |
|---|---|
onnx (default) |
In-process inference via ONNX Runtime, built into release builds of cloched. Default model: all-MiniLM-L6-v2 (384 dims, ~90 MB, ~10 ms/text on CPU), downloaded on first use to ~/.cache/cloche/models/, checksum-pinned. |
ollama |
Calls a local Ollama server’s /api/embed endpoint, for platforms without the ONNX build or users already running Ollama. |
keyword |
Token-overlap scoring, no model, always available. A degraded fallback — embeddings roughly double recall over keyword overlap. |
intent.embedder in config.toml pins resolution to one adapter; unset uses the chain above. When no embedder is available, selection degrades gracefully to status + deterministic scoping + keyword retrieval, with a one-time warning in the daemon log. The feature never blocks a run on embedding availability. Only cloched links an embedder; cloche, clo, and cloche-agent stay pure Go.
CLI: cloche intent
| Subcommand | Description |
|---|---|
list |
Table of id, status, scope, source, and statement. Filter with --domain or --status. |
show <id> |
Full statement, rationale, scope, provenance, and history for one requirement. |
edit <id> |
Opens $EDITOR on the raw markdown file, then marks it user_edited. |
disable <id> / enable <id> |
Toggles status between disabled and active. |
add "<statement>" |
Creates a new requirement with provenance kind=user. Project-scoped unless --domain is given (repeatable). |
preview |
Renders the exact block a given --workflow/--step/--prompt would receive, using the same selection and formatting code the daemon uses. |
scan [--full] |
Dispatches the built-in intent-scan workflow. |
All mutations are plain file edits under .cloche/intent/, visible in git diff; only scan talks to the daemon. preview is the first thing to reach for when selection surprises you. See the CLI reference for full flag detail.
Web console
Press i or click the Intent header button in the web console to open the Intent view over the current project: the requirements table with an edit drawer, the domain editor, and a Scan now button that shows the dispatched run’s id and live state, with a link to that run in the stack.
The Ledger overlay (l) has a Requirements section cross-referencing requirements with the tasks whose steps ran with them injected, and the reverse index from task to requirement IDs.
Configuration
The [intent] table in config.toml (see the configuration reference):
| Key | Default | Description |
|---|---|---|
embedder |
(unset) | Pins the adapter chain to "onnx", "ollama", or "keyword". Empty uses the default chain order. |
model |
(unset) | Reserved for overriding the onnx adapter’s model; not yet wired in. Use the CLOCHE_INTENT_MODEL environment variable today. |
token_budget |
0 |
Overrides the ~2000-token default selection budget. Zero means “use the default”. |
inject |
(unset) | "off" disables auto-prepending requirements to agent-step prompts project-wide. |
scan_after_tasks |
true |
Enqueue an incremental intent-scan after each completed main orchestration run. |
Adopting on an existing project
cloche init never creates .cloche/intent/. With scan_after_tasks at its default, the first completed main task bootstraps it automatically. To adopt by hand instead:
cloche intent scan --full
cloche intent list
Review what was extracted (cloche intent show <id>), edit or disable anything wrong, and commit .cloche/intent/. From then on injection is automatic and incremental scans keep it current.