README: install, configure (.env + workspaces/), the workflow, run inside Pi, the three-level test commands, layout, references. docs/workflow-editing.md: how to edit workflow.yaml (add/reorder/merge/skip phases, advance kinds, prerequisite predicates, decision_min_phase derivation, artifacts_out + teardown) -- referencing spec §5.3. Emphasizes no mirrored constants (the F2 point). docs/testing.md: the honest L0/L1/L2 split in plain language -- what each covers and does NOT. States the headline plainly: the skill->LLM->gate loop has NO automated regression coverage (L2 only, pre-release). Documents the fake-Pi follow-up as the gap-closer. Security note on keys (.env gitignored, never logged, rotate leaked keys).
3.3 KiB
Editing the workflow
harness/workflow.yaml is the single source of workflow truth (spec F2, §5.3).
phase.py, the gate (nsp-gate.js), and the skill all read from it. There are no
mirrored constants in JS or Python — that was the ChironeWp3 drift bug
(PHASE_NAMES truncated to 7 entries in JS, F8/datamart silently dropped). Editing
this one file is the only place the workflow changes.
Structure
schema_version: 1
phases:
- id: F3 # stable id (referenced by the gate)
name: riscrittura # human label (nsp phase show / gate UI)
advance: kind:phase # how the phase advances (see below)
prerequisites: # what must hold before advancing (gate checks these)
- decision_exists: question_rewritten
artifacts_out: [question.md] # files produced; used by teardown on rollback (D15)
decision_min_phase: auto and max_phase: auto mean these are derived from the
phase list (don't hard-code them).
advance kinds
| kind | meaning |
|---|---|
kind:phase |
advances on any phase-approved decision for this phase |
auto_if_empty |
auto-advances if no decisions were made (F2 memoria) |
auto_if_empty_or_skipped |
auto-advances if empty OR a phase_skipped decision exists |
reviewer_decide |
requires an explicit reviewer decision (F4, F8) |
prerequisites predicates
A phase is "ready to advance" when ALL its prerequisites hold. Predicates:
decision_exists: <type>— a decision of that type exists (effective view).decision_subject_exists: [<type>, <subject>]— e.g. aphase_skippedforphase:6.file_validates: [<artifact>, <model>]— e.g.schema_linking.jsonparses as aSchemaLinking.all_ctes_approved: true— all CTE blocks approved (F6).any: [...]/all: [...]— combine predicates.
A decision type's min phase is derived: the earliest phase whose prerequisites
reference it. So adding a prerequisite like decision_exists: my_new_decision to F5
automatically makes my_new_decision valid from phase 5.
Common edits
Add a phase
Append a phase to phases:. max_phase becomes the new count automatically. Give it
an id, name, advance, and (optionally) prerequisites / artifacts_out.
Reorder / merge / skip phases
Reorder the list; max_phase and decision_min_phase recompute. To make a phase
skippable, set advance: auto_if_empty_or_skipped and let the gate emit a
phase_skipped decision (predicate decision_subject_exists: [phase_skipped, "phase:N"]).
Add an artifact
Add it to a phase's artifacts_out. On rollback (teardown_to_phase), artifacts
produced by phases after the target are deleted; the target phase's artifacts are
preserved. This is the D15 fix for the orphaned-CTE-blocks-finalize bug.
After editing
nsp phase meta --json # confirm the new shape (max_phase, phases, artifacts_out)
pytest # L1 coherence smoke re-derives from the new workflow.yaml
No JS or Python constants to update — that's the point.