v2.145.1

Workflow

The order C3's managed instructions ask every agent to follow. Hooks enforce the read/write parts — skipping straight to native tools causes denials.

📊 Visual Overview

Scroll inside the diagram to zoom · drag to pan · built with Excalidraw

Loading diagram… requires internet connection (CDN)

🗺️ Overview
⚠️
Enforced by hooks Native tools (Read, Grep, Glob, Edit, Write) are blocked by PreToolUse hooks unless a c3_* tool was called first. Violating the workflow order causes immediate tool denials — not warnings.
#StepToolWhen
1 Recall c3_memory(action='recall') Before any multi-step or context-dependent task
2 Search c3_search(action='code|files|semantic') Before ANY file discovery or content search
3 Map + Read c3_read(file_path) (map) → c3_read(symbols=…) Before reading any file content
4 Impact c3_impact(target='symbol') Before editing shared symbols — check blast radius
5 Edit c3_edit(file, old, new, summary) All file modifications
6 Filter c3_filter(text=…) Terminal output >10 lines or log files
7 Shell c3_shell(cmd, cwd='', timeout=60) Tests, git, builds, scripts — instead of native Bash. c3_shell_job for anything that outruns one tool call
8 Validate c3_validate(file_path) After every edit; type check auto-runs if pyright/tsc installed
9 Session c3_session(action='log' | 'snapshot' | 'note') Log decisions, snapshot before /clear, leave a note before stopping
10 Delegate c3_delegate(task, task_type, ...) Bounded subtask to your own provider one tier down, or c3_agent(workflow=...) for a multi-step pipeline. Full guide →
11 Local CI c3_ci(action='run') Run this repo's real .github/workflows here before pushing. Only FULL_CI_PASS means safe to push. Full guide →
+ Bitbucket c3_bitbucket(action='list_prs') v2.30.0 — see/act on PRs, branches, builds, repo admin on enterprise Bitbucket Data Center. Full guide →
+ Jira c3_jira(action='my_issues') v2.56.0 — search, create, transition, comment on Jira Cloud/Data Center issues. Full guide →
+ Credentials c3_shell(cmd, env_creds='NAME') v2.58.0 — use a vault secret by name; it never enters model context. Full guide →
+ Cross-project c3_project(action='list') v2.31.0 — discover and operate on OTHER c3-installed projects; reads run free, writes need allow_write=true. Tool card →
+ Sub-projects c3 sub add <folder> v2.96.0 — link child projects anywhere on disk, nested up to 8 levels; federated search/memory + Hub management. Section →
+ Confirm holds c3_override(action='wait', request_id=...) v2.97.0 — a [c3-access:confirm] refusal is a pause, not a block. Section →

1
Recall Memory
Recover context from previous sessions before doing anything
c3_memory

The first thing Claude should do at the start of any non-trivial task. This loads relevant facts from the persistent memory store — architecture decisions, conventions, ongoing work, gotchas discovered in prior sessions.

c3_memory(action='recall', query='authentication system refactor')

When to recall

  • Start of any multi-step task
  • When the task touches a system you've worked on before
  • When you suspect there are architecture decisions or gotchas to be aware of

When you can skip

  • Trivial one-off tasks (rename a variable, fix a typo)
  • Brand new projects with no prior sessions
💡
Use a specific query matching your task — "recall" pulls the top-k most relevant facts by semantic similarity. A vague query returns less useful results.
2
Search First
Always discover files and code via c3_search — never Grep/Glob directly
c3_search
ActionUse case
code Default. SQLite FTS5/BM25 fused with the embedding index by reciprocal rank — find functions, classes, patterns by content
lexical The BM25 ranking alone, no fusion
files File discovery — exact name, glob, or substring
semantic Embedding-based similarity search alone — find conceptually related code
exact Regex over every indexed file, definitions printed first — when you know the literal string or pattern

Filter any action with path=, lang=, kind= (comma-separated). Full reference: c3_search tool card →.

❌ Wrong
# Never start with native tools
Grep(pattern='AuthMiddleware')
Glob(pattern='**/*.py')
✅ Correct
c3_search(query='AuthMiddleware', action='code')
c3_search(query='*.py auth', action='files')

prefetch=True

Pass prefetch=True to auto-read the top result files, saving a round-trip:

c3_search(query='session manager', action='code', prefetch=True)
3
Map then Read
Get a structural overview first, then read only what you need
c3_read

Reading a whole file wastes tokens. c3_read(file_path) called with no symbols/lines IS the map: one line per symbol with its signature and [La-Lb] range, at roughly 10% of the file's tokens. A directory path maps its files instead (2.123.0). c3_compress left the MCP surface in 2.124.0 — its six modes collapsed into this one canonical map, since telemetry showed almost nothing used the other five. There is no separate compress step any more: map, then read the symbols or lines you need.

❌ Wrong
# Reading entire files
Read(file_path='services/session_manager.py')
✅ Correct
# Map → targeted read
c3_read(file_path='services/session_manager.py')
c3_read(file_path='services/session_manager.py', symbols=['SessionManager.save'])

Directories, batches, and large files

CallWhat it returns
c3_read('services/') A directory map: one line per file, up to six top symbols each, most-recently-edited first (capped at 400 files)
c3_read('a.py,b.py') A batch map of several files in one call
c3_read(file_path, lines=[a,b]) The exact line range — the only way into a 2 MB+ file, which isn't parsed for a map
💡
A file whose map would cost more tokens than its source is served whole instead. See docs/file-map.md for the grammar and the retired-mode table.
4
Impact Check
Know the blast radius before editing shared symbols
c3_impact

Before renaming, removing, or changing the signature of any function or class used across files, run c3_impact to find every call site. A HIGH risk result means all those files need updating in the same edit pass — skipping this step is how silent breakages happen.

# Check blast radius before renaming a shared function
c3_impact(target='handle_memory', file_path='cli/tools/memory.py')

# Check what's affected AND has uncommitted changes
c3_impact(target='MemoryStore', mode='unstaged')

When to skip

Skip c3_impact when editing purely local symbols (private helpers, inner functions, one-off scripts). Use it for anything in a shared service, public API, or imported across files.

⚠️
A HIGH risk result (7+ files) means the edit isn't isolated — plan to update all call sites in the same session before reporting done.
5
Edit via C3
Atomic read-patch-write-log — always c3_edit, never native Edit
c3_edit

c3_edit does read + patch + write + edit ledger log in one step. It also validates the old_string match before writing. If the literal match fails but the strings differ only by unicode lookalikes (curly quotes, em/en-dashes, NBSP), c3_edit retries with a 1:1 normalized match and tags the response [unicode-normalized].

c3_edit(
  file_path='services/auth.py',
  old_string='return token.expiry > now',
  new_string='return token.expiry > now + timedelta(seconds=CLOCK_SKEW)',
  summary='Add clock skew buffer to token expiry check'
)

Batching edits to the same file

Use the edits=[] parameter to apply multiple patches to one file in one call:

c3_edit(
  file_path='src/config.py',
  edits=[
    {"old_string": "DEBUG = True",  "new_string": "DEBUG = False"},
    {"old_string": "HOST = 'localhost'", "new_string": "HOST = '0.0.0.0'"}
  ],
  summary='Production config hardening'
)

Parallel edits across files

# Call c3_edit for multiple files in parallel (same message, different files)
c3_edit(file_path='src/a.py', old_string='...', new_string='...', summary='...')
c3_edit(file_path='src/b.py', old_string='...', new_string='...', summary='...')
6
Filter Output
Extract signal from noisy terminal output and log files
c3_filter

Any terminal output longer than ~10 lines should be passed through c3_filter to extract only the relevant signal before including it in context.

# Filter raw terminal output
c3_filter(text="<long test output here>")

# Filter a log file
c3_filter(file_path=".c3/sessions/session_001.json", pattern="error|warning")
💡
c3_filter is especially useful after running tests, build commands, or any operation that produces verbose output. It strips noise and returns structured signal.
7
Shell
Tests, git, builds, scripts — instead of native Bash
c3_shell

c3_shell(cmd, cwd='', timeout=60) returns structured {exit_code, stdout, stderr, duration_ms}, auto-logs git-mutating commands (commit/add/merge/rebase/reset/restore/checkout) to the edit ledger, and keeps a response under an 18 KiB byte budget (whatever gets clipped is kept in a spill store you page back with output_id). It best-effort blocks the most catastrophic commands (rm -rf of /, a top-level system dir, or $HOME/~; fork bombs; whole-drive wipes) and soft-warns on --force, --no-verify, reset --hard — a guard, not a sandbox. Native Bash remains the fallback for interactive/TTY commands.

# Run tests — failure blocks and totals are kept first if output is long
c3_shell(cmd='pytest tests/ -v')

# Git mutation — auto-logged to the edit ledger
c3_shell(cmd='git commit -m "wire retry backoff"')
💡
Anything that outlives one tool call (a full test suite, a long build, a device command) belongs in c3_shell_job(action='start', cmd=..., timeout=1800) instead: it hands the command to a detached supervisor and gives you a job_id back immediately. A c3_shell timeout is never converted into a job. Poll with c3_shell_job(action='status'|'tail', job_id=...), then page the kept output the same way as c3_shell's. Prefer c3_ci (step 11) for this repository's own GitHub Actions workflows.
8
Validate
Syntax + type check every edited file before reporting done
c3_validate

Always call c3_validate after edits. Never report "done" without validation. A syntax error introduced silently is worse than no change at all. If pyright (Python) or tsc (TypeScript) is on your PATH, C3 also runs a deep type check automatically and surfaces type errors as advisory warnings alongside the PASS result.

# Single file
c3_validate(file_path='services/auth.py')

# Batch — comma-separated (runs in parallel internally)
c3_validate(file_path='services/auth.py,services/session.py,cli/c3.py')

Supported languages

Python (.py) JavaScript (.js .mjs) TypeScript (.ts .tsx) HTML (.html) CSS (.css) JSON (.json) TOML (.toml) YAML (.yml .yaml) Bash (.sh)
9
Session
Persist decisions and context so the next session — or the next person — starts informed
c3_session

Leave a note before stopping

v2.143.0 — before /clear or before you stop, leave a note: what this session did, what's next. It shows on the session's card in the Hub/Desk Sessions view.

c3_session(action='note', data='Wired the Shell workflow step', reasoning='Still need the Delegate step')

If this session supersedes, finishes, or abandons an earlier one, mark that one stale so nobody resumes a dead end — reasoning is required, and target takes a session id, an 8+ char prefix, or current:

c3_session(action='stale', target='a1b2c3d4', reasoning='restarted after a bad approach')

Full session catalog, resume flow, and the Hub Sessions tab: Sessions guide →

Log a decision

c3_session(action='log', data='Chose JWT over session cookies — easier horizontal scaling')

Snapshot before /clear

Always snapshot before running /clear. This saves the full session state so the next session can restore it.

c3_session(action='snapshot')
# Then in Claude Code: /clear

Auto-snapshot on session end

C3 automatically captures a snapshot when a session ends — including Ctrl+C, natural stop, and max-turn exits. This is handled by a Stop hook (hook_auto_snapshot.py) that fires after Claude's last response.

How it works: The Stop hook checks if the C3 UI server is running. If so, it calls POST /api/auto-snapshot which captures a full snapshot with live session state (decisions, files touched, memory facts, budget). If the server isn't running, it falls back to a lightweight file-based snapshot from persisted session data on disk.

Installation: Stop hooks are registered automatically by c3 install-mcp. For existing projects, re-run c3 install-mcp to add them. No manual configuration needed.

EventAuto-snapshot?Notes
Natural session endYesStop hook fires reliably
Ctrl+C (graceful)YesStop hook fires before exit
Ctrl+C (hard kill)PartialMay not fire if process killed instantly
/clearIndirectSession ends first, triggering the hook

Manual snapshots are still recommended before /clear for maximum reliability, but the auto-snapshot is a safety net.

Restore in new session

c3_session(action='restore')

Compact (one-step restart)

# Snapshot + summary + restore instruction in one step
c3_session(action='compact')
10
Delegate
Hand a bounded subtask to your own provider, one tier down
c3_delegate

For work that's local-model-sized (summarize a diff or log, explain a function, triage a traceback), c3_delegate sends it to a smaller model from the provider you already run on. In Claude Code that's Haiku by default (backend='host', tier='small'). scout=true lets the delegate look files up itself; write_paths='a.py,b.py' has Sonnet make a change you specified and hands back the diff to review.

# Summarize a diff on Haiku
c3_delegate(task='Summarize this diff for a changelog in three bullets', context='<diff>')

# A lookup the delegate does itself
c3_delegate(task='Which functions call charge_card, and where?', scout=True)

For a compound investigation that would otherwise be 5+ tool calls, use c3_agent(workflow='investigate' | 'review_changes' | 'prepare_context' | 'preflight' | 'validate_compress') instead. Full backend/tier reference: Delegate guide →.

💡
A [c3:delegate-hint] line on a tool response names a call worth delegating when your own work looks delegable. In Claude Code, c3 install-mcp also writes two subagents, c3-scout (Haiku, read-only) and c3-worker (Sonnet), so C3's hooks still apply inside them.
11
Local CI
Run this repository's real CI here, before pushing — not after
c3_ci

c3_ci reads .github/workflows/*.yml as the only source of truth; it does not define a second CI config. inspect shows the job DAG and what's runnable on this host; run executes in dependency order and SKIPS (never passes) a job whose dependency failed. Only FULL_CI_PASS means every job ran here and passed — the only verdict that means safe to push. PARTIAL_PASS is not a green light: something didn't run (another OS, an unsupported action, or a subset you selected).

# Before you push
c3_ci(action='inspect')
c3_ci(action='run', mode='required')

# Fix loop
c3_ci(action='rerun')
c3_ci(action='failures')

Engines: native runs jobs matching this host; act runs Linux jobs in a real container (real uses: actions included) when act + Docker are installed — a container run counts toward FULL_CI_PASS, a cross-OS one never does. Full reference: CI guide →.


🚫 Anti-patterns

These will cause hook denials or waste significant tokens:

✗
Starting with native file search/read — always use c3_search first, even for "quick" lookups.
✗
Using native Edit when c3_edit is available — c3_edit validates, logs to ledger, and is atomic. Native Edit does none of this.
✗
Reading entire files with native Read — always map with c3_read first, then read only the symbols or lines you need.
✗
Skipping c3_validate after edits — you will silently introduce syntax errors that break the next session.
✗
Retrying a c3_edit that errored or timed out — it may have already written. Send the same args to c3_edits(action='verify') first; a double-apply can corrupt a file whose new_string contains its old_string.
✗
Running /clear without a snapshot — the auto-snapshot Stop hook provides a safety net, but a manual snapshot before /clear captures richer context (task description, working files). Don't rely solely on auto-snapshot.
✗
Dumping long terminal output directly into context — always filter first with c3_filter.
✗
Skipping recall at session start — you'll rediscover decisions and conventions already documented in memory.
✗
Routing around a confirm hold — a [c3-access:confirm] refusal via c3_shell, another tool, or a native write is not a bug to work around. See Confirm Holds.

🙋 Confirm Holds

A refusal tagged [c3-access:confirm] is a pause, not a block — either the user set that path to "ask me first", or it's the builtin agent-config confirm tier (instruction docs, hooks, skills, MCP configs: writes to that whole set pause by default since v2.100.0, widened in v2.102.0 to cover shell writes too). Reads stay open; only writes hold.

  1. Read the refusal's tail. If it names a request id, C3 already filed it — wait on that id: c3_override(action='wait', request_id='...', timeout_s=180). The bare call waits only 60s; "still pending" is not a denial, so wait again or do unaffected work.
  2. If no request was filed, it names the surface that files one (c3_read, c3_edit, or a native tool) — use that surface rather than asking in chat.
  3. If a request could not be filed, obey the reason given: a denied-and-muted request means don't ask again; a rate limit means withdraw one or wait.
  4. Once approved, retry the same call once, on the same surface. Never retry before a decision, never re-file (duplicates collapse into the pending request), and never route around the hold via c3_shell or another tool.
⚠️
Most credential-vault, Tier-0, and catastrophic-shell-command denials are not escalatable at all — they're refused here and never shown to anyone. If a refusal didn't invite you to ask, don't ask; mark the step blocked and tell the user.

Full reference: Access guide →.


📐 Plan Mode
Plan Mode — Read-only tools only

In plan mode, all c3_* read tools work normally: c3_search, c3_read, c3_filter, c3_validate, c3_status, c3_impact, c3_memory (recall/index/fetch/query/list), c3_task reads, c3_artifacts reads, c3_ci (inspect/plan/status/failures/logs/runs/history/doctor). Skip edit, shell writes, and delegate write mode in plan mode.


🔄 Session Restart Cycle
Loading diagram…

Option A — Compact (one step)

c3_session(action='compact')
# → Claude gives you a restore command to paste after /clear
# → Run /clear
# → Paste the restore command

Option B — Manual (two steps)

# Step 1: Before /clear
c3_session(action='snapshot')

# Step 2: After /clear, in new session
c3_session(action='restore')

What's preserved across restarts

  • Memory facts (always persistent — not affected by /clear)
  • Session decisions and context notes (from snapshot)
  • Edit ledger (always persistent)
  • Code index (always persistent)

What's lost on /clear

  • Conversation history (by design)
  • In-flight reasoning (must be re-derived)
  • Unsnapshot session state

⚡ /terse — Terse Output Mode

Installed by c3 install-mcp into ~/.claude/commands/terse.md. Available in every Claude Code project after C3 setup. Reduces output token usage by stripping prose verbosity while leaving all technical content (code, paths, commands, URLs) unchanged.

💡
Complements C3's input-token compression (c3_read's map, c3_filter). Those shrink what Claude reads; terse mode shrinks what Claude writes.

Intensity levels

LevelEffectExample output
/terse lite Remove filler, keep grammar intact "Auth middleware handles token expiry at auth.py:42."
/terse (full) Drop articles, use fragments, cut transitions "Auth middleware: token expiry. See auth.py:42."
/terse ultra Telegraphic, maximum abbreviation "Token expiry → auth.py:42"

What gets suppressed

  • Preamble: "Sure, I'll...", "I've completed..."
  • Hedging: "I think", "it seems", "you might want to"
  • Trailing summaries that restate the diff
  • Filler conjunctions and transitional phrases

What stays exact

  • All code blocks, inline code, file paths, commands, URLs
  • Error messages and stack traces
  • Variable names, function names, type signatures

Exceptions — do not compress these

Terse mode applies to conversational prose only. The following must remain complete and precise regardless of terse level:

  • Memory saves (c3_memory add/update) — facts must be self-contained and fully worded; abbreviated facts are unsearchable
  • Session logs (c3_session log) — decisions need full reasoning to be useful in future sessions
  • CLAUDE.md / instructions files — rules must be unambiguous; never abbreviate directives
  • Planning and architecture responses — user reviews for correctness; include all steps and trade-offs
  • Error diagnosis — include full error text, file paths, line numbers, root cause
  • Tool call arguments — old_string, new_string, fact, summary must be exact; never truncate

Deactivate

Say "normal mode" or start a new session.

Terse Advisor — automatic nudge

C3 installs a Stop hook (hook_terse_advisor.py) that fires after each response turn. When a verbose response is detected and /terse is not already active, it prints a one-time nudge per session:

────────────────────────────────────────────────────
[C3] Verbose response (~950 chars). /terse saves ~50% output tokens.
     Type /terse to activate.
     Silence: c3 terse dismiss  |  Snooze 24h: c3 terse later
────────────────────────────────────────────────────

The advisor checks the transcript for recent /terse activations and skips if terse mode is already in use. Nudge state is stored in ~/.c3/terse_advisor.json.

CommandEffect
c3 terse dismissSilence the advisor permanently
c3 terse laterSnooze nudges for 24 hours
c3 terse resetClear all advisor state
c3 terse statusShow current advisor state

🌳 Sub-projects

A c3 project can claim another one as a sub-project. Each child keeps its own full .c3 (index, memory, config), and the link is recorded in three places at once: the parent config's subprojects[] array, a back-link in the child's config, and the hub registry.

Since v2.96.0 the child does not have to live inside the parent's folder, and hierarchies nest to 8 levels — a sub-project can have sub-projects of its own. There are two link kinds, and C3 picks from the path you give it:

KindWhere the child livesEffect on the parent's index
nestedInside the parent's folderThe child's subtree is excluded, so nothing is indexed twice
externalAnywhere — a sibling folder, another driveNone: the child was never inside the tree the parent scans
💡
Use sub-projects for monorepos and nested apps, and for the case that has no folder to nest: two checkouts of one product sitting side by side. Each child stays independently searchable with its own memory, while the parent can fan queries out across the whole family.
⚠️
No cycles Hierarchy is a strict tree: one parent per project. While a child had to live inside its parent, a loop was impossible by construction; now that it can live anywhere, every link is checked against the ancestor chain and refused if it would make a project its own ancestor.

CLI — c3 sub

CommandEffect
c3 sub link <path> [--name --init] Link an existing C3 project as a child — anywhere on disk, including another drive. Refuses a folder that is not already a project unless you pass --init.
c3 sub inspect <path> Read-only report on any path: is there a project there, what is in it, who already claims it, what it claims, and which nested projects underneath are not linked yet. Mutates nothing.
c3 sub add <folder> [--name --ide] Designate a folder as a child, initializing it. Adopts an existing .c3, otherwise runs a full init. --no-init links without initializing; --parent <path> targets a different parent.
c3 sub list Direct children with status and link kind (the default when no sub-command is given).
c3 sub tree [--depth N] The whole hierarchy, indented, with a subtree rollup. --depth stops the walk early.
c3 sub remove <ref> [--clear] Unlink a child and promote it to a standalone top-level project (its .c3 is kept). --clear also wipes .c3, uninstalls the MCP config and instruction docs, and deregisters it.
c3 sub run <update|reindex|health> [--include-parent --depth N] Cascade an operation across the whole subtree. --mcp also reinstalls the MCP config during update.
c3 sub check [--fix --prune] Reconcile the three-way links. --fix repairs from the parent config; adding --prune drops entries whose folder is gone.

Agent surface — the scope parameter

c3_search and c3_memory accept a scope parameter on parent projects:

ValueMeaning
''Current project only (default)
'all'Fan out across the parent and every descendant, at any depth
'<child>'One specific descendant by name
c3_search(query='auth middleware', scope='all')
c3_memory(action='recall', query='payment gotchas', scope='billing-service')

With hybrid.subprojects.memory_rollup enabled, a parent-side recall also unions in the children's facts automatically.

Hub UI management (v2.57.0)

Everything the CLI does, plus the operations that need multi-step safety, is available in the Hub:

  • "Link project by path…" (v2.96.0) — kebab menu on any card, at any depth. Browse anywhere or paste a path; C3 inspects it before anything happens and shows you what you are about to claim: the project's name and version, whether the hub already knows it, how much is in it, who already claims it, and what sub-projects it would bring with it. One confirm registers and links an unregistered project. Unlinked nested projects it finds are offered as suggestions, never applied.
  • Designate — kebab menu → "Designate sub-project…" on any project card, or from the drill-in Sub-projects tab. A folder picker fenced to the parent's subtree validates the choice, shows adopt-vs-initialize expectations up front, and offers an instruction-docs/IDE picker (auto | claude | vscode | cursor | codex | grok | antigravity).
  • "Make sub-project of…" — the reverse move: pick any registered project to sit under. The only projects excluded are this one and its own descendants.
  • Sub-projects drill tab — per-child rows with inline Validate and Promote; inline Reconcile with per-issue detail and planned repair actions; a Cascade launcher with operation picker, include-parent toggle, affected-children preview, live progress, and Cancel.
  • Link health, passively — parents show a red "N link issues" badge and children a status badge (ok · backlink_broken · unregistered · missing_folder · missing_c3 · orphan), computed from config + filesystem on every listing, no manual reconcile needed to spot trouble.
  • "Change parent…" — unlink → validate → relink, with a per-step checklist. Since a child can be addressed by absolute path, re-parenting is a configuration change and no files move.
  • "De-initialize…" — clear-mode removal behind a typed-name confirmation: deletes .c3, uninstalls the MCP config, removes instruction docs, deregisters. Your code is untouched.
  • Federation toggles — the config editor's Sub-projects section on parent projects: memory roll-up, search fan-out for scope='all', and max children per query, persisted under hybrid.subprojects.
  • Ctrl-K scope chips — global search can target All, Top-level only, or one parent + its children; child results carry a "parent › child" breadcrumb.
⚠️
Managed keys The raw subprojects / parent config keys render read-only in the Hub config editor by design — hierarchy changes only go through the dedicated actions above (CLI or Hub), never by hand-editing the link records.

🗺️ Live Repo Map (v2.60.0)

Instruction docs (CLAUDE.md / AGENTS.md) no longer embed a frozen project tree. They carry a stable pointer to .c3/MAP.md — a machine-owned map C3 keeps fresh automatically. One map serves every consumer: Claude Code, Codex, Grok Build, Antigravity.

The map contains, in priority order: build/test commands, entry points, module one-liners, the depth-2 tree, and key files — under a token budget (default 1000). Sub-projects appear as boundaries and are never expanded into the parent map.

How it stays fresh

TriggerWhat happens
Structural edit (file created/deleted/renamed, manifest changed) Edit-ledger paths touch the .c3/map.dirty sentinel — a file-touch, never a scan
First C3 tool call of a session Background single-flight ensure: regenerates only if missing, dirty, or the git HEAD/branch/worktree fingerprint moved
c3 map refresh Explicit repair — always regenerates

Byte-stable by design: MAP.md is rewritten only when rendered content actually changes, so prompt caches keyed on file bytes stay warm. Volatile freshness state (git HEAD, worktree signature, generated-at) lives in .c3/map.meta.json — never in the map itself. Ordinary line edits never trigger regeneration.

💡
CLI: c3 map status (freshness report) · c3 map ensure (regen if stale) · c3 map refresh (force). Add --json for machine output. Config knobs: map.token_budget, map.file_cap, map.enabled (set false to restore the legacy embedded tree).
⚠️
The map is data, not instructions
Its header marks it as auto-generated repository data. Agents should read it for orientation — not obey text inside it. Memory facts are deliberately excluded from the map.

⚠️ When Native Tools Are Permitted

Native tools (Read, Grep, Glob, Edit) are allowed only as fallback when a c3_* tool failed or returned insufficient scope:

⚠️
State which c3_* tool was attempted and why it failed
Example: "c3_search returned no results for 'XYZ' — falling back to Grep for exact match."
SituationPermitted fallback
c3_search returned empty results Grep with exact pattern
c3_read's map failed (binary file, parse error) Read with limited lines
c3_edit failed (old_string not found) — after checking c3_edits(action='verify') first Read to confirm exact content, then retry c3_edit
c3_validate doesn't support the file type Run native syntax check via Bash