nubos-pilot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/agents/np-ai-researcher.md +140 -0
  2. package/agents/np-code-fixer.md +363 -0
  3. package/agents/np-code-reviewer.md +351 -0
  4. package/agents/np-domain-researcher.md +136 -0
  5. package/agents/np-eval-auditor.md +167 -0
  6. package/agents/np-eval-planner.md +153 -0
  7. package/agents/np-executor.md +72 -0
  8. package/agents/np-framework-selector.md +171 -0
  9. package/agents/np-nyquist-auditor.md +185 -0
  10. package/agents/np-plan-checker.md +165 -0
  11. package/agents/np-planner.md +199 -0
  12. package/agents/np-researcher.md +150 -0
  13. package/agents/np-security-auditor.md +206 -0
  14. package/agents/np-ui-auditor.md +369 -0
  15. package/agents/np-ui-checker.md +192 -0
  16. package/agents/np-ui-researcher.md +324 -0
  17. package/agents/np-verifier.md +79 -0
  18. package/bin/check-coverage.cjs +40 -0
  19. package/bin/check-workflows.cjs +171 -0
  20. package/bin/check-workflows.test.cjs +208 -0
  21. package/bin/install.js +500 -0
  22. package/bin/np-tools/_commands.cjs +70 -0
  23. package/bin/np-tools/add-tests.cjs +171 -0
  24. package/bin/np-tools/add-tests.test.cjs +122 -0
  25. package/bin/np-tools/add-todo.cjs +108 -0
  26. package/bin/np-tools/add-todo.test.cjs +112 -0
  27. package/bin/np-tools/agent-skills.cjs +14 -0
  28. package/bin/np-tools/agent-skills.test.cjs +42 -0
  29. package/bin/np-tools/ai-integration-phase.cjs +109 -0
  30. package/bin/np-tools/ai-integration-phase.test.cjs +123 -0
  31. package/bin/np-tools/askuser.cjs +53 -0
  32. package/bin/np-tools/askuser.test.cjs +49 -0
  33. package/bin/np-tools/autonomous.cjs +69 -0
  34. package/bin/np-tools/autonomous.test.cjs +74 -0
  35. package/bin/np-tools/checkpoint.cjs +101 -0
  36. package/bin/np-tools/checkpoint.test.cjs +119 -0
  37. package/bin/np-tools/code-review.cjs +133 -0
  38. package/bin/np-tools/code-review.test.cjs +96 -0
  39. package/bin/np-tools/commit-task.cjs +120 -0
  40. package/bin/np-tools/commit-task.test.cjs +160 -0
  41. package/bin/np-tools/commit.cjs +103 -0
  42. package/bin/np-tools/commit.test.cjs +93 -0
  43. package/bin/np-tools/config.cjs +101 -0
  44. package/bin/np-tools/config.test.cjs +71 -0
  45. package/bin/np-tools/discuss-phase-power.cjs +265 -0
  46. package/bin/np-tools/discuss-phase-power.test.cjs +242 -0
  47. package/bin/np-tools/discuss-phase.cjs +132 -0
  48. package/bin/np-tools/discuss-phase.test.cjs +148 -0
  49. package/bin/np-tools/dispatch.cjs +116 -0
  50. package/bin/np-tools/doctor.cjs +242 -0
  51. package/bin/np-tools/eval-review.cjs +116 -0
  52. package/bin/np-tools/eval-review.test.cjs +123 -0
  53. package/bin/np-tools/execute-phase.cjs +182 -0
  54. package/bin/np-tools/execute-phase.test.cjs +116 -0
  55. package/bin/np-tools/execute-plan.cjs +124 -0
  56. package/bin/np-tools/execute-plan.test.cjs +82 -0
  57. package/bin/np-tools/help.cjs +28 -0
  58. package/bin/np-tools/help.test.cjs +29 -0
  59. package/bin/np-tools/init-dispatch.test.cjs +91 -0
  60. package/bin/np-tools/metrics.cjs +97 -0
  61. package/bin/np-tools/metrics.test.cjs +188 -0
  62. package/bin/np-tools/new-milestone.cjs +288 -0
  63. package/bin/np-tools/new-milestone.test.cjs +166 -0
  64. package/bin/np-tools/new-project.cjs +284 -0
  65. package/bin/np-tools/new-project.test.cjs +165 -0
  66. package/bin/np-tools/next.cjs +7 -0
  67. package/bin/np-tools/next.test.cjs +30 -0
  68. package/bin/np-tools/park.cjs +48 -0
  69. package/bin/np-tools/park.test.cjs +50 -0
  70. package/bin/np-tools/pause-work.cjs +24 -0
  71. package/bin/np-tools/pause-work.test.cjs +74 -0
  72. package/bin/np-tools/phase.cjs +71 -0
  73. package/bin/np-tools/phase.test.cjs +81 -0
  74. package/bin/np-tools/plan-diff.cjs +57 -0
  75. package/bin/np-tools/plan-diff.test.cjs +134 -0
  76. package/bin/np-tools/plan-milestone-gaps.cjs +115 -0
  77. package/bin/np-tools/plan-milestone-gaps.test.cjs +122 -0
  78. package/bin/np-tools/plan-phase.cjs +350 -0
  79. package/bin/np-tools/plan-phase.test.cjs +263 -0
  80. package/bin/np-tools/progress.cjs +7 -0
  81. package/bin/np-tools/progress.test.cjs +44 -0
  82. package/bin/np-tools/queue.cjs +213 -0
  83. package/bin/np-tools/research-phase.cjs +144 -0
  84. package/bin/np-tools/research-phase.test.cjs +154 -0
  85. package/bin/np-tools/reset-slice.cjs +17 -0
  86. package/bin/np-tools/reset-slice.test.cjs +96 -0
  87. package/bin/np-tools/resolve-model.cjs +110 -0
  88. package/bin/np-tools/resolve-model.test.cjs +200 -0
  89. package/bin/np-tools/resume-work.cjs +76 -0
  90. package/bin/np-tools/resume-work.test.cjs +91 -0
  91. package/bin/np-tools/skip.cjs +48 -0
  92. package/bin/np-tools/skip.test.cjs +66 -0
  93. package/bin/np-tools/slug.cjs +34 -0
  94. package/bin/np-tools/slug.test.cjs +46 -0
  95. package/bin/np-tools/state.cjs +16 -0
  96. package/bin/np-tools/state.test.cjs +40 -0
  97. package/bin/np-tools/stats.cjs +151 -0
  98. package/bin/np-tools/stats.test.cjs +118 -0
  99. package/bin/np-tools/triage.cjs +128 -0
  100. package/bin/np-tools/ui-phase.cjs +108 -0
  101. package/bin/np-tools/ui-phase.test.cjs +121 -0
  102. package/bin/np-tools/ui-review.cjs +108 -0
  103. package/bin/np-tools/ui-review.test.cjs +120 -0
  104. package/bin/np-tools/undo-task.cjs +31 -0
  105. package/bin/np-tools/undo-task.test.cjs +117 -0
  106. package/bin/np-tools/undo.cjs +43 -0
  107. package/bin/np-tools/undo.test.cjs +120 -0
  108. package/bin/np-tools/unpark.cjs +48 -0
  109. package/bin/np-tools/unpark.test.cjs +50 -0
  110. package/bin/np-tools/verify-work.cjs +186 -0
  111. package/bin/np-tools/verify-work.test.cjs +97 -0
  112. package/docs/adr/0001-no-daemon-invariant.md +82 -0
  113. package/docs/adr/0002-zero-runtime-dependencies.md +90 -0
  114. package/docs/adr/0003-max-six-unit-types.md +85 -0
  115. package/docs/adr/0004-atomic-commit-per-unit.md +102 -0
  116. package/docs/adr/0005-three-orthogonal-file-trees.md +98 -0
  117. package/docs/adr/0006-yaml-dependency-amendment.md +60 -0
  118. package/docs/adr/README.md +27 -0
  119. package/docs/agent-frontmatter-schema.md +84 -0
  120. package/docs/phase-artifact-schemas.md +292 -0
  121. package/docs/phase-directory-layout.md +82 -0
  122. package/lib/__tests__/README.md +1 -0
  123. package/lib/agents.cjs +98 -0
  124. package/lib/agents.test.cjs +286 -0
  125. package/lib/askuser.cjs +36 -0
  126. package/lib/askuser.test.cjs +310 -0
  127. package/lib/checkpoint.cjs +135 -0
  128. package/lib/checkpoint.test.cjs +184 -0
  129. package/lib/core.cjs +165 -0
  130. package/lib/core.test.cjs +405 -0
  131. package/lib/fixtures/README.md +1 -0
  132. package/lib/fixtures/phase-tree/README.md +1 -0
  133. package/lib/fixtures/plans/cycle/PLAN.md +16 -0
  134. package/lib/fixtures/plans/cycle/tasks/T-01.md +20 -0
  135. package/lib/fixtures/plans/cycle/tasks/T-02.md +20 -0
  136. package/lib/fixtures/plans/cycle/tasks/T-03.md +20 -0
  137. package/lib/fixtures/plans/linear/PLAN.md +16 -0
  138. package/lib/fixtures/plans/linear/tasks/T-01.md +20 -0
  139. package/lib/fixtures/plans/linear/tasks/T-02.md +20 -0
  140. package/lib/fixtures/plans/linear/tasks/T-03.md +20 -0
  141. package/lib/fixtures/plans/parallel/PLAN.md +16 -0
  142. package/lib/fixtures/plans/parallel/tasks/T-01.md +20 -0
  143. package/lib/fixtures/plans/parallel/tasks/T-02.md +20 -0
  144. package/lib/fixtures/plans/parallel/tasks/T-03.md +20 -0
  145. package/lib/fixtures/plans/wave-conflict/PLAN.md +16 -0
  146. package/lib/fixtures/plans/wave-conflict/tasks/T-01.md +20 -0
  147. package/lib/fixtures/plans/wave-conflict/tasks/T-02.md +20 -0
  148. package/lib/fixtures/roadmap/ROADMAP-malformed.md +3 -0
  149. package/lib/fixtures/roadmap/ROADMAP-minimal.md +51 -0
  150. package/lib/fixtures/roadmap/roadmap-malformed.yaml +7 -0
  151. package/lib/fixtures/roadmap/roadmap-minimal.yaml +40 -0
  152. package/lib/fixtures/roadmap/roadmap-ten-phases.yaml +101 -0
  153. package/lib/fixtures/templates/phase-context.md +6 -0
  154. package/lib/fixtures/templates/plan-skeleton.md +6 -0
  155. package/lib/frontmatter.cjs +251 -0
  156. package/lib/frontmatter.test.cjs +177 -0
  157. package/lib/gaps.cjs +197 -0
  158. package/lib/gaps.test.cjs +200 -0
  159. package/lib/git.cjs +207 -0
  160. package/lib/git.test.cjs +305 -0
  161. package/lib/install/agents-md.cjs +77 -0
  162. package/lib/install/backup.cjs +70 -0
  163. package/lib/install/codex-toml.cjs +440 -0
  164. package/lib/install/managed-block.cjs +30 -0
  165. package/lib/install/manifest.cjs +148 -0
  166. package/lib/install/mcp-writer.cjs +127 -0
  167. package/lib/install/runtime-detect.cjs +44 -0
  168. package/lib/install/staging.cjs +149 -0
  169. package/lib/metrics-aggregate.cjs +229 -0
  170. package/lib/metrics-aggregate.test.cjs +192 -0
  171. package/lib/metrics.cjs +120 -0
  172. package/lib/metrics.test.cjs +182 -0
  173. package/lib/model-aliases.regression.test.cjs +16 -0
  174. package/lib/model-profiles.cjs +42 -0
  175. package/lib/model-profiles.test.cjs +61 -0
  176. package/lib/next.cjs +236 -0
  177. package/lib/next.test.cjs +194 -0
  178. package/lib/phase.cjs +95 -0
  179. package/lib/phase.test.cjs +189 -0
  180. package/lib/plan-checker-contract.test.cjs +72 -0
  181. package/lib/plan-diff.cjs +173 -0
  182. package/lib/plan-diff.test.cjs +217 -0
  183. package/lib/plan.cjs +85 -0
  184. package/lib/plan.test.cjs +263 -0
  185. package/lib/progress.cjs +95 -0
  186. package/lib/progress.test.cjs +116 -0
  187. package/lib/researcher-contract.test.cjs +61 -0
  188. package/lib/roadmap-render.cjs +206 -0
  189. package/lib/roadmap-render.test.cjs +121 -0
  190. package/lib/roadmap.cjs +416 -0
  191. package/lib/roadmap.test.cjs +371 -0
  192. package/lib/runtime/_contract.test.cjs +61 -0
  193. package/lib/runtime/_readline.cjs +119 -0
  194. package/lib/runtime/_readline.test.cjs +126 -0
  195. package/lib/runtime/claude.cjs +48 -0
  196. package/lib/runtime/claude.test.cjs +101 -0
  197. package/lib/runtime/codex.cjs +35 -0
  198. package/lib/runtime/codex.test.cjs +114 -0
  199. package/lib/runtime/gemini.cjs +35 -0
  200. package/lib/runtime/gemini.test.cjs +109 -0
  201. package/lib/runtime/index.cjs +49 -0
  202. package/lib/runtime/index.test.cjs +181 -0
  203. package/lib/runtime/opencode.cjs +35 -0
  204. package/lib/runtime/opencode.test.cjs +124 -0
  205. package/lib/state.cjs +205 -0
  206. package/lib/state.test.cjs +264 -0
  207. package/lib/surface-audit.test.cjs +46 -0
  208. package/lib/tasks.cjs +327 -0
  209. package/lib/tasks.test.cjs +389 -0
  210. package/lib/template.cjs +66 -0
  211. package/lib/template.test.cjs +159 -0
  212. package/lib/undo.cjs +179 -0
  213. package/lib/undo.test.cjs +261 -0
  214. package/lib/verify.cjs +116 -0
  215. package/lib/verify.test.cjs +187 -0
  216. package/np-tools.cjs +303 -0
  217. package/package.json +39 -0
  218. package/templates/AI-SPEC.md +90 -0
  219. package/templates/CONTEXT.md +32 -0
  220. package/templates/PLAN.md +69 -0
  221. package/templates/PROJECT.md +60 -0
  222. package/templates/REQUIREMENTS.md +38 -0
  223. package/templates/SECURITY.md +61 -0
  224. package/templates/UI-SPEC.md +64 -0
  225. package/templates/VALIDATION.md +76 -0
  226. package/templates/claude/payload/README.md +11 -0
  227. package/templates/opencode/opencode.json +6 -0
  228. package/templates/opencode/payload/AGENTS.md +9 -0
  229. package/workflows/add-backlog.md +212 -0
  230. package/workflows/add-tests.md +69 -0
  231. package/workflows/add-todo.md +222 -0
  232. package/workflows/ai-integration-phase.md +230 -0
  233. package/workflows/autonomous.md +94 -0
  234. package/workflows/cleanup.md +325 -0
  235. package/workflows/code-review-fix.md +435 -0
  236. package/workflows/code-review.md +447 -0
  237. package/workflows/discuss-phase-assumptions.md +269 -0
  238. package/workflows/discuss-phase-power.md +139 -0
  239. package/workflows/discuss-phase.md +386 -0
  240. package/workflows/dispatch.md +9 -0
  241. package/workflows/doctor.md +10 -0
  242. package/workflows/eval-review.md +243 -0
  243. package/workflows/execute-phase.md +142 -0
  244. package/workflows/execute-plan.md +82 -0
  245. package/workflows/help.md +8 -0
  246. package/workflows/new-milestone.md +166 -0
  247. package/workflows/new-project.md +213 -0
  248. package/workflows/next.md +8 -0
  249. package/workflows/note.md +244 -0
  250. package/workflows/park.md +29 -0
  251. package/workflows/pause-work.md +34 -0
  252. package/workflows/plan-milestone-gaps.md +233 -0
  253. package/workflows/plan-phase.md +351 -0
  254. package/workflows/progress.md +8 -0
  255. package/workflows/queue.md +9 -0
  256. package/workflows/research-phase.md +327 -0
  257. package/workflows/reset-slice.md +39 -0
  258. package/workflows/resume-work.md +79 -0
  259. package/workflows/review.md +489 -0
  260. package/workflows/secure-phase.md +209 -0
  261. package/workflows/session-report.md +243 -0
  262. package/workflows/skip.md +29 -0
  263. package/workflows/state.md +7 -0
  264. package/workflows/stats.md +170 -0
  265. package/workflows/thread.md +214 -0
  266. package/workflows/triage.md +9 -0
  267. package/workflows/ui-phase.md +246 -0
  268. package/workflows/ui-review.md +222 -0
  269. package/workflows/undo-task.md +42 -0
  270. package/workflows/undo.md +55 -0
  271. package/workflows/unpark.md +29 -0
  272. package/workflows/validate-phase.md +231 -0
  273. package/workflows/verify-work.md +83 -0
@@ -0,0 +1,209 @@
1
+ ---
2
+ command: np:secure-phase
3
+ description: Threat-mitigation audit on a completed phase. Reads PLAN.md threat_model + implementation, spawns np-security-auditor (opus) to score each threat as MITIGATED/PARTIAL/UNMITIGATED/N/A, writes SECURITY.md sidecar from templates/SECURITY.md skeleton. One atomic docs commit.
4
+ ---
5
+
6
+ # np:secure-phase
7
+
8
+ Produces `{phase_dir}/{padded}-SECURITY.md` via a single `np-security-auditor`
9
+ (opus) spawn. Runs AFTER `/np:execute-phase` has landed code — the audit
10
+ needs SUMMARY.md and a `<threat_model>` block in PLAN.md.
11
+
12
+ The workflow `cp`s `templates/SECURITY.md` into the sidecar BEFORE
13
+ spawning the agent; the auditor substitutes placeholders (`{N}`,
14
+ `{phase-slug}`, `{date}`) and appends per-threat scoring. The Task
15
+ spawn is wrapped in the Plan 09-05 metrics + resolve-model pattern
16
+ (D-06, D-01); `RUNTIME` is detected once and re-used by `metrics
17
+ record`. Prompts route through `np-tools.cjs askuser` (INST-03).
18
+
19
+ Pre-Phase-9 phases without `<threat_model>` degrade gracefully: the
20
+ auditor produces a best-effort audit inferred from ADRs + code patterns
21
+ (T-10-04-04 accept).
22
+
23
+ ## Initialize
24
+
25
+ ```bash
26
+ PHASE="$1"
27
+ if [[ -z "$PHASE" ]]; then
28
+ echo "Usage: /np:secure-phase <phase-number>" >&2
29
+ exit 2
30
+ fi
31
+
32
+ INIT=$(node np-tools.cjs init secure-phase "$PHASE")
33
+ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
34
+ RUNTIME=$(node -e "console.log(require('./lib/runtime/index.cjs').detect().runtime)")
35
+ ```
36
+
37
+ Parse JSON for: `padded_phase`, `phase_dir`, `phase_found`.
38
+
39
+ ```bash
40
+ PADDED=$(echo "$INIT" | jq -r '.padded_phase // .padded')
41
+ PHASE_DIR=$(echo "$INIT" | jq -r '.phase_dir')
42
+ PHASE_FOUND=$(echo "$INIT" | jq -r '.phase_found')
43
+ SECURITY_PATH="${PHASE_DIR}/${PADDED}-SECURITY.md"
44
+ SUMMARY_PATH="${PHASE_DIR}/${PADDED}-SUMMARY.md"
45
+ PLAN_PATH_GLOB="${PHASE_DIR}/${PADDED}-*-PLAN.md"
46
+ TEMPLATE_PATH="templates/SECURITY.md"
47
+ PLAN_ID="${PADDED}-secure-phase"
48
+ TASK_ID="${PADDED}-secure-phase"
49
+ ```
50
+
51
+ ## Pre-Flight Gates
52
+
53
+ <pre_flight>
54
+
55
+ ### Gate 1 — Phase found + SUMMARY.md present
56
+
57
+ ```bash
58
+ if [[ "$PHASE_FOUND" != "true" ]]; then
59
+ echo "Error: Phase $PHASE not found in roadmap or on disk." >&2
60
+ exit 1
61
+ fi
62
+ if [[ ! -f "$SUMMARY_PATH" ]]; then
63
+ echo "Error: Phase $PHASE has no SUMMARY.md at $SUMMARY_PATH." >&2
64
+ echo "Run /np:execute-phase $PHASE before auditing." >&2
65
+ exit 1
66
+ fi
67
+ ```
68
+
69
+ ### Gate 2 — SECURITY.md already exists
70
+
71
+ If a prior audit is present, let the user choose Re-run / View / Skip.
72
+ The `cp` step only runs in the Re-run branch — View or Skip never
73
+ overwrites a user-edited sidecar (T-10-04-01 mitigation).
74
+
75
+ ```bash
76
+ RERUN="false"
77
+ if [[ -f "$SECURITY_PATH" ]]; then
78
+ CHOICE=$(node np-tools.cjs askuser --json '{
79
+ "type": "select",
80
+ "header": "Existing SECURITY.md",
81
+ "question": "SECURITY.md already exists for Phase '"$PHASE"'. What would you like to do?",
82
+ "options": [
83
+ {"label": "Re-run — replace the current audit", "description": "Re-runs np-security-auditor and overwrites the existing file."},
84
+ {"label": "View — display current audit and exit", "description": "Reads the file and exits without changes."},
85
+ {"label": "Skip — keep current audit and exit", "description": "Leaves the file untouched."}
86
+ ]
87
+ }')
88
+ case "$CHOICE" in
89
+ "View"*) cat "$SECURITY_PATH"; exit 0 ;;
90
+ "Skip"*) exit 0 ;;
91
+ "Re-run"*) RERUN="true" ;;
92
+ esac
93
+ fi
94
+ ```
95
+
96
+ ### Gate 3 — Template present
97
+
98
+ ```bash
99
+ if [[ ! -f "$TEMPLATE_PATH" ]]; then
100
+ echo "Error: $TEMPLATE_PATH missing; Plan 10-01-T03 should have ported it." >&2
101
+ echo "Re-run 'npx nubos-pilot install' or restore templates/SECURITY.md from source." >&2
102
+ exit 1
103
+ fi
104
+ ```
105
+
106
+ </pre_flight>
107
+
108
+ ## Load Template
109
+
110
+ Copy `templates/SECURITY.md` into the sidecar ONLY when absent OR user
111
+ chose Re-run. The agent substitutes `{N}` / `{phase-slug}` / `{date}`
112
+ at write time — the workflow never pre-substitutes.
113
+
114
+ ```bash
115
+ if [[ ! -f "$SECURITY_PATH" || "$RERUN" == "true" ]]; then
116
+ cp "$TEMPLATE_PATH" "$SECURITY_PATH"
117
+ fi
118
+ ```
119
+
120
+ ## Extract Threat Model
121
+
122
+ The `np-security-auditor` agent reads every
123
+ `${PHASE_DIR}/${PADDED}-*-PLAN.md`, extracts the `<threat_model>`
124
+ block, and consolidates into a unified audit. The workflow passes only
125
+ the glob + phase dir — the agent parses via `Read` / `Grep`.
126
+
127
+ ## Spawn np-security-auditor (opus)
128
+
129
+ ```bash
130
+ START=$(node np-tools.cjs metrics start-timestamp)
131
+ MODEL=$(node np-tools.cjs resolve-model np-security-auditor --profile balanced)
132
+ # Spawn agent=np-security-auditor model=$MODEL
133
+ # input: plan_path_glob=$PLAN_PATH_GLOB, summary_path=$SUMMARY_PATH,
134
+ # security_path=$SECURITY_PATH, template_path=$TEMPLATE_PATH,
135
+ # phase_dir=$PHASE_DIR, phase=$PHASE, padded=$PADDED
136
+ # output: $SECURITY_PATH with scored threats (MITIGATED / PARTIAL /
137
+ # UNMITIGATED / N/A), using templates/SECURITY.md as skeleton.
138
+ Task(
139
+ subagent_type="np-security-auditor",
140
+ model="$MODEL",
141
+ prompt="<files_to_read>${PLAN_PATH_GLOB} ${SUMMARY_PATH} ${TEMPLATE_PATH} CLAUDE.md PROJECT.md</files_to_read><config>plan_path_glob=$PLAN_PATH_GLOB,summary_path=$SUMMARY_PATH,security_path=$SECURITY_PATH,template_path=$TEMPLATE_PATH,phase_dir=$PHASE_DIR,phase=$PHASE,padded=$PADDED</config>"
142
+ )
143
+ END=$(node np-tools.cjs metrics end-timestamp)
144
+ node np-tools.cjs metrics record \
145
+ --agent np-security-auditor --tier opus --resolved-model "$MODEL" \
146
+ --phase "$PHASE" --plan "$PLAN_ID" --task "$TASK_ID" \
147
+ --started "$START" --ended "$END" \
148
+ --tokens-in "${TOKENS_IN:-0}" --tokens-out "${TOKENS_OUT:-0}" \
149
+ --retry-count 0 --status ok --runtime "$RUNTIME"
150
+ ```
151
+
152
+ ## Validation Gate
153
+
154
+ ```bash
155
+ if [[ ! -f "$SECURITY_PATH" ]]; then
156
+ CHOICE=$(node np-tools.cjs askuser --json '{
157
+ "type": "select",
158
+ "header": "SECURITY.md missing",
159
+ "question": "np-security-auditor did not write SECURITY.md. What would you like to do?",
160
+ "options": [
161
+ {"label": "Re-run np-security-auditor", "description": "Spawn the auditor once more."},
162
+ {"label": "Abort", "description": "Exit without committing."}
163
+ ]
164
+ }')
165
+ case "$CHOICE" in "Abort") exit 1 ;; esac
166
+ fi
167
+ ```
168
+
169
+ ## Commit
170
+
171
+ ```bash
172
+ node np-tools.cjs commit "docs(${PADDED}): add security audit report" --files "$SECURITY_PATH"
173
+ ```
174
+
175
+ One atomic docs commit per ADR-0004. The commit helper routes through
176
+ `lib/git.cjs.assertCommittablePaths` (gitignore-guard) before staging.
177
+
178
+ ## Scope Guardrail
179
+
180
+ <scope_guardrail>
181
+ **Do:**
182
+ - Run `np-security-auditor` exactly once per invocation (single-pass audit).
183
+ - Emit a metrics record AFTER the Task spawn (D-06).
184
+ - Resolve MODEL via `np-tools.cjs resolve-model np-security-auditor --profile balanced` — no hardcoded IDs.
185
+ - Use `np-tools.cjs askuser` for every prompt (INST-03 invariant).
186
+ - `cp templates/SECURITY.md` into the sidecar BEFORE spawning the agent.
187
+ - Only overwrite existing SECURITY.md on Re-run choice (T-10-04-01).
188
+ - Abort early when phase_dir or SUMMARY.md is absent.
189
+ - Treat implementation files as READ-ONLY (D-20 SC-5).
190
+
191
+ **Don't:**
192
+ - Run this workflow on a phase without SUMMARY.md.
193
+ - Invoke host-specific prompt tools directly — route through `np-tools.cjs askuser`.
194
+ - Overwrite a user-edited SECURITY.md without the Re-run gate (T-10-04-01).
195
+ - Construct phase paths from raw `$1` — consume `padded_phase` / `phase_dir`
196
+ from `np-tools.cjs init` (SAFE_PHASE_RE enforced upstream, T-10-04-03).
197
+ - Skip the metrics record block (D-06).
198
+ - Inject raw secret values into the agent prompt (T-10-04-02 defence-in-depth).
199
+ - Spawn any additional agent beyond `np-security-auditor`.
200
+ </scope_guardrail>
201
+
202
+ ## Output
203
+
204
+ - `{phase_dir}/{padded}-SECURITY.md` — threat register with
205
+ MITIGATED / PARTIAL / UNMITIGATED / N/A scoring, Trust Boundaries,
206
+ Accepted Risks log, Security Audit Trail. Frontmatter carries
207
+ `threats_total`, `mitigated`, `partial`, `unmitigated`, `threats_open`.
208
+ - 1 metrics record in `.nubos-pilot/metrics/phase-${PHASE}.jsonl`.
209
+ - One atomic `docs(${PADDED}): add security audit report` git commit.
@@ -0,0 +1,243 @@
1
+ ---
2
+ command: np:session-report
3
+ description: Generate session report from metrics since .nubos-pilot/reports/.last-session pointer. Pointer update is file-lock-guarded (Pitfall 8). Output filename is ISO-8601-prefixed (YYYY-MM-DDTHHMM-session-report.md) for deterministic sort and no overwrite (D-17). Uses lib/metrics-aggregate.cjs.aggregateSession (D-18). One atomic docs commit.
4
+ ---
5
+
6
+ # np:session-report
7
+
8
+ Implements UTIL-07a. Produces a post-session markdown report
9
+ summarising metrics, commits, and progress since the last report.
10
+ Three deliberate design choices:
11
+
12
+ - **D-16 pointer file** — persists
13
+ `.nubos-pilot/reports/.last-session` (ISO-8601 timestamp) so each
14
+ report covers exactly "since last report" regardless of clock time
15
+ (rather than a rolling 24h window that would double-count overlaps).
16
+ - **D-17 ISO-prefixed filename** — emits
17
+ `YYYY-MM-DDTHHMM-session-report.md` so reports never overwrite and
18
+ sort deterministically.
19
+ - **D-18 aggregation helper** — metrics come from
20
+ `lib/metrics-aggregate.cjs.aggregateSession` (Plan 10-01-T02);
21
+ workflow does not parse JSONL itself.
22
+
23
+ Pointer read + aggregation + write are wrapped in
24
+ `lib/core.cjs.withFileLock` (10s timeout per Pitfall 8, T-10-06-02
25
+ mitigation). Two concurrent `/np:session-report` invocations
26
+ serialise on the pointer so neither produces an overlapping report.
27
+
28
+ Pure-CRUD workflow — no agent spawn, no resolve-model, no metrics
29
+ record. Pitfall 9 / `workflow-missing-metrics` is exempt.
30
+
31
+ ## Initialize
32
+
33
+ ```bash
34
+ SINCE_OVERRIDE=""
35
+ for arg in "$@"; do
36
+ case "$arg" in
37
+ --since=*) SINCE_OVERRIDE="${arg#--since=}" ;;
38
+ esac
39
+ done
40
+
41
+ STATE_DIR=$(node -e "console.log(require('./lib/core.cjs').projectStateDir(process.cwd()))")
42
+ REPORTS_DIR="${STATE_DIR}/reports"
43
+ POINTER="${REPORTS_DIR}/.last-session"
44
+ mkdir -p "$REPORTS_DIR"
45
+
46
+ NOW_ISO=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
47
+ LOCAL_FILENAME_TS=$(date +"%Y-%m-%dT%H%M")
48
+ REPORT_PATH="${REPORTS_DIR}/${LOCAL_FILENAME_TS}-session-report.md"
49
+ ```
50
+
51
+ The filename format is `YYYY-MM-DDTHHMM-session-report.md` (D-17 —
52
+ 4-char HHMM, no seconds, local time) so reports sort
53
+ lexicographically by invocation order.
54
+
55
+ ## Pointer Read + Aggregation
56
+
57
+ Pointer read and metrics aggregation run inside a single
58
+ `withFileLock` call so a concurrent invocation cannot interleave
59
+ between "read pointer" and "write new pointer" (T-10-06-02 / Pitfall
60
+ 8 mitigation). The lock times out at 10 000 ms; callers that wait
61
+ longer hit `lock-timeout` from `lib/core.cjs.NubosPilotError`.
62
+
63
+ ```bash
64
+ REPORT_JSON=$(node -e '
65
+ const fs = require("node:fs");
66
+ const { withFileLock } = require("./lib/core.cjs");
67
+ const { aggregateSession } = require("./lib/metrics-aggregate.cjs");
68
+ const pointer = process.argv[1];
69
+ const override = process.argv[2] || "";
70
+ const done = withFileLock(pointer, async () => {
71
+ let since = override || "";
72
+ if (!override && fs.existsSync(pointer)) {
73
+ since = fs.readFileSync(pointer, "utf-8").trim();
74
+ }
75
+ return aggregateSession(since || null, { cwd: process.cwd() });
76
+ }, { timeoutMs: 10000 });
77
+ Promise.resolve(done).then((r) => process.stdout.write(JSON.stringify(r)));
78
+ ' "$POINTER" "$SINCE_OVERRIDE")
79
+ ```
80
+
81
+ The `aggregateSession` helper returns
82
+ `{since_iso, record_count, by_phase, total_tokens_in, total_tokens_out,
83
+ partial_tokens, total_duration_ms, error_count, phases_touched}`.
84
+ Null token values (non-claude runtimes per Phase 9 D-09) pass through
85
+ and are rendered as `—` in the output table.
86
+
87
+ ## Render Report Body
88
+
89
+ Use the `Write` tool to create `$REPORT_PATH` with the body below
90
+ (not a bash heredoc per CLAUDE.md). Render values from
91
+ `$REPORT_JSON` using Node to produce the table rows (null-safe with
92
+ `—` for any null token fields, per D-09 / D-15).
93
+
94
+ ```markdown
95
+ # Session Report — <NOW_ISO>
96
+
97
+ **Since:** <since_iso or "project inception">
98
+ **Records:** <record_count>
99
+ **Phases touched:** <phases_touched joined with comma>
100
+ **Total duration:** <total_duration_ms> ms
101
+ **Errors:** <error_count>
102
+
103
+ ## By Phase
104
+
105
+ | Phase | Records | Tokens In | Tokens Out | Errors | Retry Sum |
106
+ |-------|---------|-----------|------------|--------|-----------|
107
+ | <phase> | <record_count> | <tokens_in or "—"> | <tokens_out or "—"> | <error_count> | <retry_count_sum> |
108
+ ```
109
+
110
+ To produce the rendered body deterministically, the agent invokes a
111
+ short Node snippet that consumes `$REPORT_JSON` on stdin and emits
112
+ the markdown table rows — then feeds the full text to the `Write`
113
+ tool. The snippet shape:
114
+
115
+ ```bash
116
+ BODY=$(node -e '
117
+ const j = JSON.parse(process.argv[1]);
118
+ const fmt = (v) => v === null || v === undefined ? "—" : String(v);
119
+ const rows = Object.entries(j.by_phase || {}).sort()
120
+ .map(([k, p]) => `| ${k} | ${p.record_count} | ${fmt(p.total_tokens_in)} | ${fmt(p.total_tokens_out)} | ${p.error_count} | ${p.retry_count_sum} |`)
121
+ .join("\n");
122
+ process.stdout.write(rows);
123
+ ' "$REPORT_JSON")
124
+ ```
125
+
126
+ ## Update Pointer
127
+
128
+ AFTER the report file is written via `Write`, update the pointer
129
+ inside a second `withFileLock` call so a crash between "write report"
130
+ and "update pointer" leaves the pointer STALE — the next run
131
+ re-covers the missing period (safe-by-default).
132
+
133
+ ```bash
134
+ node -e '
135
+ const { withFileLock, atomicWriteFileSync } = require("./lib/core.cjs");
136
+ withFileLock(
137
+ process.argv[1],
138
+ () => atomicWriteFileSync(process.argv[1], process.argv[2]),
139
+ { timeoutMs: 10000 }
140
+ );
141
+ ' "$POINTER" "$NOW_ISO"
142
+ ```
143
+
144
+ Using `atomicWriteFileSync` ensures the pointer update is crash-safe
145
+ (ADR-0004) — a mid-write crash leaves the OLD pointer intact, not a
146
+ truncated file.
147
+
148
+ ## Commit
149
+
150
+ Both the new report and the updated pointer land in a single atomic
151
+ docs commit per ADR-0004. Route through `node np-tools.cjs commit`
152
+ so `lib/git.cjs.assertCommittablePaths()` validates the paths.
153
+
154
+ ```bash
155
+ node np-tools.cjs commit "docs(10): add session report — ${LOCAL_FILENAME_TS}" \
156
+ --files "$REPORT_PATH" "$POINTER"
157
+ ```
158
+
159
+ ## Report
160
+
161
+ ```
162
+ Session report: $REPORT_PATH
163
+ Since: <since_iso from JSON>
164
+ Records: <record_count>
165
+ Pointer: $POINTER (updated to $NOW_ISO)
166
+ ```
167
+
168
+ ## Scope Guardrail
169
+
170
+ <scope_guardrail>
171
+ **Do:**
172
+ - Wrap pointer reads AND pointer writes in `withFileLock` (10s
173
+ timeout per Pitfall 8, T-10-06-02 mitigation).
174
+ - Use local time `YYYY-MM-DDTHHMM` for the filename prefix (D-17 —
175
+ no seconds; deterministic sort; no overwrite).
176
+ - Render `—` for null token fields (Phase 9 D-09 non-claude runtimes).
177
+ - Commit BOTH the report file AND the updated pointer in a single
178
+ atomic commit (ADR-0004).
179
+ - Delegate all JSONL parsing to `lib/metrics-aggregate.cjs` (D-18
180
+ schema single-source-of-truth).
181
+
182
+ **Don't:**
183
+ - Use a 24h rolling window (rejected per D-16 — two invocations in 12
184
+ hours would double-count the overlap).
185
+ - Overwrite `SESSION_REPORT.md` (rejected per D-17 — previous reports
186
+ would be lost on every run).
187
+ - Bypass `aggregateSession` for raw JSONL reads — schema guarantees
188
+ come from the aggregator.
189
+ - Update the pointer BEFORE the report file write succeeds — a crash
190
+ between the two would skip a session.
191
+ - Invoke host-specific prompt tools directly (the BARE_ASKUSER lint
192
+ in `bin/check-workflows.cjs` blocks them) — route through
193
+ `node np-tools.cjs askuser --json '…'`.
194
+ - Add a `metrics record` block. No Task/Spawn site; Pitfall 9 /
195
+ `workflow-missing-metrics` is exempt.
196
+ </scope_guardrail>
197
+
198
+ ## Output
199
+
200
+ - `.nubos-pilot/reports/YYYY-MM-DDTHHMM-session-report.md` — rendered
201
+ markdown with session summary, per-phase table (null tokens as
202
+ `—`), and metadata header.
203
+ - `.nubos-pilot/reports/.last-session` — pointer file updated to the
204
+ current ISO-8601 UTC timestamp (atomic write; file-locked).
205
+ - One atomic git commit
206
+ `docs(10): add session report — <local-ts>` containing both files
207
+ (ADR-0004).
208
+
209
+ ## Success Criteria
210
+
211
+ - [ ] `--since=<ISO>` argv override honoured when present.
212
+ - [ ] Reports directory created via `projectStateDir` +
213
+ `mkdir -p` (no direct project-state reads).
214
+ - [ ] Pointer read AND pointer write both wrapped in `withFileLock`
215
+ with `timeoutMs: 10000` (Pitfall 8 / T-10-06-02).
216
+ - [ ] Metrics aggregation via `lib/metrics-aggregate.cjs.aggregateSession`
217
+ (D-18 — workflow never parses JSONL directly).
218
+ - [ ] Filename format `YYYY-MM-DDTHHMM-session-report.md` (D-17 —
219
+ no overwrite, deterministic sort).
220
+ - [ ] Null token fields rendered as `—` in the Phase table (D-09 /
221
+ D-15).
222
+ - [ ] Pointer update happens AFTER report write succeeds (stale
223
+ pointer on crash is safer than skipped session).
224
+ - [ ] Pointer written via `atomicWriteFileSync` (ADR-0004 crash-safety).
225
+ - [ ] Single atomic commit via `np-tools.cjs commit` containing both
226
+ report file and pointer.
227
+ - [ ] Lint clean under `bin/check-workflows.cjs` — no BARE_ASKUSER
228
+ violations, no DIRECT_READ matches.
229
+
230
+ ## Related Workflows
231
+
232
+ - **`/np:stats`** — stats snapshot (read-only, no pointer, no commit).
233
+ - **`/np:cleanup`** — archive completed milestones (distinct
234
+ milestone-level aggregation).
235
+
236
+ ## Design Notes
237
+
238
+ D-16 pointer file replaces any rolling-window approach with
239
+ deterministic "since last report" semantics. D-17 ISO-prefixed
240
+ filename makes reports append-only and deterministically sortable.
241
+ D-18 delegates metrics aggregation to `lib/metrics-aggregate.cjs`
242
+ (landed Plan 10-01-T02). Pitfall 8 mitigation wraps pointer access
243
+ in `withFileLock`.
@@ -0,0 +1,29 @@
1
+ ---
2
+ command: np:skip
3
+ description: Mark a task as skipped (lifecycle CRUD). The task is excluded from wave-selection until it is unparked or its status is set back to pending.
4
+ ---
5
+
6
+ # /np:skip
7
+
8
+ <objective>
9
+ Flip the task's frontmatter `status` field to `skipped`. The wave-selector
10
+ treats `skipped` like `done` for advancement purposes, so the next wave
11
+ can proceed without this task. No commit is made; the task file is rewritten
12
+ in place.
13
+ </objective>
14
+
15
+ ## Execution
16
+
17
+ ```bash
18
+ TASK_ID="$1"
19
+ if [ -z "$TASK_ID" ]; then
20
+ echo "Usage: /np:skip <task-id>" >&2
21
+ exit 1
22
+ fi
23
+ node np-tools.cjs skip "$TASK_ID"
24
+ ```
25
+
26
+ ## Scope Guardrail
27
+
28
+ **Do:** flip task status to `skipped` via `lib/tasks.setTaskStatus`.
29
+ **Don't:** revert commits; modify other frontmatter fields.
@@ -0,0 +1,7 @@
1
+ # np:state
2
+
3
+ Print a JSON snapshot of the current STATE.md frontmatter.
4
+
5
+ ```bash
6
+ node np-tools.cjs state
7
+ ```
@@ -0,0 +1,170 @@
1
+ ---
2
+ command: np:stats
3
+ description: Stats output — phases-table (name/plans/completed/status/%) + metrics aggregation (tokens-in/out per phase, avg duration by tier, retry_count_sum, error_rate). Consumes node np-tools.cjs stats json. Null-token runtimes render as `—` (Phase 9 D-09). Read-only — no commits, no STATE mutation.
4
+ ---
5
+
6
+ # np:stats
7
+
8
+ Implements UTIL-07b. Renders an on-demand snapshot combining a
9
+ phases-table (phase / plans total / complete / status / percent)
10
+ with metrics aggregation (tokens-in / tokens-out per phase, avg
11
+ duration by tier, retry_count_sum, error_rate). Read-only surface
12
+ per D-20 SC-5 — no files written, no state mutated, no git commit.
13
+
14
+ The workflow delegates ALL data collection to
15
+ `bin/np-tools/stats.cjs` (landed Plan 10-01-T04), which emits a
16
+ `schema_version:1` JSON envelope on stdout. This workflow consumes
17
+ that JSON and renders markdown. No JSONL parsing inline.
18
+
19
+ Pure read-only workflow — no agent spawn, no resolve-model, no
20
+ metrics record. Pitfall 9 / `workflow-missing-metrics` is exempt.
21
+
22
+ ## Initialize
23
+
24
+ ```bash
25
+ STATS_JSON=$(node np-tools.cjs stats json)
26
+ if [[ -z "$STATS_JSON" ]]; then
27
+ echo "No stats available (empty project?)" >&2
28
+ exit 0
29
+ fi
30
+ ```
31
+
32
+ The stats CLI produces the full payload — `{schema_version, milestone,
33
+ phases, plans_total, plans_complete, percent, git, last_activity,
34
+ metrics_by_phase}`. An empty project yields an empty JSON; the
35
+ workflow short-circuits gracefully rather than producing an empty
36
+ table.
37
+
38
+ ## Render
39
+
40
+ Render the JSON as markdown via a `node -e` one-liner. Null token
41
+ cells render as `—` (Phase 9 D-09) so non-claude runtimes don't show
42
+ misleading zeros. Progress bar is a 20-char block-string
43
+ `[████░░░░░░…]` (ADR-0002 — no cli-progress dep).
44
+
45
+ Progress bar helper:
46
+
47
+ ```javascript
48
+ const filled = Math.round(percent / 5);
49
+ const bar = "█".repeat(filled) + "░".repeat(20 - filled);
50
+ ```
51
+
52
+ Null-safe token rendering:
53
+
54
+ ```javascript
55
+ const fmt = (v) => v === null || v === undefined ? "—" : v.toLocaleString();
56
+ ```
57
+
58
+ Rendered output sections: Project Stats header (milestone / progress
59
+ bar / last activity / commits / start date), Phases table (phase /
60
+ name / plans / complete / status / percent), Metrics by Phase table
61
+ (records / tokens / tier-avg durations / errors). Example row for a
62
+ phase with no metrics: `| 10 | — | — | — | — | — | — | — |`.
63
+
64
+ The full render is a single `node -e` call consuming `$STATS_JSON`:
65
+
66
+ ```bash
67
+ node -e '
68
+ const j = JSON.parse(process.argv[1]);
69
+ const fmt = (v) => v === null || v === undefined ? "—" : (typeof v === "number" ? v.toLocaleString() : String(v));
70
+ const filled = Math.round((j.percent || 0) / 5);
71
+ const bar = "█".repeat(filled) + "░".repeat(20 - filled);
72
+ const lines = [];
73
+ lines.push("## Project Stats");
74
+ lines.push("");
75
+ lines.push(`**Milestone:** ${fmt(j.milestone && j.milestone.version)} — ${fmt(j.milestone && j.milestone.name)}`);
76
+ lines.push(`**Progress:** [${bar}] ${j.percent || 0}% (${j.plans_complete}/${j.plans_total} plans)`);
77
+ lines.push(`**Last activity:** ${fmt(j.last_activity)}`);
78
+ lines.push(`**Commits:** ${fmt(j.git && j.git.commits)}`);
79
+ lines.push(`**Project started:** ${fmt(j.git && j.git.first_commit_at)}`);
80
+ lines.push("");
81
+ lines.push("### Phases");
82
+ lines.push("");
83
+ lines.push("| Phase | Name | Plans | Completed | Status | % |");
84
+ lines.push("|-------|------|-------|-----------|--------|---|");
85
+ for (const ph of (j.phases || [])) {
86
+ const pct = ph.plans_total > 0 ? Math.round(ph.plans_complete / ph.plans_total * 100) : 0;
87
+ lines.push(`| ${ph.number} | ${ph.name} | ${ph.plans_total} | ${ph.plans_complete} | ${ph.status} | ${pct}% |`);
88
+ }
89
+ lines.push("");
90
+ lines.push("### Metrics by Phase");
91
+ lines.push("");
92
+ lines.push("| Phase | Records | Tokens In | Tokens Out | Avg Opus ms | Avg Sonnet ms | Avg Haiku ms | Errors |");
93
+ lines.push("|-------|---------|-----------|------------|-------------|---------------|--------------|--------|");
94
+ for (const ph of (j.phases || [])) {
95
+ const m = (j.metrics_by_phase || {})[ph.number];
96
+ if (!m || m.record_count === 0) {
97
+ lines.push(`| ${ph.number} | — | — | — | — | — | — | — |`);
98
+ continue;
99
+ }
100
+ const t = m.avg_duration_ms_by_tier || {};
101
+ lines.push(`| ${ph.number} | ${m.record_count} | ${fmt(m.total_tokens_in)} | ${fmt(m.total_tokens_out)} | ${fmt(t.opus)} | ${fmt(t.sonnet)} | ${fmt(t.haiku)} | ${m.error_count} |`);
102
+ }
103
+ process.stdout.write(lines.join("\n") + "\n");
104
+ ' "$STATS_JSON"
105
+ ```
106
+
107
+ ## No Commit
108
+
109
+ Stats is read-only (D-20 SC-5). No files are written, no state is
110
+ mutated, no git commit is made. The markdown goes directly to stdout
111
+ and is rendered by the agent CLI.
112
+
113
+ ## Scope Guardrail
114
+
115
+ <scope_guardrail>
116
+ **Do:**
117
+ - Consume `node np-tools.cjs stats json` — trust its
118
+ `schema_version: 1` output shape (Plan 10-01-T04 contract).
119
+ - Render `tokens_in` / `tokens_out` as `—` when null (Phase 9 D-09
120
+ non-claude runtimes; D-15 hybrid-output decision).
121
+ - Render the progress bar as a 20-char `[████░░…]` string (ADR-0002
122
+ — no cli-progress dep).
123
+ - Keep the workflow read-only — no files written, no STATE mutated,
124
+ no git commit (D-20 SC-5).
125
+
126
+ **Don't:**
127
+ - Re-implement JSONL aggregation inline — `lib/metrics-aggregate.cjs`
128
+ owns the schema (D-18).
129
+ - Write any files — this workflow is a render, not a producer.
130
+ - Add a git commit — there is nothing to commit.
131
+ - Invoke host-specific prompt tools directly (the BARE_ASKUSER lint
132
+ in `bin/check-workflows.cjs` blocks them) — route through
133
+ `node np-tools.cjs askuser --json '…'` if prompts are ever added.
134
+ - Add a `metrics record` block. No Task/Spawn site; Pitfall 9 /
135
+ `workflow-missing-metrics` is exempt.
136
+ </scope_guardrail>
137
+
138
+ ## Output
139
+
140
+ - Markdown snapshot on stdout with Project Stats header, Phases
141
+ table, and Metrics by Phase table.
142
+ - No files created. No state mutated. No git commit.
143
+
144
+ ## Success Criteria
145
+
146
+ - [ ] Data sourced exclusively from `node np-tools.cjs stats json`
147
+ (Plan 10-01-T04) — no inline JSONL parsing.
148
+ - [ ] Null `tokens_in` / `tokens_out` render as `—` (D-09 / D-15).
149
+ - [ ] Progress bar is a 20-char block-string (ADR-0002).
150
+ - [ ] Phases table contains phase / name / plans_total / completed
151
+ / status / percent.
152
+ - [ ] Metrics-by-phase table shows records / tokens / tier-avg
153
+ durations / errors per phase.
154
+ - [ ] Zero file writes, zero state mutations, zero commits (D-20
155
+ SC-5).
156
+ - [ ] Lint clean under `bin/check-workflows.cjs` — no BARE_ASKUSER
157
+ violations, no DIRECT_READ matches.
158
+
159
+ ## Related Workflows
160
+
161
+ - **`/np:progress`** — base-workflow percent snapshot (subset).
162
+ - **`/np:session-report`** — commits a rendered report with
163
+ since-last-session metrics (the producer pair for `/np:stats`).
164
+
165
+ ## Design Notes
166
+
167
+ Phases-table + metrics aggregation combined per D-15. Stats CLI
168
+ (`bin/np-tools/stats.cjs`) is the single data source (D-20 SC-5).
169
+ Null-token semantics from Phase 9 D-09. Progress bar uses block
170
+ characters (ADR-0002) instead of a cli-progress dep.