Appearance
State & Files
The Project-State tree under .nubos-pilot/ is the only place workflows write end-user data. Every file here either is the source of truth for some unit-type, or is a derived artifact regenerated from one.
Top-level layout
.nubos-pilot/
├── PROJECT.md # core value, constraints, current focus, decisions log
├── REQUIREMENTS.md # REQ-NN active / validated / out-of-scope
├── roadmap.yaml # canonical roadmap source (parsed by yaml@^2.8)
├── ROADMAP.md # rendered from roadmap.yaml; do not hand-edit
├── STATE.md # current phase, current task, session metadata
├── config.json # runtime, model_profile, branching, parallelization, …
├── phases/
│ └── <NN>-<slug>/ # one directory per phase
├── todos/
│ └── pending/ # captured-on-the-fly Todos
├── backlog/ # deferred Backlog items
├── checkpoints/ # per-task crash-safety pointers
├── metrics/ # *.jsonl append-only metrics log
├── threads/ # cross-session thread CRUD
├── archive/v<X.Y>/ # completed-milestone archive (np:cleanup)
├── .last-session # pointer to last session report
├── .install.lock # installer single-writer lockfile
└── .tmp/ # init payloads >16KB are spilled here as @file:<path>Source of truth vs derived
| File | Source of truth | Derived |
|---|---|---|
roadmap.yaml | ✓ | |
ROADMAP.md | rendered from roadmap.yaml | |
PROJECT.md | ✓ | |
REQUIREMENTS.md | ✓ | |
STATE.md | ✓ | |
config.json | ✓ | |
phases/<NN>/<NN>-<MM>-PLAN.md | ✓ | |
phases/<NN>/<NN>-<MM>-PLAN-REVIEW.md | ✓ (append-only) | |
phases/<NN>/<NN>-VERIFICATION.md | ✓ | |
phases/<NN>/<NN>-DISCUSSION-LOG.md | ✓ | |
phases/<NN>/<NN>-CONTEXT.md | ✓ in adaptive mode; derived from QUESTIONS.json in power mode |
np:undo of a phase regenerates derived files; only source-of-truth files need to be edited by hand if you ever need to escape the workflow.
Phase directory
A phase directory is the most structured thing in the state tree. Only two file shapes are parser-mandatory:
<NN>-<MM>-PLAN.md— read bylib/plan.cjs.tasks/<task-id>.md— read bylib/tasks.cjswhen the directory exists.
Every other file (<NN>-CONTEXT.md, <NN>-RESEARCH.md, <NN>-VERIFICATION.md, …) is workflow-produced and read by explicit path with ENOENT-tolerance — missing means "that step has not run yet". Full table in the Phase Directory Layout reference.
Single-writer guarantee
Every workflow that mutates .nubos-pilot/ acquires a file lock through lib/core.cjs.withFileLock. The implementation:
- Uses
O_EXCLwrites to a<file>.locksidecar — atomic on every supported filesystem. - Embeds
pid + hostname + acquiredAtin the lock payload for diagnostics. - Stale-detects locks older than 30 s from a different host, forcibly recovers.
- Registers a
process.on('exit')cleanup so a crashed process does not leave a stuck lock.
Concurrent workflow runs queue rather than race. There is no daemon to coordinate them — the lockfile is the only coordinator (ADR-0001).
Atomic writes
Every state mutation routes through atomicWriteFileSync(path, content):
write(tmp + .pid.rand.tmp) → renameSync(tmp, path)A crash mid-write leaves either the old content or the new content — never a half-written file.
Checkpoints
.nubos-pilot/checkpoints/<task-id>.json is the per-task crash-safety pointer. The executor transitions through pending → in-progress → verifying → pre-commit via np-tools.cjs checkpoint transition. On a successful commit-task, the checkpoint is deleted. On a crash, np:resume-work reads the checkpoint to classify the session as resume, orphan, or clean.
Metrics
.nubos-pilot/metrics/*.jsonl is append-only. Every workflow can record events through np-tools.cjs metrics record. np:stats aggregates them; np:session-report summarizes since .last-session.
Gitignore guard
commit-task and commit route through lib/git.cjs.assertCommittablePaths(). The guard hard-fails when all declared paths are gitignored (D-25) and warns when only some are (D-26). This is what stops the executor from silently producing empty commits.
