Skip to content

Disk contract

This is the authoritative description of what Agent NextUp writes to disk. Everything of record is a human-readable file — there is no database behind them. Two files inside .nextup/ are not text, and neither is a source of truth: the search index, which is a rebuildable derivative, and the encrypted secret store.

<project>/
AGENTS.md AI session entry point (pure index + an engine-maintained state block)
CLAUDE.md Thin shell whose one content line imports AGENTS.md (Claude Code reads only this name)
project.yaml Outward-facing aggregate manifest (phase/progress/updated synced line by line)
nextup_docs/00-protocol.md The session operating protocol — short, read on every takeover
nextup_docs/01-nextup-guide.md Complete file-contract guide (headless operating rules)
nextup_docs/modules/<module>.md One guide per *enabled* capability module; a module that is off
ships nothing, so the listing says which modules apply here
memory/MEMORY.md Cross-session memory index (one fact per file)
work_record/_TEMPLATE.md Monthly work record template
.mcp.json Hub connection settings for the agent host
(Agent NextUp points this at the absolute path of nextup-mcp; no manual PATH setup)
.agents/skills/ General-purpose workflow skills (/align-scope, /wrap-up,
.claude/skills/ /adversarial-review, /handoff-check) — the same files in both
Written at init to BOTH .agents/skills/ and .claude/skills/ as identical
real files (not a symlink); customisable, and once customised never
overwritten, while pristine copies upgrade along with the engine
.nextup/
context.json Project metadata, goals, boundaries (versioned by schemaVersion)
rules.json AI operating rules + self-improvement flywheel (with revision history)
workflow.json Execution harness: phases, AI instructions, exit gates, current position
agent_access.json Hub tool authorisation (a master switch gates the hub; with it on,
reads need no grant and writes are an explicit per-tool allowlist)
modules.json Enabled capability modules (collab / team / specs / prime).
File absent = none enabled
settings.json Workspace behaviour settings (auto-archive switch and day count).
File absent = on, 7 days
exchange/ Cross-project delivery envelopes (team module; created on the first
publish or the first arrival): outbox/ published and awaiting routing,
inbox/ arrived from an upstream. One <box>/<id>.json per envelope; when
attachments are present their bytes live in a sibling <box>/<id>/ folder
orchestrator.json LLM model registry + routing policy (dormant layer; nothing writes
or reads this today, so the file is usually absent)
mcp.json MCP client registry (archived design; usually absent)
.gitignore Excludes machine-local and rebuildable files
(secrets.enc, index.sqlite, .mutex)
.mutex Cross-process mutation lock (empty file; released automatically on OS crash)
index.sqlite FTS5 full-text index (rebuildable derivative; excluded from backups)
secrets.enc Encrypted machine-local key-value store (created empty; sealed with
a key held in the OS credential store under the service name
agent-nextup, which never leaves this machine, so the file itself is
excluded from git and from backups — an export re-encrypts its
contents under the backup passphrase as
.nextup/secrets.portable.enc instead)
ledger.jsonl Append-only event ledger
shipped_assets.json Content fingerprints of shipped engine curriculum
(the basis for telling "outdated" apart from "customised")
asset_backups/ Backups of curriculum files taken before an upgrade. Carries its own
.gitignore of `*` — the safety net protects the machine it was made
on and is deliberately never committed or exported
snapshots/latest_handoff.md The full handoff snapshot
specs/<capability>/spec.md Current-state specs (specs module; human-readable, git-trackable, indexed)
tasks/ One JSON per atomic task (T-0001.json…). Ships with a .gitkeep so the
empty directory survives a clone
tasks/T-XXXX/ Optional task artifact bundle: free-form artifacts plus
specs/<capability>/spec.md deltas, folded into the root specs/ on archive.
Deleting the task deletes this whole bundle
artifacts/ Deliverable outputs (also .gitkeep-seeded)

Not everything lives inside a workspace. These sit in your home directory and are shared by every project on the machine:

~/.nextup/ (or the directory NEXTUP_APP_DIR names, if it is set)
registry.json The project directory behind the Projects home screen
teams.json The team graph: members, flow edges, canvas layout, auto-send flags,
and which member (if any) coordinates the team
.mutex Cross-process lock for the two files above (empty; released on crash)
templates/ Custom workflow templates (drop a JSON file in to add or shadow a built-in)
agents.json The agent CLI catalog for the built-in terminal, including custom
entries and their environment variables (stored in plaintext)
terminals/ One JSON per persisted terminal session (metadata only — scrollback
is deliberately not kept across restarts)
⚠️ NEXTUP_APP_DIR moves registry.json, teams.json, .mutex and templates/ only.
agents.json and terminals/ always resolve against the real home directory,
so setting the variable splits the app-level store across two places.

The entry point is the root AGENTS.md, the filename any agent host can read. Claude Code reads only CLAUDE.md, so a thin shell there — its one content line is the @AGENTS.md import — pulls in the body: one place to edit, reachable from both. And it never goes stale:

  • The current state block in AGENTS.md is fenced by <!-- NEXTUP:STATE:BEGIN/END --> and the engine rewrites it on every task, decision, and phase change (one-line situation, this phase’s instructions, gate checklist, next step, blockers, recent decisions, the last progress note, and — where the module is on — the spec layer and the enabled module guides, closing with a pointer to the full snapshot). Handwritten content outside the block is never touched, and deleting the markers stops the engine touching the file at all.
  • In project.yaml only the phase, progress, and updated lines are replaced; every other field, comment, and manual edit is preserved — so an outward-facing aggregator always reads fresh data.
  • nextup_docs/00-protocol.md is the procedure — the eight steps of a takeover, kept short because it is re-read every session. nextup_docs/01-nextup-guide.md is the contract, read when it is needed: task and ledger JSON formats, gate semantics, headless advancement rules, the honesty principles, and which files must not be touched.
  • nextup_docs/modules/ holds one guide per enabled capability module. A module that is off ships no guide at all, so a workspace never carries the contract for a capability it does not have — and the directory listing agrees with the hub’s tool list by construction. Turning a module off leaves its file in place; it simply stops applying.
  • Hub tools: the Claude CLI connects to nextup-mcp automatically via .mcp.json (other vendors’ CLIs wire MCP up their own way); tasks, decisions, milestones, and advancement all go through tool calls. The engine keeps the ledger and handoff layer in sync, and every call — including denied ones — leaves an agent_tool_called audit entry. Gate overrides and manual confirmation are human-only and have no tool surface at all.
  • Legacy workspaces: Project settings → AI takeover layer → Fill missing files supplies whatever is absent (fills gaps only, never overwrites). Engine curriculum travels with the engine — the guide and the general-purpose skills record content fingerprints when shipped, so after an engine update the Tools page (or an agent, via the upgrade_workspace_assets tool) can upgrade untouched curriculum files in one press, backing them up first. Files you edited are never touched.

Every initialised project is a harness-driven state machine, not a pile of passive config:

  • Phase — defined by the template (plan → execute → review → post_mortem, for example). Each phase carries aiInstructions (what an arriving AI session should do) and exitGates.
  • Gate — evaluated by the engine against real state; an AI cannot self-report. See Gates for the full list.
  • Force advanceadvance_phase errors out when a gate has not passed. Forcing it requires a reason and is recorded to the ledger and history as an OVERRIDE.
  • Templates are data — five built-ins are embedded as JSON (bilingual: content follows the interface language, ids stay stable across languages, and a project is instantiated in whichever language is active at creation). Drop a JSON file into ~/.nextup/templates/ to add or shadow one, with no recompilation. The Templates page lets you add, edit, and delete custom templates visually.
  • Closed loop with the handoff — the snapshot’s first section states the current phase, and the third section opens with this phase’s instructions and gate list, so a new session immediately knows where it is, what to do, and what it has to pass.
  • Files are the source of truth. Tasks and settings are human-readable files: git-friendly, watchable. Indexes (SQLite FTS5, RAG) are only rebuildable derivatives.
  • Continuity contract. Every state change — task creation, transition, verification, decisions, milestones, rejections, phase advances, lessons — regenerates latest_handoff.md and the AGENTS.md state block. The snapshot is never older than the last operation.
  • No watcher feedback loop. The file watcher filters by allowlist (user-facing files such as tasks/, artifacts/, specs/, context and rules) and ignores the engine’s own derivative writes (snapshots, secrets, atomic temp files). The ledger is the one deliberate exception: an append emits its own signal — it feeds the notification surfaces but never means “re-read state”, and reading a tail writes nothing, so no loop closes.
  • Error model. One error type throughout — fallible paths return it rather than panicking — serialised as {kind, message} across both IPC and MCP. Collaboration rejections carry structured fields (dependencies_unmet with blockingTasks[], already_claimed with currentAssignee) so callers branch on fields rather than parsing message text.