Chain files: naming, roles, and resolution
Every chain file Contenox ships follows one grammar:
chain-<role>-<variant>.json
The role says what kind of work the chain does and therefore where it is selectable. The variant distinguishes chains within a role — default, conservative, or any name you choose for your own. Roles communicate; nothing gates: a valid chain file is selectable in every context its role appears in. There is no registry to update and no approval step — the filename is the declaration.
One name everywhere is the rule behind the grammar: the name embedded in the binary, the name contenox init seeds to disk, and the name the docs use are the same string. When a page says chain-agent-acp.json, that is the literal file in ~/.contenox/.
The roles
| Role | What it does | Where it is selectable |
|---|---|---|
agent | A conversational, tool-using loop | Session and editor surfaces (--chain, the default-chain config, per-surface env overrides) and mission dispatch: chain-agent discovery declares every chain-agent-*.json file as a fleet-dispatchable agent |
planner | Holds and evolves a mission’s living plan | Mission planning: the default agent behind /mission and contenox mission fire |
oracle | Reviews routine mission questions in unattended runs | Mounted in-process by contenox mission fire --oracle — no trigger, no dispatcher, no second process; see The attention oracle |
compact | Summarizes conversation history | The compaction machinery: /compact in editor and terminal-UI sessions, contenox session fork --summary |
fim | Fill-in-the-middle completion | Editor autocomplete (_contenox/autocomplete) |
What every agent chain shares — the bounded ReAct loop inside it — is mapped in The agentic loop.
The seeded set:
chain-agent-contenox.json— interactive CLI chat:contenox chatand a barecontenox "..."chain-agent-run.json— the one-shot, statelesscontenox runloopchain-agent-acp.json— editor (ACP) sessionschain-agent-acpx.json— the headless / untrusted-driver ACP profilechain-agent-beam.json—contenox new, the terminal UIchain-planner-default.json— the default mission plannerchain-compact-default.json— history compactionchain-fim-default.json— editor autocompletechain-oracle-default.json/chain-oracle-conservative.json— the auto-attention oracle variants; seeded like everything else but inert untilcontenox mission fire --oraclemounts one (a beta-only flag)
trigger-*.json and hitl-policy-*.json are different kinds of files (event triggers and HITL envelopes) and keep their own conventions; init seeds no trigger files (triggers are operator-authored) and seeds hitl-policy-oracle.json — the attention oracle’s envelope — alongside the other policy presets.
Note: the fleet agent’s name is the chain’s
idfield, not its filename. The seeded planner’s id isagent-planner, socontenox mission fire agent-plannerand a storeddefault-mission-agentconfig are stable however the file is named.
Resolution: which file wins
For files resolved by name — the CLI chat chain, the run chain, the compact chain, trigger-referenced chains and policies — resolution is workspace-first:
- the workspace
.contenox/<name>(found by walking up from your current directory, like.git/), then ~/.contenox/<name>as the fallback.
A workspace file wins by name. That is the whole override mechanism: put a same-named file in the workspace .contenox/ and it shadows the global copy. contenox doctor lists every shadowing copy it finds.
The ACP surfaces resolve differently, by design: contenox acp, acpx, and contenox new load their chain from ~/.contenox/ only, each overridable with its own environment variable — CONTENOX_ACP_CHAIN_PATH, CONTENOX_ACPX_CHAIN_PATH, CONTENOX_BEAM_CHAIN_PATH, and CONTENOX_ACP_FIM_CHAIN_PATH for autocomplete. An editor may be launched from anywhere, so these surfaces anchor to the home directory rather than a cwd walk; the env var is the per-launch override.
What contenox init touches — and never touches
| Invocation | Touches | Never touches |
|---|---|---|
contenox init | Writes the .contenox/workspace.id marker (an existing marker keeps its id); seeds the chain files and hitl-policy-*.json presets into ~/.contenox/ only where absent; prints a note for every workspace copy that shadows a global file | Existing files (it never overwrites), workspace chain files, config, sessions, the database |
contenox init --local | Same seeding, into the workspace .contenox/ instead — deliberate overrides that shadow the global copies by name | ~/.contenox/ chain files |
contenox init --force | Overwrites every seeded chain file and rewrites the HITL presets across the whole search path (home and any shadowing workspace copy) — your edits to seeded files are replaced | User-authored files (anything init does not seed), config, sessions |
contenox init --update | First renames any shipped chain file still carrying a pre-v0.38 name to its new name (see below); then refreshes a seeded file only when its checksum matches a known unmodified prior build — edited files are skipped and reported | Hand-edited content (a rename moves bytes, the refresh skips them), user-authored files, config, sessions |
contenox init --refresh-policies | Rewrites only the hitl-policy-*.json presets from this build, in ~/.contenox and any workspace copy that shadows one — this is what contenox doctor points at when an envelope predates a shipped toolset | Chain files, config, sessions |
Migrating an old install
Before v0.38 the seeded files carried per-surface names (default-chain.json, default-acp-chain.json, headless-acp-chain.json, default-beam-chain.json, default-run-chain.json, default-fim-chain.json, chain-compact.json, agent-planner.json). Resolution now looks up only the new names. contenox init --update performs the migration:
- Each shipped legacy-named file is renamed to its new name, byte-for-byte — a rename, never a rewrite, so a hand-edited chain keeps your content under its new name.
- The rename runs in both
~/.contenox/and the workspace.contenox/: a workspace override left under its legacy name would silently stop shadowing. - If a legacy name and its new name both exist, the new file wins untouched; the legacy file is left in place with a one-line note, never deleted.
- The step is idempotent — a second
--updatefinds nothing to rename.
For example, chain-agent-acp.json (formerly default-acp-chain.json) is the editor chain after one contenox init --update.
Your own files are yours: --update never renames or rewrites anything init did not seed. One consequence of the clean cut is discovery — fleet chain-agent discovery keys on the chain-agent-* filename, so a custom agent chain you named agent-mybot.json under the old convention is no longer discovered. Renaming it to chain-agent-mybot.json is the whole migration; its agent name (the chain id) does not change.
Next
- Your first chain — author a chain from scratch
- Core concepts — chains, tasks, tools, transitions
contenox initreference — every flag