nubos-pilot 1.2.3 → 1.3.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/CHANGELOG.md +24 -0
- package/README.md +18 -1
- package/SECURITY.md +3 -4
- package/bin/np-tools/_commands.cjs +1 -0
- package/bin/np-tools/learnings.cjs +5 -1
- package/bin/np-tools/resolve-model.cjs +55 -1
- package/bin/np-tools/resolve-model.test.cjs +139 -0
- package/bin/np-tools/security.cjs +4 -1
- package/bin/np-tools/spawn-headless.cjs +135 -2
- package/bin/np-tools/spawn-headless.test.cjs +225 -40
- package/bin/np-tools/spawn-offhost.cjs +93 -0
- package/bin/np-tools/spawn-offhost.test.cjs +38 -0
- package/lib/agents.cjs +16 -2
- package/lib/config-schema.cjs +5 -1
- package/lib/headless-guard.cjs +127 -0
- package/lib/headless-guard.test.cjs +119 -0
- package/lib/learnings/extract.cjs +4 -4
- package/lib/learnings/extract.test.cjs +8 -8
- package/lib/model-providers.cjs +118 -0
- package/lib/model-providers.test.cjs +85 -0
- package/lib/runtime/agent-loop.cjs +64 -0
- package/lib/runtime/agent-loop.test.cjs +135 -0
- package/lib/runtime/dispatch.cjs +174 -0
- package/lib/runtime/dispatch.test.cjs +193 -0
- package/lib/runtime/preflight.cjs +68 -0
- package/lib/runtime/preflight.test.cjs +62 -0
- package/lib/runtime/providers/openai-compat.cjs +102 -0
- package/lib/runtime/providers/openai-compat.test.cjs +103 -0
- package/lib/runtime/tools/index.cjs +415 -0
- package/lib/runtime/tools/index.test.cjs +230 -0
- package/lib/security/review.cjs +4 -4
- package/lib/security/review.test.cjs +6 -6
- package/np-tools.cjs +1 -0
- package/package.json +1 -1
- package/templates/claude/payload/hooks/np-learnings-hook.cjs +1 -0
- package/templates/claude/payload/hooks/np-security-hook.cjs +1 -0
- package/workflows/add-tests.md +41 -0
- package/workflows/architect-phase.md +19 -0
- package/workflows/discuss-phase.md +29 -10
- package/workflows/execute-phase.md +93 -4
- package/workflows/plan-phase.md +57 -16
- package/workflows/research-phase.md +45 -0
- package/workflows/scan-codebase.md +21 -3
- package/workflows/validate-phase.md +30 -13
- package/workflows/verify-work.md +17 -0
package/workflows/plan-phase.md
CHANGED
|
@@ -241,17 +241,38 @@ for ITER in 1 2; do
|
|
|
241
241
|
# <prior_findings>$LAST_FINDINGS</prior_findings> (path to verdict JSON, R≥2)
|
|
242
242
|
# <agent_skills>$AGENT_SKILLS_PLANNER</agent_skills>
|
|
243
243
|
# Agent MUST: write/update slice plans inside $milestone_dir.
|
|
244
|
+
# Off-host (ADR-0021): when np-planner routes to an openai-compat provider
|
|
245
|
+
# (agent_routing), run it via spawn-offhost (below) INSTEAD of the Agent tool.
|
|
244
246
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
245
247
|
PLANNER_START=$(node .nubos-pilot/bin/np-tools.cjs metrics start-timestamp)
|
|
246
248
|
PLANNER_MODEL=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-planner --profile frontier)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
249
|
+
PLANNER_KIND=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-planner --json 2>/dev/null \
|
|
250
|
+
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).kind||"native")}catch{console.log("native")}})')
|
|
251
|
+
if [ "$PLANNER_KIND" = "openai-compat" ]; then
|
|
252
|
+
# np-planner is NOT Rule-9-audited and writes ONLY planning artefacts under
|
|
253
|
+
# .nubos-pilot/ (inside the repo cwd — NOT live code), so it runs off-host
|
|
254
|
+
# with the default cwd (repo root): Read/Grep/Glob over the whole repo + Write
|
|
255
|
+
# confined to cwd. NO --allow-bash and NO worktree (there is no live-code
|
|
256
|
+
# blast radius to isolate, unlike the executor). It writes slice plans into
|
|
257
|
+
# $milestone_dir exactly as the native planner does — no emit-and-persist
|
|
258
|
+
# contract needed. spawn-offhost records the metrics row itself.
|
|
259
|
+
PLANNER_PROMPT="${TMPDIR:-/tmp}/np-offhost-planner-${milestone_id}-i${ITER}.md"
|
|
260
|
+
# … render the SAME prompt the ACTION CONTRACT above describes (mode,
|
|
261
|
+
# milestone, milestone_dir, goal, requirements, prior_findings,
|
|
262
|
+
# agent_skills) PLUS $LANG_DIRECTIVE into "$PLANNER_PROMPT" …
|
|
263
|
+
node .nubos-pilot/bin/np-tools.cjs spawn-offhost \
|
|
264
|
+
--agent np-planner --task-file "$PLANNER_PROMPT" \
|
|
265
|
+
--phase "$PHASE" --plan "${milestone_id}-plan" >/dev/null
|
|
266
|
+
else
|
|
267
|
+
# → execute the Agent call per ACTION CONTRACT above (native host spawn), then:
|
|
268
|
+
PLANNER_END=$(node .nubos-pilot/bin/np-tools.cjs metrics end-timestamp)
|
|
269
|
+
node .nubos-pilot/bin/np-tools.cjs metrics record \
|
|
270
|
+
--agent np-planner --tier opus --resolved-model "$PLANNER_MODEL" \
|
|
271
|
+
--phase "$PHASE" --plan "${milestone_id}-plan" --task "${milestone_id}-planner-run" \
|
|
272
|
+
--started "$PLANNER_START" --ended "$PLANNER_END" \
|
|
273
|
+
--tokens-in "${TOKENS_IN:-0}" --tokens-out "${TOKENS_OUT:-0}" \
|
|
274
|
+
--retry-count 0 --status ok --runtime "$RUNTIME"
|
|
275
|
+
fi
|
|
255
276
|
|
|
256
277
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
257
278
|
# ACTION CONTRACT — Step 2b: Spawn np-plan-checker (immediately after 2a)
|
|
@@ -265,17 +286,37 @@ for ITER in 1 2; do
|
|
|
265
286
|
# Agent MUST: read planner output (slice plans inside $milestone_dir),
|
|
266
287
|
# write YAML verdict to $milestone_dir/.tmp-verdict-$ITER.yaml. Orchestrator
|
|
267
288
|
# converts YAML → JSON at $VERDICT_JSON_PATH (next bash section).
|
|
289
|
+
# Off-host (ADR-0021): when np-plan-checker routes to an openai-compat provider,
|
|
290
|
+
# run it via spawn-offhost (below) INSTEAD of the Agent tool.
|
|
268
291
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
269
292
|
CHECKER_START=$(node .nubos-pilot/bin/np-tools.cjs metrics start-timestamp)
|
|
270
293
|
CHECKER_MODEL=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-plan-checker --profile frontier)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
294
|
+
CHECKER_KIND=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-plan-checker --json 2>/dev/null \
|
|
295
|
+
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).kind||"native")}catch{console.log("native")}})')
|
|
296
|
+
if [ "$CHECKER_KIND" = "openai-compat" ]; then
|
|
297
|
+
# np-plan-checker is NOT Rule-9-audited and writes ONLY the verdict YAML under
|
|
298
|
+
# $milestone_dir (inside the repo cwd), so it runs off-host with the default
|
|
299
|
+
# cwd: Read/Grep/Glob over the repo + Write confined to cwd. NO --allow-bash,
|
|
300
|
+
# NO worktree. It writes $milestone_dir/.tmp-verdict-$ITER.yaml exactly as the
|
|
301
|
+
# native checker does (the orchestrator's YAML→JSON step is unchanged).
|
|
302
|
+
# spawn-offhost records the metrics row itself.
|
|
303
|
+
CHECKER_PROMPT="${TMPDIR:-/tmp}/np-offhost-plan-checker-${milestone_id}-i${ITER}.md"
|
|
304
|
+
# … render the SAME prompt the ACTION CONTRACT above describes (milestone,
|
|
305
|
+
# milestone_dir, agent_skills) PLUS $LANG_DIRECTIVE, and MUST state the exact
|
|
306
|
+
# output path $milestone_dir/.tmp-verdict-$ITER.yaml, into "$CHECKER_PROMPT" …
|
|
307
|
+
node .nubos-pilot/bin/np-tools.cjs spawn-offhost \
|
|
308
|
+
--agent np-plan-checker --task-file "$CHECKER_PROMPT" \
|
|
309
|
+
--phase "$PHASE" --plan "${milestone_id}-plan" >/dev/null
|
|
310
|
+
else
|
|
311
|
+
# → execute the Agent call per ACTION CONTRACT above (native host spawn), then:
|
|
312
|
+
CHECKER_END=$(node .nubos-pilot/bin/np-tools.cjs metrics end-timestamp)
|
|
313
|
+
node .nubos-pilot/bin/np-tools.cjs metrics record \
|
|
314
|
+
--agent np-plan-checker --tier opus --resolved-model "$CHECKER_MODEL" \
|
|
315
|
+
--phase "$PHASE" --plan "${milestone_id}-plan" --task "${milestone_id}-planner-run" \
|
|
316
|
+
--started "$CHECKER_START" --ended "$CHECKER_END" \
|
|
317
|
+
--tokens-in "${TOKENS_IN:-0}" --tokens-out "${TOKENS_OUT:-0}" \
|
|
318
|
+
--retry-count 0 --status ok --runtime "$RUNTIME"
|
|
319
|
+
fi
|
|
279
320
|
|
|
280
321
|
VERDICT_JSON_PATH="$milestone_dir/.tmp-verdict-$ITER.json"
|
|
281
322
|
# (verdict JSON: {status: passed|issues_found, findings: [...] })
|
|
@@ -253,8 +253,36 @@ omit the `model:` parameter at spawn (Phase 8 D-22 inherit-pattern).
|
|
|
253
253
|
```bash
|
|
254
254
|
RESEARCHER_START=$(node .nubos-pilot/bin/np-tools.cjs metrics start-timestamp)
|
|
255
255
|
RESEARCHER_MODEL=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-researcher --profile balanced)
|
|
256
|
+
RESEARCHER_KIND=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-researcher --kind 2>/dev/null || echo native)
|
|
256
257
|
```
|
|
257
258
|
|
|
259
|
+
**Off-host (ADR-0021):** when `np-researcher` routes to an `openai-compat` provider, run the `$SWARM_K` spawns via `spawn-offhost` instead of the abstract host spawn. Two specifics for this audited agent:
|
|
260
|
+
|
|
261
|
+
- **Synthetic canonical task-id.** `np-researcher` is Rule-9-audited and `dispatchOffHost` requires a `M<NNN>-S<NNN>-T<NNNN>` id for the search-evidence ledger + audit. Research is milestone-level (no real slice/task), so mint the synthetic id `${MILESTONE_ID}-S000-T0000` (the `S000-T0000` suffix is the documented "milestone-level, no slice/task" convention). The injected native `knowledge-search` tool satisfies Rule 9; the orchestrator stamps one `loop-audit-tool-use` per spawn.
|
|
262
|
+
- **Offline only.** The off-host toolset has **no `WebFetch`/`context7`** — an off-host researcher can only do `$MODE == offline` (knowledge-search) research. If `$MODE == online`, keep `np-researcher` native (or accept offline-only research for that spawn). This is a capability bound, surfaced loudly, not a silent degrade.
|
|
263
|
+
|
|
264
|
+
Each spawn writes its own `$RESEARCH_DIR/spawn-<i>.md` (inside the repo cwd), so it runs write-enabled (NOT `--read-only`), no `--allow-bash`, no worktree.
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
if [ "$RESEARCHER_KIND" = "openai-compat" ]; then
|
|
268
|
+
OFFHOST_TASK_ID="${MILESTONE_ID}-S000-T0000"
|
|
269
|
+
i=0
|
|
270
|
+
while [ "$i" -lt "${SWARM_K:-3}" ]; do
|
|
271
|
+
R_PROMPT="${TMPDIR:-/tmp}/np-offhost-researcher-${MILESTONE_ID}-${i}.md"
|
|
272
|
+
# … render spawn-spec i (files_to_read + goal + requirements + seed_delta[i] +
|
|
273
|
+
# $SPAWN_SCHEMA + the EXACT output path $RESEARCH_DIR/spawn-${i}.md) PLUS
|
|
274
|
+
# $LANG_DIRECTIVE into "$R_PROMPT" …
|
|
275
|
+
R_OUT=$(node .nubos-pilot/bin/np-tools.cjs spawn-offhost \
|
|
276
|
+
--agent np-researcher --task-file "$R_PROMPT" --task-id "$OFFHOST_TASK_ID" --no-audit)
|
|
277
|
+
R_LOG=$(echo "$R_OUT" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.stringify((JSON.parse(s).toolLog||[]).map(t=>t.name)))}catch{console.log("[]")}})')
|
|
278
|
+
node .nubos-pilot/bin/np-tools.cjs loop-audit-tool-use "$OFFHOST_TASK_ID" --agent np-researcher --tool-use-log "$R_LOG"
|
|
279
|
+
i=$((i+1))
|
|
280
|
+
done
|
|
281
|
+
fi
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
When `$RESEARCHER_KIND = native`, use the abstract host spawn below (the Phase 8 runtime adapter binds it):
|
|
285
|
+
|
|
258
286
|
```text
|
|
259
287
|
Spawn agent=np-researcher tier=sonnet model=$RESEARCHER_MODEL mode=$MODE phase=$PHASE context=$CONTEXT_PATH output=$RESEARCH_PATH
|
|
260
288
|
```
|
|
@@ -351,6 +379,23 @@ Spawn agent=np-researcher-reconciler tier=sonnet model=$RECONCILER_MODEL phase=$
|
|
|
351
379
|
schema_prompt=$RECONCILER_SCHEMA
|
|
352
380
|
```
|
|
353
381
|
|
|
382
|
+
**Off-host (ADR-0021):** when `np-researcher-reconciler` routes to an `openai-compat` provider, run it via `spawn-offhost` instead of the host spawn. The reconciler is NOT Rule-9-audited and writes only the single `$RESEARCH_PATH` (`M<NNN>-RESEARCH.md`) artefact under `.nubos-pilot/` (inside the repo cwd — never live code), so it runs off-host with the default cwd (Read/Grep/Glob over the spawn outputs + Write confined to cwd), **no `--allow-bash`, no worktree**. It writes `$RESEARCH_PATH` exactly as the native reconciler; no emit-and-persist contract is needed. spawn-offhost self-records.
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
RECONCILER_KIND=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-researcher-reconciler --json 2>/dev/null \
|
|
386
|
+
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).kind||"native")}catch{console.log("native")}})')
|
|
387
|
+
if [ "$RECONCILER_KIND" = "openai-compat" ]; then
|
|
388
|
+
RECONCILER_PROMPT="${TMPDIR:-/tmp}/np-offhost-reconciler-${MILESTONE_ID}.md"
|
|
389
|
+
# … render the SAME reconciler input (spawn_paths + merge_path + merged_json +
|
|
390
|
+
# context_path + $RECONCILER_SCHEMA + the EXACT final_path=$RESEARCH_PATH) PLUS
|
|
391
|
+
# $LANG_DIRECTIVE into "$RECONCILER_PROMPT" …
|
|
392
|
+
node .nubos-pilot/bin/np-tools.cjs spawn-offhost \
|
|
393
|
+
--agent np-researcher-reconciler --task-file "$RECONCILER_PROMPT" \
|
|
394
|
+
--phase "$PHASE" --plan "$PLAN_ID" --task "$TASK_ID" >/dev/null
|
|
395
|
+
fi
|
|
396
|
+
# else → native host spawn per the block above.
|
|
397
|
+
```
|
|
398
|
+
|
|
354
399
|
The reconciler classifies each consensus decision's reasoning-trace as `identical | overlapping | orthogonal | unknown` (groupthink detection), picks each contested decision with documented reason, and writes `$RESEARCH_PATH` with `agreement_score` and `contested_count` in frontmatter.
|
|
355
400
|
|
|
356
401
|
```bash
|
|
@@ -106,9 +106,27 @@ is runtime-agnostic — pick whichever dispatch mechanism your host supports.
|
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
PROSE_FILE=$(mktemp -t np-prose-XXXXXX.json)
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
|
|
109
|
+
# Off-host (ADR-0021): when np-codebase-documenter routes to an openai-compat
|
|
110
|
+
# provider (agent_routing), run it via spawn-offhost INSTEAD of the native host
|
|
111
|
+
# dispatch below.
|
|
112
|
+
DOCUMENTER_KIND=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-codebase-documenter --kind 2>/dev/null || echo native)
|
|
113
|
+
if [ "$DOCUMENTER_KIND" = "openai-compat" ]; then
|
|
114
|
+
# np-codebase-documenter is NOT Rule-9-audited and writes ONLY
|
|
115
|
+
# .nubos-pilot/codebase/ artefacts (inside the repo cwd — NOT live code), so it
|
|
116
|
+
# runs off-host with the default cwd (repo root): Read/Grep/Glob over the repo +
|
|
117
|
+
# Write confined to cwd. NO --allow-bash and NO worktree (no live-code blast
|
|
118
|
+
# radius to isolate). The agent writes its module doc JSON itself, inside cwd.
|
|
119
|
+
DOC_PROMPT="${TMPDIR:-/tmp}/np-offhost-documenter-${MODULE_ID}.md"
|
|
120
|
+
# … render the SAME buildDocumenterPrompt(facts) prompt the native dispatch
|
|
121
|
+
# below describes PLUS $LANG_DIRECTIVE into "$DOC_PROMPT" …
|
|
122
|
+
node .nubos-pilot/bin/np-tools.cjs spawn-offhost \
|
|
123
|
+
--agent np-codebase-documenter --task-file "$DOC_PROMPT" \
|
|
124
|
+
--phase scan >/dev/null
|
|
125
|
+
else
|
|
126
|
+
# Host dispatches agent with buildDocumenterPrompt(facts) and writes JSON
|
|
127
|
+
# to $PROSE_FILE. Validate JSON before proceeding.
|
|
128
|
+
python -c 'import json,sys; json.load(open(sys.argv[1]))' "$PROSE_FILE"
|
|
129
|
+
fi
|
|
112
130
|
```
|
|
113
131
|
|
|
114
132
|
Batch pacing: the user opted into batches during Step 1. Between batches,
|
|
@@ -129,6 +129,8 @@ The auditor reads `REQUIREMENTS.md`, filters to the milestone's declared require
|
|
|
129
129
|
```bash
|
|
130
130
|
START=$(node .nubos-pilot/bin/np-tools.cjs metrics start-timestamp)
|
|
131
131
|
MODEL=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-nyquist-auditor --profile frontier)
|
|
132
|
+
AUDITOR_KIND=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-nyquist-auditor --json 2>/dev/null \
|
|
133
|
+
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).kind||"native")}catch{console.log("native")}})')
|
|
132
134
|
|
|
133
135
|
# Build the read list from the init payload:
|
|
134
136
|
SLICE_PLANS=$(find "$MILESTONE_DIR/slices" -maxdepth 2 -name 'S*-PLAN.md' 2>/dev/null)
|
|
@@ -136,19 +138,34 @@ SLICE_SUMMARIES=$(find "$MILESTONE_DIR/slices" -maxdepth 2 -name 'S*-SUMMARY.md'
|
|
|
136
138
|
TASK_PLANS=$(find "$MILESTONE_DIR/slices" -path '*/tasks/*/T*-PLAN.md' 2>/dev/null)
|
|
137
139
|
TASK_SUMMARIES=$(find "$MILESTONE_DIR/slices" -path '*/tasks/*/T*-SUMMARY.md' 2>/dev/null)
|
|
138
140
|
|
|
139
|
-
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
141
|
+
if [ "$AUDITOR_KIND" = "openai-compat" ]; then
|
|
142
|
+
# Off-host (ADR-0021): np-nyquist-auditor is NOT Rule-9-audited and writes ONLY
|
|
143
|
+
# $VALIDATION_PATH (M<NNN>-VALIDATION.md) under .nubos-pilot/ (inside the repo
|
|
144
|
+
# cwd — NOT live code), so it runs off-host with the default cwd: Read/Grep/Glob
|
|
145
|
+
# over the repo + Write confined to cwd. NO --allow-bash, NO worktree. It writes
|
|
146
|
+
# the file from templates/VALIDATION.md exactly as the native auditor does (the
|
|
147
|
+
# orchestrator's output-lint check is unchanged). spawn-offhost self-records.
|
|
148
|
+
AUDITOR_PROMPT="${TMPDIR:-/tmp}/np-offhost-nyquist-${MILESTONE_ID}.md"
|
|
149
|
+
# … render the SAME auditor prompt (read list above + $VALIDATION_SCHEMA +
|
|
150
|
+
# template_path + requirements_path + the EXACT output path $VALIDATION_PATH)
|
|
151
|
+
# PLUS $LANG_DIRECTIVE into "$AUDITOR_PROMPT" …
|
|
152
|
+
node .nubos-pilot/bin/np-tools.cjs spawn-offhost \
|
|
153
|
+
--agent np-nyquist-auditor --task-file "$AUDITOR_PROMPT" \
|
|
154
|
+
--phase "$PHASE" --plan "$PLAN_ID" --task "$TASK_ID" >/dev/null
|
|
155
|
+
else
|
|
156
|
+
# Spawn agent=np-nyquist-auditor model=$MODEL (native host spawn)
|
|
157
|
+
# input: slice_plans, slice_summaries, task_plans, task_summaries, validation_path,
|
|
158
|
+
# template_path, requirements_path, milestone_dir, milestone, milestone_id
|
|
159
|
+
# output: $VALIDATION_PATH with per-requirement Nyquist scoring
|
|
160
|
+
# (COVERED / UNDER_SAMPLED / UNCOVERED), using templates/VALIDATION.md as skeleton.
|
|
161
|
+
END=$(node .nubos-pilot/bin/np-tools.cjs metrics end-timestamp)
|
|
162
|
+
node .nubos-pilot/bin/np-tools.cjs metrics record \
|
|
163
|
+
--agent np-nyquist-auditor --tier haiku --resolved-model "$MODEL" \
|
|
164
|
+
--phase "$PHASE" --plan "$PLAN_ID" --task "$TASK_ID" \
|
|
165
|
+
--started "$START" --ended "$END" \
|
|
166
|
+
--tokens-in "${TOKENS_IN:-0}" --tokens-out "${TOKENS_OUT:-0}" \
|
|
167
|
+
--retry-count 0 --status ok --runtime "$RUNTIME"
|
|
168
|
+
fi
|
|
152
169
|
```
|
|
153
170
|
|
|
154
171
|
## Validation Gate
|
package/workflows/verify-work.md
CHANGED
|
@@ -81,6 +81,23 @@ Spawn `agents/np-verifier.md` (tier: sonnet, READ-ONLY tools) with:
|
|
|
81
81
|
|
|
82
82
|
The agent emits a structured verdict per SC: Pass | Fail | Needs-User-Confirm | Defer (never invents a SC, never edits source).
|
|
83
83
|
|
|
84
|
+
**Off-host (ADR-0021):** when `np-verifier` routes to an `openai-compat` provider, run it via `spawn-offhost --read-only` instead of the host spawn. The verifier never edits source and **emits** its per-SC verdict as the final message — exactly the contract the native path already consumes — so it needs no worktree, no Bash, no Write. Pass `--output-schema verification` for the dispatch-level lint hook, then feed the returned `.content` (the emitted verdict) into the SAME Pass-1 `emit-draft` / Pass-2 `record-sc` persistence below; `VERIFICATION.md` is produced exactly as for the native agent.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
VERIFIER_KIND=$(node .nubos-pilot/bin/np-tools.cjs resolve-model np-verifier --json 2>/dev/null \
|
|
88
|
+
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).kind||"native")}catch{console.log("native")}})')
|
|
89
|
+
if [ "$VERIFIER_KIND" = "openai-compat" ]; then
|
|
90
|
+
VERIFIER_PROMPT="${TMPDIR:-/tmp}/np-offhost-verifier-${MILESTONE_ID}.md"
|
|
91
|
+
# … render the SAME spawn prompt (files_to_read + success_criteria +
|
|
92
|
+
# $VERIFICATION_SCHEMA) PLUS $LANG_DIRECTIVE into "$VERIFIER_PROMPT" …
|
|
93
|
+
VERIFIER_OUT=$(node .nubos-pilot/bin/np-tools.cjs spawn-offhost \
|
|
94
|
+
--agent np-verifier --task-file "$VERIFIER_PROMPT" --read-only \
|
|
95
|
+
--output-schema verification --phase "$PHASE")
|
|
96
|
+
# The emitted verdict is `.content` of $VERIFIER_OUT — it drives emit-draft / record-sc below.
|
|
97
|
+
fi
|
|
98
|
+
# else → native host spawn per the block above.
|
|
99
|
+
```
|
|
100
|
+
|
|
84
101
|
Persist the deterministic draft:
|
|
85
102
|
|
|
86
103
|
```bash
|