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.
- package/agents/np-ai-researcher.md +140 -0
- package/agents/np-code-fixer.md +363 -0
- package/agents/np-code-reviewer.md +351 -0
- package/agents/np-domain-researcher.md +136 -0
- package/agents/np-eval-auditor.md +167 -0
- package/agents/np-eval-planner.md +153 -0
- package/agents/np-executor.md +72 -0
- package/agents/np-framework-selector.md +171 -0
- package/agents/np-nyquist-auditor.md +185 -0
- package/agents/np-plan-checker.md +165 -0
- package/agents/np-planner.md +199 -0
- package/agents/np-researcher.md +150 -0
- package/agents/np-security-auditor.md +206 -0
- package/agents/np-ui-auditor.md +369 -0
- package/agents/np-ui-checker.md +192 -0
- package/agents/np-ui-researcher.md +324 -0
- package/agents/np-verifier.md +79 -0
- package/bin/check-coverage.cjs +40 -0
- package/bin/check-workflows.cjs +171 -0
- package/bin/check-workflows.test.cjs +208 -0
- package/bin/install.js +500 -0
- package/bin/np-tools/_commands.cjs +70 -0
- package/bin/np-tools/add-tests.cjs +171 -0
- package/bin/np-tools/add-tests.test.cjs +122 -0
- package/bin/np-tools/add-todo.cjs +108 -0
- package/bin/np-tools/add-todo.test.cjs +112 -0
- package/bin/np-tools/agent-skills.cjs +14 -0
- package/bin/np-tools/agent-skills.test.cjs +42 -0
- package/bin/np-tools/ai-integration-phase.cjs +109 -0
- package/bin/np-tools/ai-integration-phase.test.cjs +123 -0
- package/bin/np-tools/askuser.cjs +53 -0
- package/bin/np-tools/askuser.test.cjs +49 -0
- package/bin/np-tools/autonomous.cjs +69 -0
- package/bin/np-tools/autonomous.test.cjs +74 -0
- package/bin/np-tools/checkpoint.cjs +101 -0
- package/bin/np-tools/checkpoint.test.cjs +119 -0
- package/bin/np-tools/code-review.cjs +133 -0
- package/bin/np-tools/code-review.test.cjs +96 -0
- package/bin/np-tools/commit-task.cjs +120 -0
- package/bin/np-tools/commit-task.test.cjs +160 -0
- package/bin/np-tools/commit.cjs +103 -0
- package/bin/np-tools/commit.test.cjs +93 -0
- package/bin/np-tools/config.cjs +101 -0
- package/bin/np-tools/config.test.cjs +71 -0
- package/bin/np-tools/discuss-phase-power.cjs +265 -0
- package/bin/np-tools/discuss-phase-power.test.cjs +242 -0
- package/bin/np-tools/discuss-phase.cjs +132 -0
- package/bin/np-tools/discuss-phase.test.cjs +148 -0
- package/bin/np-tools/dispatch.cjs +116 -0
- package/bin/np-tools/doctor.cjs +242 -0
- package/bin/np-tools/eval-review.cjs +116 -0
- package/bin/np-tools/eval-review.test.cjs +123 -0
- package/bin/np-tools/execute-phase.cjs +182 -0
- package/bin/np-tools/execute-phase.test.cjs +116 -0
- package/bin/np-tools/execute-plan.cjs +124 -0
- package/bin/np-tools/execute-plan.test.cjs +82 -0
- package/bin/np-tools/help.cjs +28 -0
- package/bin/np-tools/help.test.cjs +29 -0
- package/bin/np-tools/init-dispatch.test.cjs +91 -0
- package/bin/np-tools/metrics.cjs +97 -0
- package/bin/np-tools/metrics.test.cjs +188 -0
- package/bin/np-tools/new-milestone.cjs +288 -0
- package/bin/np-tools/new-milestone.test.cjs +166 -0
- package/bin/np-tools/new-project.cjs +284 -0
- package/bin/np-tools/new-project.test.cjs +165 -0
- package/bin/np-tools/next.cjs +7 -0
- package/bin/np-tools/next.test.cjs +30 -0
- package/bin/np-tools/park.cjs +48 -0
- package/bin/np-tools/park.test.cjs +50 -0
- package/bin/np-tools/pause-work.cjs +24 -0
- package/bin/np-tools/pause-work.test.cjs +74 -0
- package/bin/np-tools/phase.cjs +71 -0
- package/bin/np-tools/phase.test.cjs +81 -0
- package/bin/np-tools/plan-diff.cjs +57 -0
- package/bin/np-tools/plan-diff.test.cjs +134 -0
- package/bin/np-tools/plan-milestone-gaps.cjs +115 -0
- package/bin/np-tools/plan-milestone-gaps.test.cjs +122 -0
- package/bin/np-tools/plan-phase.cjs +350 -0
- package/bin/np-tools/plan-phase.test.cjs +263 -0
- package/bin/np-tools/progress.cjs +7 -0
- package/bin/np-tools/progress.test.cjs +44 -0
- package/bin/np-tools/queue.cjs +213 -0
- package/bin/np-tools/research-phase.cjs +144 -0
- package/bin/np-tools/research-phase.test.cjs +154 -0
- package/bin/np-tools/reset-slice.cjs +17 -0
- package/bin/np-tools/reset-slice.test.cjs +96 -0
- package/bin/np-tools/resolve-model.cjs +110 -0
- package/bin/np-tools/resolve-model.test.cjs +200 -0
- package/bin/np-tools/resume-work.cjs +76 -0
- package/bin/np-tools/resume-work.test.cjs +91 -0
- package/bin/np-tools/skip.cjs +48 -0
- package/bin/np-tools/skip.test.cjs +66 -0
- package/bin/np-tools/slug.cjs +34 -0
- package/bin/np-tools/slug.test.cjs +46 -0
- package/bin/np-tools/state.cjs +16 -0
- package/bin/np-tools/state.test.cjs +40 -0
- package/bin/np-tools/stats.cjs +151 -0
- package/bin/np-tools/stats.test.cjs +118 -0
- package/bin/np-tools/triage.cjs +128 -0
- package/bin/np-tools/ui-phase.cjs +108 -0
- package/bin/np-tools/ui-phase.test.cjs +121 -0
- package/bin/np-tools/ui-review.cjs +108 -0
- package/bin/np-tools/ui-review.test.cjs +120 -0
- package/bin/np-tools/undo-task.cjs +31 -0
- package/bin/np-tools/undo-task.test.cjs +117 -0
- package/bin/np-tools/undo.cjs +43 -0
- package/bin/np-tools/undo.test.cjs +120 -0
- package/bin/np-tools/unpark.cjs +48 -0
- package/bin/np-tools/unpark.test.cjs +50 -0
- package/bin/np-tools/verify-work.cjs +186 -0
- package/bin/np-tools/verify-work.test.cjs +97 -0
- package/docs/adr/0001-no-daemon-invariant.md +82 -0
- package/docs/adr/0002-zero-runtime-dependencies.md +90 -0
- package/docs/adr/0003-max-six-unit-types.md +85 -0
- package/docs/adr/0004-atomic-commit-per-unit.md +102 -0
- package/docs/adr/0005-three-orthogonal-file-trees.md +98 -0
- package/docs/adr/0006-yaml-dependency-amendment.md +60 -0
- package/docs/adr/README.md +27 -0
- package/docs/agent-frontmatter-schema.md +84 -0
- package/docs/phase-artifact-schemas.md +292 -0
- package/docs/phase-directory-layout.md +82 -0
- package/lib/__tests__/README.md +1 -0
- package/lib/agents.cjs +98 -0
- package/lib/agents.test.cjs +286 -0
- package/lib/askuser.cjs +36 -0
- package/lib/askuser.test.cjs +310 -0
- package/lib/checkpoint.cjs +135 -0
- package/lib/checkpoint.test.cjs +184 -0
- package/lib/core.cjs +165 -0
- package/lib/core.test.cjs +405 -0
- package/lib/fixtures/README.md +1 -0
- package/lib/fixtures/phase-tree/README.md +1 -0
- package/lib/fixtures/plans/cycle/PLAN.md +16 -0
- package/lib/fixtures/plans/cycle/tasks/T-01.md +20 -0
- package/lib/fixtures/plans/cycle/tasks/T-02.md +20 -0
- package/lib/fixtures/plans/cycle/tasks/T-03.md +20 -0
- package/lib/fixtures/plans/linear/PLAN.md +16 -0
- package/lib/fixtures/plans/linear/tasks/T-01.md +20 -0
- package/lib/fixtures/plans/linear/tasks/T-02.md +20 -0
- package/lib/fixtures/plans/linear/tasks/T-03.md +20 -0
- package/lib/fixtures/plans/parallel/PLAN.md +16 -0
- package/lib/fixtures/plans/parallel/tasks/T-01.md +20 -0
- package/lib/fixtures/plans/parallel/tasks/T-02.md +20 -0
- package/lib/fixtures/plans/parallel/tasks/T-03.md +20 -0
- package/lib/fixtures/plans/wave-conflict/PLAN.md +16 -0
- package/lib/fixtures/plans/wave-conflict/tasks/T-01.md +20 -0
- package/lib/fixtures/plans/wave-conflict/tasks/T-02.md +20 -0
- package/lib/fixtures/roadmap/ROADMAP-malformed.md +3 -0
- package/lib/fixtures/roadmap/ROADMAP-minimal.md +51 -0
- package/lib/fixtures/roadmap/roadmap-malformed.yaml +7 -0
- package/lib/fixtures/roadmap/roadmap-minimal.yaml +40 -0
- package/lib/fixtures/roadmap/roadmap-ten-phases.yaml +101 -0
- package/lib/fixtures/templates/phase-context.md +6 -0
- package/lib/fixtures/templates/plan-skeleton.md +6 -0
- package/lib/frontmatter.cjs +251 -0
- package/lib/frontmatter.test.cjs +177 -0
- package/lib/gaps.cjs +197 -0
- package/lib/gaps.test.cjs +200 -0
- package/lib/git.cjs +207 -0
- package/lib/git.test.cjs +305 -0
- package/lib/install/agents-md.cjs +77 -0
- package/lib/install/backup.cjs +70 -0
- package/lib/install/codex-toml.cjs +440 -0
- package/lib/install/managed-block.cjs +30 -0
- package/lib/install/manifest.cjs +148 -0
- package/lib/install/mcp-writer.cjs +127 -0
- package/lib/install/runtime-detect.cjs +44 -0
- package/lib/install/staging.cjs +149 -0
- package/lib/metrics-aggregate.cjs +229 -0
- package/lib/metrics-aggregate.test.cjs +192 -0
- package/lib/metrics.cjs +120 -0
- package/lib/metrics.test.cjs +182 -0
- package/lib/model-aliases.regression.test.cjs +16 -0
- package/lib/model-profiles.cjs +42 -0
- package/lib/model-profiles.test.cjs +61 -0
- package/lib/next.cjs +236 -0
- package/lib/next.test.cjs +194 -0
- package/lib/phase.cjs +95 -0
- package/lib/phase.test.cjs +189 -0
- package/lib/plan-checker-contract.test.cjs +72 -0
- package/lib/plan-diff.cjs +173 -0
- package/lib/plan-diff.test.cjs +217 -0
- package/lib/plan.cjs +85 -0
- package/lib/plan.test.cjs +263 -0
- package/lib/progress.cjs +95 -0
- package/lib/progress.test.cjs +116 -0
- package/lib/researcher-contract.test.cjs +61 -0
- package/lib/roadmap-render.cjs +206 -0
- package/lib/roadmap-render.test.cjs +121 -0
- package/lib/roadmap.cjs +416 -0
- package/lib/roadmap.test.cjs +371 -0
- package/lib/runtime/_contract.test.cjs +61 -0
- package/lib/runtime/_readline.cjs +119 -0
- package/lib/runtime/_readline.test.cjs +126 -0
- package/lib/runtime/claude.cjs +48 -0
- package/lib/runtime/claude.test.cjs +101 -0
- package/lib/runtime/codex.cjs +35 -0
- package/lib/runtime/codex.test.cjs +114 -0
- package/lib/runtime/gemini.cjs +35 -0
- package/lib/runtime/gemini.test.cjs +109 -0
- package/lib/runtime/index.cjs +49 -0
- package/lib/runtime/index.test.cjs +181 -0
- package/lib/runtime/opencode.cjs +35 -0
- package/lib/runtime/opencode.test.cjs +124 -0
- package/lib/state.cjs +205 -0
- package/lib/state.test.cjs +264 -0
- package/lib/surface-audit.test.cjs +46 -0
- package/lib/tasks.cjs +327 -0
- package/lib/tasks.test.cjs +389 -0
- package/lib/template.cjs +66 -0
- package/lib/template.test.cjs +159 -0
- package/lib/undo.cjs +179 -0
- package/lib/undo.test.cjs +261 -0
- package/lib/verify.cjs +116 -0
- package/lib/verify.test.cjs +187 -0
- package/np-tools.cjs +303 -0
- package/package.json +39 -0
- package/templates/AI-SPEC.md +90 -0
- package/templates/CONTEXT.md +32 -0
- package/templates/PLAN.md +69 -0
- package/templates/PROJECT.md +60 -0
- package/templates/REQUIREMENTS.md +38 -0
- package/templates/SECURITY.md +61 -0
- package/templates/UI-SPEC.md +64 -0
- package/templates/VALIDATION.md +76 -0
- package/templates/claude/payload/README.md +11 -0
- package/templates/opencode/opencode.json +6 -0
- package/templates/opencode/payload/AGENTS.md +9 -0
- package/workflows/add-backlog.md +212 -0
- package/workflows/add-tests.md +69 -0
- package/workflows/add-todo.md +222 -0
- package/workflows/ai-integration-phase.md +230 -0
- package/workflows/autonomous.md +94 -0
- package/workflows/cleanup.md +325 -0
- package/workflows/code-review-fix.md +435 -0
- package/workflows/code-review.md +447 -0
- package/workflows/discuss-phase-assumptions.md +269 -0
- package/workflows/discuss-phase-power.md +139 -0
- package/workflows/discuss-phase.md +386 -0
- package/workflows/dispatch.md +9 -0
- package/workflows/doctor.md +10 -0
- package/workflows/eval-review.md +243 -0
- package/workflows/execute-phase.md +142 -0
- package/workflows/execute-plan.md +82 -0
- package/workflows/help.md +8 -0
- package/workflows/new-milestone.md +166 -0
- package/workflows/new-project.md +213 -0
- package/workflows/next.md +8 -0
- package/workflows/note.md +244 -0
- package/workflows/park.md +29 -0
- package/workflows/pause-work.md +34 -0
- package/workflows/plan-milestone-gaps.md +233 -0
- package/workflows/plan-phase.md +351 -0
- package/workflows/progress.md +8 -0
- package/workflows/queue.md +9 -0
- package/workflows/research-phase.md +327 -0
- package/workflows/reset-slice.md +39 -0
- package/workflows/resume-work.md +79 -0
- package/workflows/review.md +489 -0
- package/workflows/secure-phase.md +209 -0
- package/workflows/session-report.md +243 -0
- package/workflows/skip.md +29 -0
- package/workflows/state.md +7 -0
- package/workflows/stats.md +170 -0
- package/workflows/thread.md +214 -0
- package/workflows/triage.md +9 -0
- package/workflows/ui-phase.md +246 -0
- package/workflows/ui-review.md +222 -0
- package/workflows/undo-task.md +42 -0
- package/workflows/undo.md +55 -0
- package/workflows/unpark.md +29 -0
- package/workflows/validate-phase.md +231 -0
- package/workflows/verify-work.md +83 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
command: np:ai-integration-phase
|
|
3
|
+
description: Generate AI-SPEC.md for AI/ML phases via 4-agent chain (np-framework-selector → np-ai-researcher → np-domain-researcher → np-eval-planner) with a completeness validation gate.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# np:ai-integration-phase
|
|
7
|
+
|
|
8
|
+
Produces `{phase_dir}/{padded}-AI-SPEC.md` by running a strict-serial 4-agent chain.
|
|
9
|
+
Inserts between `/np:discuss-phase` and `/np:plan-phase`. Locks domain
|
|
10
|
+
context, framework selection, implementation patterns, and evaluation
|
|
11
|
+
strategy BEFORE the planner creates tasks.
|
|
12
|
+
|
|
13
|
+
Every Task-spawn site is wrapped in the Plan 09-05 metrics + resolve-model
|
|
14
|
+
pattern (D-06, D-01). `RUNTIME` is detected once at the top of the bash
|
|
15
|
+
block and re-used by every `metrics record` call.
|
|
16
|
+
|
|
17
|
+
## Initialize
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
PHASE="$1"
|
|
21
|
+
if [[ -z "$PHASE" ]]; then
|
|
22
|
+
echo "Usage: /np:ai-integration-phase <phase-number>" >&2
|
|
23
|
+
exit 2
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
INIT=$(node np-tools.cjs init ai-integration-phase "$PHASE")
|
|
27
|
+
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
28
|
+
RUNTIME=$(node -e "console.log(require('./lib/runtime/index.cjs').detect().runtime)")
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Parse JSON for: `phase`, `padded`, `phase_dir`, `ai_spec_path`,
|
|
32
|
+
`has_ai_spec`, `template_path`, `agents.framework_selector`,
|
|
33
|
+
`agents.ai_researcher`, `agents.domain_researcher`, `agents.eval_planner`.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
PADDED=$(echo "$INIT" | jq -r '.padded')
|
|
37
|
+
PHASE_DIR=$(echo "$INIT" | jq -r '.phase_dir')
|
|
38
|
+
AI_SPEC_PATH=$(echo "$INIT" | jq -r '.ai_spec_path')
|
|
39
|
+
HAS_AI_SPEC=$(echo "$INIT" | jq -r '.has_ai_spec')
|
|
40
|
+
TEMPLATE_PATH=$(echo "$INIT" | jq -r '.template_path')
|
|
41
|
+
PLAN_ID="${PADDED}-ai-integration"
|
|
42
|
+
TASK_ID="${PADDED}-ai-integration"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Pre-Flight Gates
|
|
46
|
+
|
|
47
|
+
<pre_flight>
|
|
48
|
+
|
|
49
|
+
### Gate 1 — AI-SPEC.md already exists
|
|
50
|
+
|
|
51
|
+
If `has_ai_spec == true`:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
if [[ "$HAS_AI_SPEC" == "true" ]]; then
|
|
55
|
+
CHOICE=$(node np-tools.cjs askuser --json '{
|
|
56
|
+
"type": "select",
|
|
57
|
+
"header": "Existing AI-SPEC",
|
|
58
|
+
"question": "AI-SPEC.md already exists for Phase '"$PHASE"'. What would you like to do?",
|
|
59
|
+
"options": [
|
|
60
|
+
{"label": "Update — re-run with existing as baseline", "description": "Re-runs the 4-agent chain against the current spec."},
|
|
61
|
+
{"label": "View — display current AI-SPEC and exit", "description": "Reads the file and exits without changes."},
|
|
62
|
+
{"label": "Skip — keep current AI-SPEC and exit", "description": "Leaves the file untouched."}
|
|
63
|
+
]
|
|
64
|
+
}')
|
|
65
|
+
case "$CHOICE" in
|
|
66
|
+
"View"*) cat "$AI_SPEC_PATH"; exit 0 ;;
|
|
67
|
+
"Skip"*) exit 0 ;;
|
|
68
|
+
esac
|
|
69
|
+
fi
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
</pre_flight>
|
|
73
|
+
|
|
74
|
+
## Philosophy
|
|
75
|
+
|
|
76
|
+
<philosophy>
|
|
77
|
+
AI-SPEC.md locks the four things AI projects most often ship wrong: the
|
|
78
|
+
framework choice (too early / for the wrong reason), the domain rubrics
|
|
79
|
+
(assumed without asking an expert), the implementation pattern (copy-paste
|
|
80
|
+
from outdated docs), and the evaluation strategy (retrofitted after code
|
|
81
|
+
is merged). Running this workflow BEFORE `/np:plan-phase` surfaces those
|
|
82
|
+
decisions as explicit artifacts the planner and plan-checker can then
|
|
83
|
+
enforce.
|
|
84
|
+
</philosophy>
|
|
85
|
+
|
|
86
|
+
## Main Flow
|
|
87
|
+
|
|
88
|
+
The 4 agents run in strict serial order. Each spawn is wrapped in the
|
|
89
|
+
Plan-09-05 metrics pattern. A failure in any step aborts the chain — the
|
|
90
|
+
next agent reads the partial AI-SPEC.md that prior agents wrote.
|
|
91
|
+
|
|
92
|
+
### Step 0 — Initialize AI-SPEC.md from template
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
if [[ "$HAS_AI_SPEC" != "true" ]]; then
|
|
96
|
+
cp "$TEMPLATE_PATH" "$AI_SPEC_PATH"
|
|
97
|
+
fi
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Step 1 — Framework selection (np-framework-selector, opus)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
START=$(node np-tools.cjs metrics start-timestamp)
|
|
104
|
+
MODEL=$(node np-tools.cjs resolve-model np-framework-selector --profile balanced)
|
|
105
|
+
# Spawn agent=np-framework-selector model=$MODEL
|
|
106
|
+
# input: phase_number=$PHASE, ai_spec_path=$AI_SPEC_PATH, context=$PHASE_DIR/$PADDED-CONTEXT.md
|
|
107
|
+
# output: section 2 of AI-SPEC.md (framework scoring + selected)
|
|
108
|
+
END=$(node np-tools.cjs metrics end-timestamp)
|
|
109
|
+
node np-tools.cjs metrics record \
|
|
110
|
+
--agent np-framework-selector --tier opus --resolved-model "$MODEL" \
|
|
111
|
+
--phase "$PHASE" --plan "$PLAN_ID" --task "$TASK_ID" \
|
|
112
|
+
--started "$START" --ended "$END" \
|
|
113
|
+
--tokens-in "${TOKENS_IN:-0}" --tokens-out "${TOKENS_OUT:-0}" \
|
|
114
|
+
--retry-count 0 --status ok --runtime "$RUNTIME"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Step 2 — AI researcher (np-ai-researcher, sonnet)
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
START=$(node np-tools.cjs metrics start-timestamp)
|
|
121
|
+
MODEL=$(node np-tools.cjs resolve-model np-ai-researcher --profile balanced)
|
|
122
|
+
# Spawn agent=np-ai-researcher model=$MODEL
|
|
123
|
+
# input: ai_spec_path=$AI_SPEC_PATH (framework from step 1 already present)
|
|
124
|
+
# output: sections 3 (Implementation) + 4b (Pydantic models) of AI-SPEC.md
|
|
125
|
+
END=$(node np-tools.cjs metrics end-timestamp)
|
|
126
|
+
node np-tools.cjs metrics record \
|
|
127
|
+
--agent np-ai-researcher --tier sonnet --resolved-model "$MODEL" \
|
|
128
|
+
--phase "$PHASE" --plan "$PLAN_ID" --task "$TASK_ID" \
|
|
129
|
+
--started "$START" --ended "$END" \
|
|
130
|
+
--tokens-in "${TOKENS_IN:-0}" --tokens-out "${TOKENS_OUT:-0}" \
|
|
131
|
+
--retry-count 0 --status ok --runtime "$RUNTIME"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Step 3 — Domain researcher (np-domain-researcher, sonnet)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
START=$(node np-tools.cjs metrics start-timestamp)
|
|
138
|
+
MODEL=$(node np-tools.cjs resolve-model np-domain-researcher --profile balanced)
|
|
139
|
+
# Spawn agent=np-domain-researcher model=$MODEL
|
|
140
|
+
# input: ai_spec_path=$AI_SPEC_PATH
|
|
141
|
+
# output: section 1b (Domain Context) of AI-SPEC.md
|
|
142
|
+
END=$(node np-tools.cjs metrics end-timestamp)
|
|
143
|
+
node np-tools.cjs metrics record \
|
|
144
|
+
--agent np-domain-researcher --tier sonnet --resolved-model "$MODEL" \
|
|
145
|
+
--phase "$PHASE" --plan "$PLAN_ID" --task "$TASK_ID" \
|
|
146
|
+
--started "$START" --ended "$END" \
|
|
147
|
+
--tokens-in "${TOKENS_IN:-0}" --tokens-out "${TOKENS_OUT:-0}" \
|
|
148
|
+
--retry-count 0 --status ok --runtime "$RUNTIME"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Step 4 — Eval planner (np-eval-planner, opus)
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
START=$(node np-tools.cjs metrics start-timestamp)
|
|
155
|
+
MODEL=$(node np-tools.cjs resolve-model np-eval-planner --profile balanced)
|
|
156
|
+
# Spawn agent=np-eval-planner model=$MODEL
|
|
157
|
+
# input: ai_spec_path=$AI_SPEC_PATH (domain rubrics from step 3 ground the dimensions)
|
|
158
|
+
# output: sections 5 (Eval Dimensions), 6 (Guardrails), 7 (Monitoring)
|
|
159
|
+
END=$(node np-tools.cjs metrics end-timestamp)
|
|
160
|
+
node np-tools.cjs metrics record \
|
|
161
|
+
--agent np-eval-planner --tier opus --resolved-model "$MODEL" \
|
|
162
|
+
--phase "$PHASE" --plan "$PLAN_ID" --task "$TASK_ID" \
|
|
163
|
+
--started "$START" --ended "$END" \
|
|
164
|
+
--tokens-in "${TOKENS_IN:-0}" --tokens-out "${TOKENS_OUT:-0}" \
|
|
165
|
+
--retry-count 0 --status ok --runtime "$RUNTIME"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Validation Gate
|
|
169
|
+
|
|
170
|
+
After all 4 agents finish, grep the completed AI-SPEC.md for the required
|
|
171
|
+
section headers. Missing any → ask the user to re-run that step or accept
|
|
172
|
+
with warnings.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
MISSING=""
|
|
176
|
+
for SECTION in "## 1b. Domain Context" "## 2. Framework Selection" "## 3. Implementation" "## 5. Eval Dimensions" "## 6. Guardrails"; do
|
|
177
|
+
if ! grep -qF "$SECTION" "$AI_SPEC_PATH"; then
|
|
178
|
+
MISSING="$MISSING\n - missing: $SECTION"
|
|
179
|
+
fi
|
|
180
|
+
done
|
|
181
|
+
|
|
182
|
+
if [[ -n "$MISSING" ]]; then
|
|
183
|
+
CHOICE=$(node np-tools.cjs askuser --json '{
|
|
184
|
+
"type": "select",
|
|
185
|
+
"header": "AI-SPEC validation — missing sections",
|
|
186
|
+
"question": "Some required AI-SPEC sections are missing. What would you like to do?",
|
|
187
|
+
"options": [
|
|
188
|
+
{"label": "Re-run eval planner", "description": "Spawn np-eval-planner once more with current AI-SPEC state."},
|
|
189
|
+
{"label": "Accept with warnings", "description": "Proceed and surface the gaps in the commit message."},
|
|
190
|
+
{"label": "Abort", "description": "Exit without committing."}
|
|
191
|
+
]
|
|
192
|
+
}')
|
|
193
|
+
case "$CHOICE" in
|
|
194
|
+
"Abort") exit 1 ;;
|
|
195
|
+
esac
|
|
196
|
+
fi
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Commit
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
git add "$AI_SPEC_PATH"
|
|
203
|
+
git commit -m "docs(${PADDED}): generate AI-SPEC.md via 4-agent chain"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Scope Guardrail
|
|
207
|
+
|
|
208
|
+
<scope_guardrail>
|
|
209
|
+
**Do:**
|
|
210
|
+
- Run the 4 agents in strict serial order (selector → ai → domain → eval).
|
|
211
|
+
- Emit a metrics record AFTER every Task spawn (D-06).
|
|
212
|
+
- Resolve every MODEL via `np-tools.cjs resolve-model` — no hardcoded IDs.
|
|
213
|
+
- Use `np-tools.cjs askuser` for every prompt (Phase-3 INST-03 invariant).
|
|
214
|
+
- Commit the final AI-SPEC.md when validation passes or the user accepts.
|
|
215
|
+
|
|
216
|
+
**Don't:**
|
|
217
|
+
- Invoke the host-specific prompt tool directly — always route through `np-tools.cjs askuser`.
|
|
218
|
+
- Parallelize the chain — later agents read what earlier agents wrote.
|
|
219
|
+
- Call any tools binary other than `np-tools.cjs` (the sole CLI entry per Plan 09-05 D-14).
|
|
220
|
+
- Reference legacy homedir payload paths — those directories do not exist
|
|
221
|
+
in nubos-pilot projects.
|
|
222
|
+
- Skip any metrics record block — the Phase-10 np:stats consumer expects
|
|
223
|
+
one record per Task spawn.
|
|
224
|
+
</scope_guardrail>
|
|
225
|
+
|
|
226
|
+
## Output
|
|
227
|
+
|
|
228
|
+
- `{phase_dir}/{padded}-AI-SPEC.md` — filled-in AI design contract
|
|
229
|
+
- 4 metrics records in `.nubos-pilot/metrics/phase-${PHASE}.jsonl`
|
|
230
|
+
- One git commit (when validation passes or user accepts with warnings)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
command: np:autonomous
|
|
3
|
+
description: In-session auto-advance loop — runs phase after phase, asking the user between phases. NOT a daemon (ADR-0001 No-Daemon Invariant).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /np:autonomous
|
|
7
|
+
|
|
8
|
+
<objective>
|
|
9
|
+
Iterate `resolveGate` → `/np:execute-phase` → `/np:verify-work` → askUser
|
|
10
|
+
until the user says stop. The loop is an in-session for-loop executed by
|
|
11
|
+
the host agent — it does NOT fork, spawn a detached process, or survive
|
|
12
|
+
session end (ADR-0001).
|
|
13
|
+
</objective>
|
|
14
|
+
|
|
15
|
+
## Initialize
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
PHASE="$1"
|
|
19
|
+
GATE_JSON=$(node np-tools.cjs init autonomous "$PHASE")
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Parse: `status` (ok | advancement-blocked), `gate.rule`, `gate.task`.
|
|
23
|
+
|
|
24
|
+
If `status == advancement-blocked`:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
CHOICE=$(node np-tools.cjs askuser --json '{
|
|
28
|
+
"type": "select",
|
|
29
|
+
"header": "Phase kann nicht fortschreiten",
|
|
30
|
+
"question": "Jede Pending-Task in Wave 1 ist skipped oder parked. Was tun?",
|
|
31
|
+
"options": [
|
|
32
|
+
{"label": "Unpark tasks and continue", "description": "Dispatch /np:unpark zur Reaktivierung, dann erneut /np:autonomous starten."},
|
|
33
|
+
{"label": "Abort", "description": "Exit; User entscheidet manuell."}
|
|
34
|
+
]
|
|
35
|
+
}')
|
|
36
|
+
exit 0
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Execution — in-session loop (NO daemon, NO background)
|
|
40
|
+
|
|
41
|
+
The loop runs inside the agent context. Each iteration:
|
|
42
|
+
|
|
43
|
+
1. Re-read gate for `$PHASE`: `node np-tools.cjs init autonomous "$PHASE"`.
|
|
44
|
+
2. If `gate.rule == 1` → dispatch `/np:discuss-phase $PHASE`, wait for
|
|
45
|
+
completion, continue.
|
|
46
|
+
3. If `gate.rule == 2` → dispatch `/np:plan-phase $PHASE`, wait, continue.
|
|
47
|
+
4. If `gate.rule == 3` → dispatch `/np:execute-phase $PHASE`, wait, continue.
|
|
48
|
+
5. If `gate.rule == 4` → dispatch `/np:verify-work $PHASE`, wait, continue.
|
|
49
|
+
6. If `gate.rule == 5` → phase verified. Ask the user:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
CHOICE=$(node np-tools.cjs askuser --json '{
|
|
53
|
+
"type": "select",
|
|
54
|
+
"header": "Phase verifiziert",
|
|
55
|
+
"question": "Phase '"$PHASE"' ist verified. Weitermachen mit der nächsten Phase?",
|
|
56
|
+
"options": [
|
|
57
|
+
{"label": "Continue", "description": "Nächste Phase automatisch starten."},
|
|
58
|
+
{"label": "Pause", "description": "Stop, handoff via /np:pause-work."},
|
|
59
|
+
{"label": "Abort", "description": "Exit."}
|
|
60
|
+
]
|
|
61
|
+
}')
|
|
62
|
+
case "$CHOICE" in
|
|
63
|
+
"Continue") PHASE=$((PHASE + 1));;
|
|
64
|
+
"Pause") node np-tools.cjs init pause-work; exit 0 ;;
|
|
65
|
+
"Abort") exit 0 ;;
|
|
66
|
+
esac
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Hard-stops (D-15)
|
|
70
|
+
|
|
71
|
+
- Any `NubosPilotError` with severity fatal → exit non-zero immediately.
|
|
72
|
+
- commit-task loud-fails (gitignored-only) → propagate, do not retry.
|
|
73
|
+
- `advancement-blocked` → askUser gate above, never silent spin.
|
|
74
|
+
|
|
75
|
+
## Scope Guardrail
|
|
76
|
+
|
|
77
|
+
<!-- scope_guardrail -->
|
|
78
|
+
**Do:**
|
|
79
|
+
- Drive the loop inside the current agent session.
|
|
80
|
+
- Ask the user at every phase boundary (D-14).
|
|
81
|
+
- Respect `resolveGate` outputs verbatim.
|
|
82
|
+
|
|
83
|
+
**Don't:**
|
|
84
|
+
- Fork, `setInterval`, `nohup`, `spawn --detached`, or `&` the loop body —
|
|
85
|
+
ADR-0001 bans daemon behavior (T-06-14).
|
|
86
|
+
- Skip the inter-phase askUser gate.
|
|
87
|
+
- Silently advance past a Fail verdict from `/np:verify-work`.
|
|
88
|
+
<!-- /scope_guardrail -->
|
|
89
|
+
|
|
90
|
+
## Output
|
|
91
|
+
|
|
92
|
+
- N phases advanced (N = user choice count).
|
|
93
|
+
- Full audit trail in per-phase `PLAN-REVIEW.md` + `VERIFICATION.md`.
|
|
94
|
+
- Session ends cleanly; resume via `/np:resume-work`.
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
---
|
|
2
|
+
command: np:cleanup
|
|
3
|
+
description: Archive completed milestones to .nubos-pilot/archive/v<X.Y>/ (D-06). Collapses each archived milestone's ROADMAP.md block into a <details> summary (D-07). Idempotent per D-08 (target-path existence check). One atomic chore commit (D-09). Path-preserving git mv (rename history kept).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# np:cleanup
|
|
7
|
+
|
|
8
|
+
Implements UTIL-08. Enumerates milestones whose every phase is marked
|
|
9
|
+
complete, previews the archive plan, moves phase directories into
|
|
10
|
+
`.nubos-pilot/archive/v<X.Y>/` with `git mv` (preserving rename
|
|
11
|
+
history), collapses the milestone's ROADMAP.md block into a
|
|
12
|
+
`<details>` wrapper via `lib/roadmap.cjs.collapseMilestone` (landed
|
|
13
|
+
Plan 10-01-T05), and commits the whole operation as a single atomic
|
|
14
|
+
chore unit.
|
|
15
|
+
|
|
16
|
+
Four deliberate design choices:
|
|
17
|
+
|
|
18
|
+
- **D-06** — archive layout lives at `.nubos-pilot/archive/v<X.Y>/`,
|
|
19
|
+
the canonical home under the project-state tree (ADR-0005
|
|
20
|
+
3-file-tree invariant).
|
|
21
|
+
- **D-07** — collapsed milestones render as `<details>` blocks in
|
|
22
|
+
the generated ROADMAP.md rather than being deleted. Milestone
|
|
23
|
+
history stays visible on demand.
|
|
24
|
+
- **D-08** — idempotency. If `archive/v<X.Y>/` already exists for a
|
|
25
|
+
milestone, the workflow skips it. Partial-move recovery is safe
|
|
26
|
+
because per-milestone state is all-or-nothing.
|
|
27
|
+
- **D-09** — single atomic commit per run. All milestones archived
|
|
28
|
+
in one invocation land in one commit (ADR-0004).
|
|
29
|
+
|
|
30
|
+
Security-relevant defenses built in:
|
|
31
|
+
|
|
32
|
+
- **T-10-06-03 (partial-move)** — idempotent skip + per-mv failure
|
|
33
|
+
abort + single commit after ALL mvs succeed.
|
|
34
|
+
- **T-10-06-04 (symlink traversal)** — `lstat`-based reject before
|
|
35
|
+
every `git mv` (pattern reused from Phase 7 Plan 07-02 backup.cjs).
|
|
36
|
+
|
|
37
|
+
Pure CRUD / filesystem workflow — no agent spawn, no resolve-model,
|
|
38
|
+
no metrics record. Pitfall 9 / `workflow-missing-metrics` is exempt.
|
|
39
|
+
|
|
40
|
+
## Initialize
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
STATE_DIR=$(node -e "console.log(require('./lib/core.cjs').projectStateDir(process.cwd()))")
|
|
44
|
+
ARCHIVE_ROOT="${STATE_DIR}/archive"
|
|
45
|
+
mkdir -p "$ARCHIVE_ROOT"
|
|
46
|
+
|
|
47
|
+
DRY_RUN_FLAG=""
|
|
48
|
+
for arg in "$@"; do
|
|
49
|
+
case "$arg" in
|
|
50
|
+
--dry-run) DRY_RUN_FLAG="1" ;;
|
|
51
|
+
esac
|
|
52
|
+
done
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Enumerate Completed Milestones
|
|
56
|
+
|
|
57
|
+
All enumeration happens in Node so we can consume `lib/roadmap.cjs`
|
|
58
|
+
parse output directly (not re-implement YAML parsing). The query
|
|
59
|
+
filters: (a) skip the synthetic `backlog` milestone, (b) require
|
|
60
|
+
every phase in the milestone to have `status === "complete"`, (c)
|
|
61
|
+
skip if `archive/<id>/` already exists (D-08 idempotency), (d) list
|
|
62
|
+
the phase directories that exist on disk.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ACTIONS_JSON=$(node -e '
|
|
66
|
+
const { parseRoadmap } = require("./lib/roadmap.cjs");
|
|
67
|
+
const fs = require("node:fs");
|
|
68
|
+
const path = require("node:path");
|
|
69
|
+
const stateDir = process.argv[1];
|
|
70
|
+
const archive = path.join(stateDir, "archive");
|
|
71
|
+
const phasesRoot = path.join(stateDir, "phases");
|
|
72
|
+
const parsed = parseRoadmap(process.cwd());
|
|
73
|
+
const raw = parsed && parsed.doc ? parsed.doc : null;
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const m of (raw && raw.milestones) || []) {
|
|
76
|
+
if (!m || m.id === "backlog") continue;
|
|
77
|
+
const phases = m.phases || [];
|
|
78
|
+
if (phases.length === 0) continue;
|
|
79
|
+
const allDone = phases.every((ph) => ph && ph.status === "complete");
|
|
80
|
+
if (!allDone) continue;
|
|
81
|
+
const target = path.join(archive, m.id);
|
|
82
|
+
if (fs.existsSync(target)) continue;
|
|
83
|
+
let entries = [];
|
|
84
|
+
try { entries = fs.readdirSync(phasesRoot, { withFileTypes: true }); } catch (_e) {}
|
|
85
|
+
const phaseDirs = [];
|
|
86
|
+
for (const ph of phases) {
|
|
87
|
+
const padded = String(ph.number).padStart(2, "0");
|
|
88
|
+
const match = entries.filter((e) => e.isDirectory())
|
|
89
|
+
.map((e) => e.name)
|
|
90
|
+
.find((n) => n === padded || n.startsWith(padded + "-"));
|
|
91
|
+
if (match) phaseDirs.push(path.join(phasesRoot, match));
|
|
92
|
+
}
|
|
93
|
+
if (phaseDirs.length === 0) continue;
|
|
94
|
+
out.push({ milestone_id: m.id, target_dir: target, phase_dirs: phaseDirs });
|
|
95
|
+
}
|
|
96
|
+
process.stdout.write(JSON.stringify(out));
|
|
97
|
+
' "$STATE_DIR")
|
|
98
|
+
|
|
99
|
+
ACTION_COUNT=$(echo "$ACTIONS_JSON" | jq 'length')
|
|
100
|
+
if [[ "$ACTION_COUNT" -eq 0 ]]; then
|
|
101
|
+
echo "Nothing to archive (no complete + un-archived milestones)."
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Symlink Safety Check (T-10-06-04)
|
|
107
|
+
|
|
108
|
+
Before any `git mv`, `lstat` each phase directory and reject
|
|
109
|
+
symlinks. A symlinked phase dir would escape the archive root on
|
|
110
|
+
rename (the link target might point outside `.nubos-pilot/phases/`).
|
|
111
|
+
Pattern reused from Phase 7 Plan 07-02 `backup.cjs`.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
SYMLINK_FOUND=$(node -e '
|
|
115
|
+
const fs = require("node:fs");
|
|
116
|
+
const actions = JSON.parse(process.argv[1]);
|
|
117
|
+
for (const a of actions) {
|
|
118
|
+
for (const d of a.phase_dirs) {
|
|
119
|
+
try {
|
|
120
|
+
const st = fs.lstatSync(d);
|
|
121
|
+
if (st.isSymbolicLink()) { process.stdout.write(d); process.exit(0); }
|
|
122
|
+
} catch (_e) {}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
' "$ACTIONS_JSON")
|
|
126
|
+
if [[ -n "$SYMLINK_FOUND" ]]; then
|
|
127
|
+
echo "Error: symlink detected at $SYMLINK_FOUND — refusing to archive. Remove or unlink and retry." >&2
|
|
128
|
+
exit 1
|
|
129
|
+
fi
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Dry-Run Summary + askuser Confirm
|
|
133
|
+
|
|
134
|
+
Show the plan before mutating anything. `--dry-run` exits after the
|
|
135
|
+
preview; otherwise confirm via `askuser` Pattern S-3.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
echo "$ACTIONS_JSON" | jq -r '.[] | "Milestone " + .milestone_id + ":\n → " + .target_dir + "\n Phases: " + ([.phase_dirs[] | split("/") | last] | join(", "))'
|
|
139
|
+
|
|
140
|
+
if [[ -n "$DRY_RUN_FLAG" ]]; then
|
|
141
|
+
echo "Dry-run: no changes made."
|
|
142
|
+
exit 0
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
CHOICE=$(node np-tools.cjs askuser --json '{
|
|
146
|
+
"type": "select",
|
|
147
|
+
"header": "Cleanup Preview",
|
|
148
|
+
"question": "Archive '"$ACTION_COUNT"' milestone(s) listed above?",
|
|
149
|
+
"options": [
|
|
150
|
+
{"label": "Yes — archive listed phases", "description": "Creates .nubos-pilot/archive/<id>/ + ROADMAP details-block + 1 atomic commit"},
|
|
151
|
+
{"label": "Cancel", "description": "Exits without mutation"}
|
|
152
|
+
]
|
|
153
|
+
}')
|
|
154
|
+
case "$CHOICE" in
|
|
155
|
+
"Cancel"*) exit 0 ;;
|
|
156
|
+
esac
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Execute Archive
|
|
160
|
+
|
|
161
|
+
Per-milestone loop: create the archive target dir, re-`lstat` every
|
|
162
|
+
phase directory IMMEDIATELY before each `git mv` and abort if a
|
|
163
|
+
symlink is detected, then call `lib/roadmap.cjs.collapseMilestone`
|
|
164
|
+
to flip the `collapsed: true` flag so `lib/roadmap-render.cjs` wraps
|
|
165
|
+
the block in a `<details>` summary (landed Plan 10-01-T05).
|
|
166
|
+
|
|
167
|
+
The symlink re-check is intentionally collocated with `git mv`
|
|
168
|
+
inside ONE Node process (no intermediate bash fork) so there is no
|
|
169
|
+
TOCTOU window between check and move. The earlier "Symlink Safety
|
|
170
|
+
Check" section is a defense-in-depth pre-flight that fails fast
|
|
171
|
+
before the askuser confirm; this block is the authoritative
|
|
172
|
+
just-in-time guard (T-10-06-04 mitigation).
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
node -e '
|
|
176
|
+
const fs = require("node:fs");
|
|
177
|
+
const { execFileSync } = require("node:child_process");
|
|
178
|
+
const { collapseMilestone } = require("./lib/roadmap.cjs");
|
|
179
|
+
const actions = JSON.parse(process.argv[1]);
|
|
180
|
+
for (const a of actions) {
|
|
181
|
+
fs.mkdirSync(a.target_dir, { recursive: true });
|
|
182
|
+
for (const src of a.phase_dirs) {
|
|
183
|
+
const st = fs.lstatSync(src);
|
|
184
|
+
if (st.isSymbolicLink()) {
|
|
185
|
+
process.stderr.write("Error: symlink detected at " + src + " — refusing to archive.\n");
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
execFileSync("git", ["mv", src, a.target_dir + "/"], { stdio: "inherit" });
|
|
190
|
+
} catch (err) {
|
|
191
|
+
process.stderr.write("git mv failed on " + src + "\n");
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
collapseMilestone(a.milestone_id);
|
|
196
|
+
}
|
|
197
|
+
' "$ACTIONS_JSON"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`git mv` preserves rename history (per Plan §interfaces / Research
|
|
201
|
+
§4) — later blame/log on the archived files still traces back
|
|
202
|
+
through the rename.
|
|
203
|
+
|
|
204
|
+
## Commit (D-09)
|
|
205
|
+
|
|
206
|
+
Single atomic commit per run. All milestones archived together land
|
|
207
|
+
in one `chore(10): ...` commit. Subject line names the first
|
|
208
|
+
milestone and a count suffix when N > 1.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
FIRST_MILESTONE=$(echo "$ACTIONS_JSON" | jq -r '.[0].milestone_id')
|
|
212
|
+
SUFFIX=""
|
|
213
|
+
if [[ "$ACTION_COUNT" -gt 1 ]]; then
|
|
214
|
+
SUFFIX=" and $((ACTION_COUNT - 1)) other(s)"
|
|
215
|
+
fi
|
|
216
|
+
node np-tools.cjs commit "chore(10): archive milestone ${FIRST_MILESTONE}${SUFFIX}" \
|
|
217
|
+
--files "${STATE_DIR}/archive/" "${STATE_DIR}/ROADMAP.md" "${STATE_DIR}/roadmap.yaml" "${STATE_DIR}/phases/"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## If a cleanup is interrupted
|
|
221
|
+
|
|
222
|
+
If `git mv` fails mid-operation (power loss, Ctrl-C), the working
|
|
223
|
+
tree may be in a mixed state — some phase dirs moved, some not.
|
|
224
|
+
Running `/np:cleanup` again is SAFE because:
|
|
225
|
+
|
|
226
|
+
- **Per-milestone idempotency (D-08):** milestones whose archive
|
|
227
|
+
target dir already exists are skipped.
|
|
228
|
+
- Within a milestone, `git mv` on a non-existent source fails
|
|
229
|
+
loudly; the workflow aborts WITHOUT committing.
|
|
230
|
+
|
|
231
|
+
If you see a partial state, either:
|
|
232
|
+
|
|
233
|
+
1. Run `/np:cleanup` again — re-processes only the un-archived
|
|
234
|
+
milestones.
|
|
235
|
+
2. Manually `git mv` the remaining phase dirs into their archive
|
|
236
|
+
target, then run
|
|
237
|
+
`node -e "require('./lib/roadmap.cjs').collapseMilestone('v<X.Y>')"`
|
|
238
|
+
and commit manually.
|
|
239
|
+
|
|
240
|
+
No automatic `git reset --hard HEAD` is performed — that would
|
|
241
|
+
destroy unrelated uncommitted work (Open Question #3 recommendation,
|
|
242
|
+
T-10-06-03 mitigation).
|
|
243
|
+
|
|
244
|
+
## Scope Guardrail
|
|
245
|
+
|
|
246
|
+
<scope_guardrail>
|
|
247
|
+
**Do:**
|
|
248
|
+
- Use `git mv` (not plain `mv`) — preserves rename history for
|
|
249
|
+
`git blame` / `git log --follow`.
|
|
250
|
+
- Check target-dir existence BEFORE attempting mvs (D-08
|
|
251
|
+
idempotency).
|
|
252
|
+
- Reject symlinks via `lstat` before every `git mv` (T-10-06-04).
|
|
253
|
+
- Single atomic commit after ALL mvs + `collapseMilestone` mutations
|
|
254
|
+
succeed (D-09).
|
|
255
|
+
- Show the dry-run preview before any mutation (S-3 pattern).
|
|
256
|
+
|
|
257
|
+
**Don't:**
|
|
258
|
+
- `git reset --hard` on failure (destroys unrelated user work per
|
|
259
|
+
Open Question #3 / T-10-06-03).
|
|
260
|
+
- Mutate archive targets that already exist (idempotency).
|
|
261
|
+
- Skip the `collapseMilestone` call — ROADMAP must reflect the
|
|
262
|
+
archive state (D-07).
|
|
263
|
+
- Invoke host-specific prompt tools directly (the BARE_ASKUSER lint
|
|
264
|
+
in `bin/check-workflows.cjs` blocks them) — route through
|
|
265
|
+
`node np-tools.cjs askuser --json '…'`.
|
|
266
|
+
- Add a `metrics record` block. No Task/Spawn site; Pitfall 9 /
|
|
267
|
+
`workflow-missing-metrics` is exempt.
|
|
268
|
+
</scope_guardrail>
|
|
269
|
+
|
|
270
|
+
## Output
|
|
271
|
+
|
|
272
|
+
- `.nubos-pilot/archive/v<X.Y>/` — one directory per archived
|
|
273
|
+
milestone containing the milestone's phase dirs (moved via
|
|
274
|
+
`git mv`, rename history preserved).
|
|
275
|
+
- `.nubos-pilot/ROADMAP.md` — regenerated with `<details>` wrapper
|
|
276
|
+
around each archived milestone's block (D-07 via
|
|
277
|
+
`lib/roadmap.cjs.collapseMilestone`).
|
|
278
|
+
- `.nubos-pilot/roadmap.yaml` — `collapsed: true` and `collapsed_at`
|
|
279
|
+
set for each archived milestone.
|
|
280
|
+
- `.nubos-pilot/phases/` — archived phase dirs removed.
|
|
281
|
+
- One atomic git commit
|
|
282
|
+
`chore(10): archive milestone <id> [and N other(s)]` (ADR-0004,
|
|
283
|
+
D-09).
|
|
284
|
+
|
|
285
|
+
## Success Criteria
|
|
286
|
+
|
|
287
|
+
- [ ] Synthetic `backlog` milestone is never archived.
|
|
288
|
+
- [ ] Only milestones with `every phase status === "complete"` are
|
|
289
|
+
considered.
|
|
290
|
+
- [ ] Milestones whose archive target already exists are skipped
|
|
291
|
+
(D-08 idempotency).
|
|
292
|
+
- [ ] Symlinks in candidate phase dirs abort the workflow
|
|
293
|
+
(T-10-06-04 mitigation).
|
|
294
|
+
- [ ] `--dry-run` exits after the preview with no mutation.
|
|
295
|
+
- [ ] Confirmation via `askuser` Pattern S-3 before any mutation.
|
|
296
|
+
- [ ] Phase dirs moved via `git mv` (preserves rename history).
|
|
297
|
+
- [ ] `lib/roadmap.cjs.collapseMilestone` called for every archived
|
|
298
|
+
milestone (D-07).
|
|
299
|
+
- [ ] Single atomic commit via `np-tools.cjs commit` after ALL mvs +
|
|
300
|
+
collapseMilestone calls succeed (D-09).
|
|
301
|
+
- [ ] Commit subject shape `chore(10): archive milestone <id>
|
|
302
|
+
[and N other(s)]`.
|
|
303
|
+
- [ ] Partial-move recovery is idempotent — re-running picks up
|
|
304
|
+
only un-archived milestones (T-10-06-03 mitigation).
|
|
305
|
+
- [ ] Lint clean under `bin/check-workflows.cjs` — no BARE_ASKUSER
|
|
306
|
+
violations, no DIRECT_READ matches.
|
|
307
|
+
|
|
308
|
+
## Related Workflows
|
|
309
|
+
|
|
310
|
+
- **`/np:session-report`** — per-session snapshot report (distinct
|
|
311
|
+
from milestone archival).
|
|
312
|
+
- **`/np:stats`** — read-only current-state view.
|
|
313
|
+
- **`/np:add-backlog <title>`** — adds ideas to the synthetic
|
|
314
|
+
backlog milestone (which `/np:cleanup` deliberately never
|
|
315
|
+
archives).
|
|
316
|
+
|
|
317
|
+
## Design Notes
|
|
318
|
+
|
|
319
|
+
D-06 fixes archive layout at `.nubos-pilot/archive/v<X.Y>/` — the
|
|
320
|
+
canonical home under the project-state tree (ADR-0005). D-07 collapse
|
|
321
|
+
renders archived milestones as `<details>` blocks instead of deleting
|
|
322
|
+
them so history stays visible on demand. D-08 idempotency via
|
|
323
|
+
existence-check enables safe re-runs. D-09 single-commit shape matches
|
|
324
|
+
ADR-0004. Symlink rejection via `lstat` (T-10-06-04) and path-preserving
|
|
325
|
+
`git mv` guard against filesystem-level surprises.
|