Appearance
Artifact Schemas
Canonical on-disk format of the planning artifacts. Downstream agents (executor, verifier, plan-checker, nyquist-auditor) parse against these schemas. Breaking changes require a superseding ADR.
All artifacts live under .nubos-pilot/milestones/M<NNN>/ (milestone scope) or .nubos-pilot/milestones/M<NNN>/slices/S<NNN>/ (slice scope) or inside tasks/T<NNNN>/ (task scope).
| Artifact | Scope | Producer | Consumer(s) | Lifecycle |
|---|---|---|---|---|
M<NNN>-CONTEXT.md | milestone | /np:discuss-phase | planner, researcher, plan-checker, executor | rewrite on save |
M<NNN>-ROADMAP.md | milestone | /np:plan-phase (planner) | plan-checker, verifier | rewrite on save |
M<NNN>-META.json | milestone | /np:plan-phase (planner) | status displays | rewrite on save |
M<NNN>-RESEARCH.md | milestone | /np:research-phase | planner, plan-checker | rewrite on save |
M<NNN>-PLAN-REVIEW.md | milestone | /np:plan-phase loop | user, audit | append-only |
M<NNN>-VERIFICATION.md | milestone | /np:verify-work | /np:add-tests, user | rewrite on save |
M<NNN>-VALIDATION.md | milestone | /np:validate-phase | user | rewrite on save |
S<NNN>-ASSESSMENT.md | slice | /np:plan-phase (planner) | plan-checker, executor | rewrite on save |
S<NNN>-PLAN.md | slice | /np:plan-phase (planner) | plan-checker, scaffolder, executor | rewrite on save |
S<NNN>-UAT.md | slice | /np:plan-phase (planner) | plan-checker, verifier | rewrite on save |
S<NNN>-SUMMARY.md | slice | executor (last task of slice) | verifier | rewrite on save |
T<NNNN>-PLAN.md | task | /np:plan-phase (scaffolder from <task> block) | executor | rewrite on repromote |
T<NNNN>-SUMMARY.md | task | executor (after commit) | verifier | rewrite on save |
S<NNN>/TODO.md | slice | lib/todo.cjs::renderTodoMd (scaffolder + setTaskStatus hook) | human, executor, verifier | rewrite on every task-status transition |
handoffs/<ts>__<from>-to-<to>__<slug>__<id>.md | milestone or project-global | lib/handoff.cjs::writeHandoff (via handoff-write CLI) | target agent, filtered by to_agent | append on create, status mutated in place |
M<NNN>-CONTEXT.md
Captures user decisions from the adaptive interview. Read by every downstream agent.
Template: templates/milestone/CONTEXT.md. Required sections: <goal>, <domain>, <decisions> (D-01..D-NN), <deferred>, <canonical_refs>.
S<NNN>-PLAN.md
Slice plan with inline <task> blocks. This is what the scaffolder parses to produce per-task files.
Frontmatter:
yaml
---
slice: "M001-S001"
milestone: "M001"
type: plan
status: pending
requirements: ["REQ-AUTH-01", "REQ-AUTH-02"]
---Body sections:
<objective>— what this slice delivers.<context>— @-references to CONTEXT, RESEARCH, prior SUMMARY files.<tasks>— one or more<task>blocks (see below).<verification>— slice-level verify commands.<success_criteria>— per-slice SCs (separate from milestone-level SCs in roadmap.yaml).<output>— whatS<NNN>-SUMMARY.mdshould contain after execution.
<task> block schema
Every <task> block inside a slice PLAN must carry four attributes on the opening tag:
xml
<task id="M001-S001-T0001" depends_on="" wave="1" tier="sonnet">
<name>Seed login form</name>
<files>src/auth/LoginForm.tsx</files>
<read_first>
- src/auth/AuthProvider.tsx
</read_first>
<action>
Create LoginForm.tsx with email + password inputs. Wire it to useAuth().
</action>
<verify>
<automated>npm test -- LoginForm</automated>
</verify>
<acceptance_criteria>
- Form renders without runtime errors
</acceptance_criteria>
<done>LoginForm component committed, unit test green.</done>
</task>Attribute rules:
id— full-idM<NNN>-S<NNN>-T<NNNN>matching the enclosing slice.depends_on— comma-separated full-ids of earlier-slice tasks (never same-slice), or"".wave— integer equal to the slice number.tier—haiku | sonnet | opus.
If any of these is missing, the task is silently dropped when scaffold-all-tasks runs.
T<NNNN>-PLAN.md
Scaffolded from a slice's <task> block. Frontmatter matches TASK_REQUIRED_FIELDS from lib/tasks.cjs:
yaml
---
id: "M001-S001-T0001"
slice: "M001-S001"
milestone: "M001"
type: execute
status: pending
tier: "sonnet"
owner: executor
wave: 1
depends_on: []
files_modified:
- "src/auth/LoginForm.tsx"
autonomous: true
must_haves: {}
---Body mirrors the <task> block sub-elements (<read_first>, <action>, <verify>, <acceptance_criteria>, <done>, <output>).
TASK_ID_RE = /^M\d{3,}-S\d{3,}-T\d{4,}$/ — enforced in frontmatter validation.
M<NNN>-PLAN-REVIEW.md
Append-only audit of each planner → plan-checker iteration. Every iteration appends a YAML block:
markdown
## Iteration 1 - 2026-04-20T10:00:00Z
**Planner output:** S001-PLAN.md committed
**Checker verdict:** issues_found
**Findings:**
\`\`\`yaml
status: issues_found
findings:
- category: missing-success-criterion
severity: critical
target: M001-S001-UAT.md §login
message: No task covers the "locked account" acceptance case.
\`\`\`
**Planner response:** revisionPre-existing bytes must remain a SHA-256-verified prefix of post-append bytes. Abort never truncates.
M<NNN>-VERIFICATION.md
Schema-bound by lib/schemas/verification.cjs (ADR-0017). Frontmatter is the canonical machine-readable signal; body blocks must match ### SC-N: … (H3, colon). Drift is rejected by output-lint check --schema verification --enforce at write time inside /np:verify-work.
markdown
---
schema_version: 2
milestone: "M001"
milestone_name: "Auth & Basic UI"
verified: "2026-04-20"
milestone_status: verified
sc_total: 2
passed: 1
failed: 1
deferred: 0
pending: 0
---
# M001 — Auth & Basic UI — Verification
**Verified:** 2026-04-20
**Milestone Status:** verified
## Success Criteria
### SC-1: User can log in
- **Status:** Pass
- **Classified by:** verifier
- **Evidence:** src/auth/loginHandler.ts, test/auth.test.cjs "login happy path"
- **Notes:** —
### SC-2: Profile visible after login
- **Status:** Fail
- **Classified by:** verifier
- **Evidence:** src/profile/ (empty)
- **Notes:** No profile page shipped in M001; promoted to M002.Milestone Status derivation (kept in lib/verify.cjs):
- Any
Fail→failed. - Else any
Deferor unresolvedneeds_user_confirm→deferred. - Else (all Pass) →
verified.
Frontmatter invariant: sc_total === passed + failed + deferred + pending. The output-lint engine enforces this and rejects [object Object] titles.
M<NNN>-VALIDATION.md
Schema-bound by lib/schemas/validation.cjs (ADR-0017). The np-nyquist-auditor agent writes this; frontmatter holds the canonical counts. The aggregator in /np:close-project reads frontmatter, never body grep — see ADR-0017 §Context for the historical failure.
markdown
---
phase: 1
slug: auth-basic-ui
audited_at: 2026-04-20T14:30:00Z
requirements_total: 12
covered: 9
under_sampled: 2
uncovered: 1
nyquist_compliant: false
status: issues_found
---
## Summary
…
## Covered
…
## Under-Sampled
…
## Uncovered
…
## Remediation Guidance
…Invariant: requirements_total === covered + under_sampled + uncovered. Hard-gated by output-lint check --schema validation --enforce inside /np:validate-phase.
M<NNN>/research/spawn-<i>.md (per-spawn researcher output)
Schema-bound by lib/schemas/researcher-output.cjs (ADR-0018). One file per Stage-1 researcher spawn, written to .nubos-pilot/milestones/M<NNN>/research/spawn-<i>.md. A Reasoning field is required per Decision/Risk/Pattern; the reconciler uses Reasoning traces to classify agreement as orthogonal, overlapping, identical, or unknown.
markdown
---
schema_version: 1
agent: np-researcher
spawn_index: 0
seed_delta: -7
task_query_hash: "abc123..."
decision_count: 2
risk_count: 1
pattern_count: 1
open_question_count: 0
source_count: 2
---
## Decisions
### D-1: Use jose@6 for JWT signing
- **Rationale:** maintained, zero peer deps
- **Confidence:** high
- **Evidence:** [CITED: https://github.com/panva/jose v6.0.10]
- **Reasoning:** compared vs jsonwebtoken (deprecated) and jws (unmaintained); jose is the only modern actively-developed option.
## Risks # ### R-N with Severity, Mitigation, Reasoning
## Patterns # ### P-N with Description, Source-Type, Reasoning
## Open Questions # ### Q-N with Why-blocked
## Sources # ### S-N with Type, NotesEach of the five sections must be present (use _None._ if empty). Linted by output-lint check --schema researcher-output --enforce per spawn at workflow Step 4.5.
M<NNN>-RESEARCH.md (reconciler output)
Schema-bound by lib/schemas/research-final.cjs (ADR-0018). Written by np-researcher-reconciler after seeing all k per-spawn outputs plus the deterministic-merge proposal. Frontmatter exposes disagreement metrics that the workflow's Step 5.7 hard-gate reads.
markdown
---
schema_version: 2
milestone: "M001"
type: research
agent: np-researcher-reconciler
k: 3
agreement_score: 0.667
contested_count: 1
reconciler_verdict: issues_flagged
decision_count: 2
risk_count: 1
pattern_count: 1
open_question_count: 0
source_count: 3
---
## Reconciler Summary
## Final Decisions # ### D-N with Reconciled-from, Confidence, Reasoning-Trace-Agreement, Reasoning
## Contested Decisions # ### CD-N with per-spawn verdicts + Reconciler pick + reason
## Final Risks
## Final Patterns
## Final Open Questions
## Sourcesreconciler_verdict ∈ {clean, issues_flagged, needs_re_spawn}. The disagreement gate (researcher-reconcile gate <N>) keys on agreement_score and contested_count.
PROJECT-SUMMARY.md
Project-level aggregate produced by /np:close-project write-summary (ADR-0016). Renders one block per milestone with verification and validation summaries plus a blocker list. Regenerated on each close. It is never authoritative: milestone-level signals live in M<NNN>-VERIFICATION.md.
Templates
All artifact templates ship under templates/:
templates/milestone/{CONTEXT.md, ROADMAP.md, META.json}templates/slice/{ASSESSMENT.md, PLAN.md, RESEARCH.md, SUMMARY.md, UAT.md}templates/task/{PLAN.md, SUMMARY.md}templates/VALIDATION.md— Nyquist audit skeletontemplates/PROJECT.md,templates/REQUIREMENTS.md— project-level
Template rendering uses lib/template.cjs with placeholders. render() fails loud on unresolved placeholders, so workflows must supply every variable the template declares.
S<NNN>/TODO.md (slice-level rollup)
Per-slice Markdown rollup of task statuses as a checkbox list. Lives at .nubos-pilot/milestones/M<NNN>/slices/S<NNN>/TODO.md.
Producer paths:
bin/np-tools/plan-milestone.cjs::_scaffoldAllTasks— initial render for every slice after task scaffolding.lib/tasks.cjs::setTaskStatus— re-renders on every task-status transition (soskip/park/unpark/commit-taskall keep it live).bin/np-tools/render-todo.cjs— explicit re-render vianode np-tools.cjs render-todo <slice-full-id>.
Consumers: human reader, executor on resume, verifier for overview. There is no parser contract: TODO.md is a derived view, never a source of truth. Task frontmatter remains authoritative.
Schema
markdown
---
schema_version: 1
milestone_id: M<NNN>
slice_id: M<NNN>-S<NNN>
total: <int>
pending: <int>
in_progress: <int>
done: <int>
skipped: <int>
parked: <int>
updated_at: <ISO-8601>
---
# Slice M<NNN>-S<NNN>
- [ ] **M<NNN>-S<NNN>-T0001** — <task name from H1 of T0001-PLAN.md>
- [~] **M<NNN>-S<NNN>-T0002** — ...
- [x] **M<NNN>-S<NNN>-T0003** — ...
- [-] **M<NNN>-S<NNN>-T0004** — ...
- [!] **M<NNN>-S<NNN>-T0005** — ...Status → checkbox mapping
Task frontmatter status | TODO checkbox |
|---|---|
pending | [ ] |
in-progress | [~] |
done | [x] |
skipped | [-] |
parked | [!] |
Mapping lives in lib/todo.cjs::STATUS_CHECKBOX.
Task-name extraction
The human-readable name is read from the first # <heading> line of the task's T<NNNN>-PLAN.md. The template format is # <task_full_id> — <task_name>; everything after — becomes the name. Empty slices render the literal placeholder _No tasks yet._; a missing H1 renders (unnamed).
Agent Handoff
Persistent agent-to-agent note produced outside the plan/verify directory tree. It carries cross-phase signals that do not fit in commit messages or frontmatter:
- Executor flags a compromise the verifier must know about →
--to np-verifier. - Executor flags a plan flaw for the next planner run →
--to np-planner. - Researcher flags a cross-milestone trap future planners must see →
--to np-planner(global, no--milestone). - Planner clarifies SC interpretation for the verifier →
--to np-verifier. - Shared-code trap applies broadly →
--to "*"(broadcast).
Location
- Milestone-scoped (default when
--milestone M<NNN>):.nubos-pilot/milestones/M<NNN>/handoffs/ - Project-global (no
--milestone):.nubos-pilot/handoffs/
Filename
<iso-8601>__<from_agent>-to-<to_agent>__<topic-slug>__<id>.mdExample: 2026-04-23T11-48-26-642Z__np-executor-to-np-verifier__feature-flag-x__c209db90.md
ISO colons and dots are replaced with - for filesystem safety. Within one Node process, timestamps are monotone (lib/handoff.cjs bumps to _lastTs + 1ms if Date.now() hasn't advanced) so rapid writes stay chronologically sortable. The trailing <id> is an 8-char hex tie-breaker for the edge case of parallel writers landing on the same millisecond.
Frontmatter schema
yaml
---
schema_version: 1
id: <8-char hex>
from_agent: <agent slug>
to_agent: <agent slug | "*" for broadcast>
topic: <quoted string>
created_at: <ISO-8601>
milestone: M<NNN> | null
slice: M<NNN>-S<NNN> | null
task: M<NNN>-S<NNN>-T<NNNN> | null
status: open | read | acted | archived
---
<freeform Markdown body — the actual message>Status lifecycle
| Status | Meaning |
|---|---|
open | Default on write. Not yet looked at by the target agent. |
read | Target agent loaded the body but hasn't acted yet. |
acted | Target agent integrated the signal into its work. |
archived | Kept for record but no longer actionable. |
Transitions: handoff-status <id> <new-status> → lib/handoff.cjs::setHandoffStatus rewrites the status: line in-place without touching other frontmatter fields.
Agent-name validation
from_agent / to_agent must match /^[a-zA-Z0-9_\-\*]+$/. Spaces, slashes, and other punctuation raise handoff-invalid-agent.
Listing semantics
handoff-list without --milestone and without --global scans every milestone's handoffs/ directory plus the project-global .nubos-pilot/handoffs/. --milestone M<NNN> narrows to that milestone only; --global narrows to non-milestone-scoped only. Results are sorted by created_at, tie-broken by id.
--for <agent> filters to entries where to_agent equals the agent name or equals * (broadcast). --status <s> filters on the current status value.
Agent-prompt contract
Each agent's prompt documents what it reads and what it may write:
np-executor— reads handoffs at start; may write tonp-verifier(compromises),np-planner(plan flaws), or*(shared-code traps).np-verifier— read-only; folds consumed handoffs into its evidence. Handoffs from executors often flip what would otherwise read asFailtoPassorDeferby explaining a deliberate compromise.np-planner— reads handoffs at start; may write tonp-executor(scope nuances) ornp-verifier(SC interpretation).np-researcher— reads handoffs at start; may write tonp-verifier(evidence hints) ornp-planner(cross-milestone traps, typically global — no--milestone).
See agents/np-*.md → "Handoff Protocol" sections for the exact CLI invocations each agent is told to run.
