devrites 3.0.2 → 3.0.4

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 (60) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +1 -1
  3. package/docs/adr/0001-go-engine-as-control-plane.md +43 -0
  4. package/docs/adr/0002-dual-host-harness.md +34 -0
  5. package/docs/adr/0003-gate-model-hitl-pause.md +38 -0
  6. package/docs/adr/0004-state-schema-phases-sections.md +47 -0
  7. package/docs/adr/0005-hooks-as-engine-subcommands.md +39 -0
  8. package/docs/adr/0006-clock-seam-and-engine-ci-gates.md +50 -0
  9. package/docs/adr/0007-canonical-live-workspace-filenames.md +36 -0
  10. package/docs/adr/0008-sanctioned-engine-network-boundary.md +33 -0
  11. package/docs/adr/README.md +58 -0
  12. package/docs/command-map.md +1 -1
  13. package/docs/engine/state-schema.md +18 -14
  14. package/docs/flow.md +1 -1
  15. package/docs/research/go-authoritative-workflow-schema-and-quality-2026-07-20.md +212 -0
  16. package/engine/hooks_workspace.go +7 -7
  17. package/engine/internal/gate/gate.go +4 -4
  18. package/engine/internal/gate/gate_test.go +2 -2
  19. package/engine/internal/lib/cursor_compat_test.go +29 -0
  20. package/engine/internal/lib/preamble.go +1 -1
  21. package/engine/internal/lib/preamble_questions_test.go +10 -0
  22. package/engine/internal/lib/progress.go +59 -20
  23. package/engine/internal/lib/resolve.go +15 -6
  24. package/engine/internal/lib/resolve_remediation_test.go +9 -0
  25. package/engine/internal/lib/tickafk.go +2 -2
  26. package/engine/internal/migrate/migrate.go +18 -44
  27. package/engine/internal/migrate/migrate_test.go +33 -5
  28. package/engine/internal/state/cmd/workflowmanifest/main.go +54 -0
  29. package/engine/internal/state/cursor.go +42 -7
  30. package/engine/internal/state/feature.go +29 -41
  31. package/engine/internal/state/schema.go +155 -28
  32. package/engine/internal/state/snapshot.go +49 -3
  33. package/engine/internal/state/state_test.go +77 -6
  34. package/engine/internal/state/workflow_manifest.json +310 -0
  35. package/engine/internal/workflow/commands.go +0 -36
  36. package/engine/internal/workflow/commands_test.go +0 -17
  37. package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +1 -1
  38. package/engine/testdata/golden/TestParityProgress/arg=done.golden +1 -1
  39. package/engine/testdata/golden/TestParityProgress/arg=mid.golden +1 -1
  40. package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +1 -1
  41. package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +1 -1
  42. package/engine/testdata/golden/TestParityProgress/arg=plan.golden +1 -1
  43. package/engine/testdata/golden/TestParityProgress/arg=seal.golden +1 -1
  44. package/engine/tests/adr_0004_required_by_phase_test.go +1 -18
  45. package/engine/tests/budget_test.go +2 -2
  46. package/engine/tests/lib_parity_test.go +1 -1
  47. package/engine/tests/meta_test.go +3 -3
  48. package/engine/tests/migrate_cli_test.go +24 -22
  49. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -3
  50. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -3
  51. package/pack/generated/codex/AGENTS.md +1 -1
  52. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -3
  53. package/package.json +2 -1
  54. package/scripts/codex-generate.sh +1 -1
  55. package/scripts/grade-feature.sh +5 -3
  56. package/scripts/run-outcome-evals.sh +21 -0
  57. package/scripts/validate-workflow-security.py +3 -8
  58. package/scripts/validate-workspace-schema.py +9 -73
  59. package/scripts/validate.sh +10 -0
  60. package/scripts/workflow_schema.py +69 -0
@@ -29,6 +29,56 @@ func TestRequiredSectionsIsPhaseRelativeAndOrdered(t *testing.T) {
29
29
  }
30
30
  }
31
31
 
32
+ func TestLifecycleRegistryOwnsOrderAndResumeCommands(t *testing.T) {
33
+ phases := LifecyclePhases()
34
+ if len(phases) == 0 || phases[0] != PhaseFrame || phases[len(phases)-1] != PhaseDone {
35
+ t.Fatalf("LifecyclePhases()=%v, want frame...done", phases)
36
+ }
37
+ if got := ResumeVerb(PhasePlan); got != "define" {
38
+ t.Fatalf("ResumeVerb(plan)=%q, want define", got)
39
+ }
40
+ if got := ResumeVerb(PhaseDone); got != "" {
41
+ t.Fatalf("ResumeVerb(done)=%q, want empty", got)
42
+ }
43
+
44
+ phases[0] = PhaseDone
45
+ if got := LifecyclePhases()[0]; got != PhaseFrame {
46
+ t.Fatalf("LifecyclePhases exposed mutable registry: first=%q", got)
47
+ }
48
+ }
49
+
50
+ func TestLifecycleRegistryInvariants(t *testing.T) {
51
+ phaseNames := map[Phase]bool{}
52
+ aliases := map[string]Phase{}
53
+ for i, definition := range phaseDefinitions {
54
+ if definition.phase == "" || phaseNames[definition.phase] {
55
+ t.Fatalf("phase definition %d has empty or duplicate ID %q", i, definition.phase)
56
+ }
57
+ phaseNames[definition.phase] = true
58
+ if len(definition.workspaceRequired) == 0 {
59
+ t.Fatalf("phase %q has no workspace requirements", definition.phase)
60
+ }
61
+ for _, alias := range definition.aliases {
62
+ if alias == "" || aliases[alias] != "" || KnownPhase(Phase(alias)) {
63
+ t.Fatalf("phase %q has empty or duplicate alias %q", definition.phase, alias)
64
+ }
65
+ aliases[alias] = definition.phase
66
+ }
67
+ for _, section := range definition.required {
68
+ known := false
69
+ for _, canonical := range Sections {
70
+ known = known || section == canonical
71
+ }
72
+ if !known {
73
+ t.Fatalf("phase %q requires unknown section %q", definition.phase, section)
74
+ }
75
+ }
76
+ if definition.shippable && !definition.proofRequired {
77
+ t.Fatalf("shippable phase %q does not require proof", definition.phase)
78
+ }
79
+ }
80
+ }
81
+
32
82
  func TestStatusFixtureBuildIncomplete(t *testing.T) {
33
83
  rep, err := Status(fixtureRoot, "auth-tokens")
34
84
  if err != nil {
@@ -88,9 +138,9 @@ func writeWorkSection(t *testing.T, root, slug, name, body string) {
88
138
  }
89
139
  }
90
140
 
91
- // A live workspace the pack creates has no feature.md manifest: the phase lives in
92
- // the state.md ledger and the proof/status sections are satisfied by their aliases
93
- // (evidence.md / state.md). The engine must load, list, and report it anyway.
141
+ // A live workspace map need not carry frontmatter: the phase lives in
142
+ // the canonical state.md ledger and the proof/status concepts are satisfied by
143
+ // evidence.md/state.md. The engine must load, list, and report it anyway.
94
144
  func TestLoadFeatureFromLedgerAndAliases(t *testing.T) {
95
145
  root := filepath.Join(t.TempDir(), ".devrites")
96
146
  writeSection(t, root, "live", "state.md", "- Phase: prove\n- Status: running\n")
@@ -98,7 +148,7 @@ func TestLoadFeatureFromLedgerAndAliases(t *testing.T) {
98
148
  writeSection(t, root, "live", "plan.md", "# Plan\n\nApproach.\n")
99
149
  writeSection(t, root, "live", "decisions.md", "# Decisions\n\nChose X.\n")
100
150
  writeSection(t, root, "live", "tasks.md", "# Tasks\n\n- [x] slice 1\n")
101
- writeSection(t, root, "live", "evidence.md", "# Evidence\n\nTests pass.\n") // alias for proof
151
+ writeSection(t, root, "live", "evidence.md", "# Evidence\n\nTests pass.\n")
102
152
 
103
153
  rep, err := Status(root, "live")
104
154
  if err != nil {
@@ -108,10 +158,10 @@ func TestLoadFeatureFromLedgerAndAliases(t *testing.T) {
108
158
  t.Errorf("phase = %q, want prove (from the state.md ledger)", rep.Phase)
109
159
  }
110
160
  if !rep.Present[SectionProof] {
111
- t.Error("proof section should be present via its evidence.md alias")
161
+ t.Error("proof section should be present via canonical evidence.md")
112
162
  }
113
163
  if !rep.Present[SectionStatus] {
114
- t.Error("status section should be present via its state.md alias")
164
+ t.Error("status section should be present via canonical state.md")
115
165
  }
116
166
  if !rep.Complete() {
117
167
  t.Errorf("prove-phase feature should be complete, missing: %v", rep.Missing)
@@ -172,6 +222,27 @@ func TestSnapshotUsesCanonicalNextActionAndWarnsWhenRequiredProofMissing(t *test
172
222
  }
173
223
  }
174
224
 
225
+ func TestSnapshotReadsCanonicalActiveSliceAndCountsQuestionsByRecord(t *testing.T) {
226
+ workDir := t.TempDir()
227
+ if err := os.WriteFile(filepath.Join(workDir, "state.md"), []byte("| phase | build |\n| active_slice | SLICE-002 |\n"), 0o644); err != nil {
228
+ t.Fatal(err)
229
+ }
230
+ if err := os.WriteFile(filepath.Join(workDir, "tasks.md"), []byte("## SLICE-001 First\n\n## SLICE-002 Second\n"), 0o644); err != nil {
231
+ t.Fatal(err)
232
+ }
233
+ if err := os.WriteFile(filepath.Join(workDir, "questions.md"), []byte("## Q-001\nstatus: open\ngate: blocking\n\n## Q-002\nstatus: answered\ngate: blocking\n"), 0o644); err != nil {
234
+ t.Fatal(err)
235
+ }
236
+
237
+ slice := currentSlice(workDir)
238
+ if slice == nil || slice.Name != "SLICE-002" || slice.Index != 2 || slice.Total != 2 {
239
+ t.Fatalf("currentSlice=%+v, want canonical SLICE-002 at 2/2", slice)
240
+ }
241
+ if drift := driftSummary(workDir); drift.Status != "open" || drift.Open != 1 {
242
+ t.Fatalf("driftSummary=%+v, want one open question record", drift)
243
+ }
244
+ }
245
+
175
246
  func TestWorkLayoutIsCanonicalAndFeaturesIsAlias(t *testing.T) {
176
247
  root := filepath.Join(t.TempDir(), ".devrites")
177
248
  writeWorkSection(t, root, "live", "state.md", "- Phase: build\n")
@@ -0,0 +1,310 @@
1
+ {
2
+ "generatedBy": "go generate ./internal/state; DO NOT EDIT",
3
+ "schemaVersion": 1,
4
+ "phases": [
5
+ {
6
+ "id": "frame",
7
+ "resumeVerb": "frame",
8
+ "workspaceRequired": [
9
+ "state.md"
10
+ ]
11
+ },
12
+ {
13
+ "id": "spec",
14
+ "resumeVerb": "spec",
15
+ "aliases": [
16
+ "specced",
17
+ "specifying"
18
+ ],
19
+ "workspaceRequired": [
20
+ "brief.md",
21
+ "spec.md",
22
+ "state.md",
23
+ "decisions.md",
24
+ "assumptions.md",
25
+ "questions.md"
26
+ ]
27
+ },
28
+ {
29
+ "id": "temper",
30
+ "resumeVerb": "temper",
31
+ "aliases": [
32
+ "tempered",
33
+ "tempering"
34
+ ],
35
+ "workspaceRequired": [
36
+ "brief.md",
37
+ "spec.md",
38
+ "state.md",
39
+ "decisions.md",
40
+ "assumptions.md",
41
+ "questions.md"
42
+ ]
43
+ },
44
+ {
45
+ "id": "define",
46
+ "resumeVerb": "define",
47
+ "aliases": [
48
+ "defined",
49
+ "defining"
50
+ ],
51
+ "workspaceRequired": [
52
+ "brief.md",
53
+ "spec.md",
54
+ "architecture.md",
55
+ "plan.md",
56
+ "tasks.md",
57
+ "traceability.md",
58
+ "state.md",
59
+ "decisions.md",
60
+ "assumptions.md",
61
+ "questions.md"
62
+ ],
63
+ "blocksOpenQuestions": true
64
+ },
65
+ {
66
+ "id": "plan",
67
+ "resumeVerb": "define",
68
+ "aliases": [
69
+ "planned",
70
+ "planning"
71
+ ],
72
+ "workspaceRequired": [
73
+ "brief.md",
74
+ "spec.md",
75
+ "architecture.md",
76
+ "plan.md",
77
+ "tasks.md",
78
+ "traceability.md",
79
+ "state.md",
80
+ "decisions.md",
81
+ "assumptions.md",
82
+ "questions.md"
83
+ ],
84
+ "blocksOpenQuestions": true
85
+ },
86
+ {
87
+ "id": "vet",
88
+ "resumeVerb": "vet",
89
+ "aliases": [
90
+ "vetted",
91
+ "vetting"
92
+ ],
93
+ "workspaceRequired": [
94
+ "brief.md",
95
+ "spec.md",
96
+ "architecture.md",
97
+ "plan.md",
98
+ "tasks.md",
99
+ "traceability.md",
100
+ "state.md",
101
+ "decisions.md",
102
+ "assumptions.md",
103
+ "questions.md"
104
+ ],
105
+ "blocksOpenQuestions": true
106
+ },
107
+ {
108
+ "id": "build",
109
+ "resumeVerb": "build",
110
+ "aliases": [
111
+ "building",
112
+ "wip",
113
+ "in",
114
+ "in-progress"
115
+ ],
116
+ "workspaceRequired": [
117
+ "brief.md",
118
+ "spec.md",
119
+ "architecture.md",
120
+ "plan.md",
121
+ "tasks.md",
122
+ "traceability.md",
123
+ "state.md",
124
+ "decisions.md",
125
+ "assumptions.md",
126
+ "questions.md"
127
+ ],
128
+ "blocksOpenQuestions": true
129
+ },
130
+ {
131
+ "id": "converge",
132
+ "resumeVerb": "converge",
133
+ "aliases": [
134
+ "converged",
135
+ "converging"
136
+ ],
137
+ "workspaceRequired": [
138
+ "brief.md",
139
+ "spec.md",
140
+ "architecture.md",
141
+ "plan.md",
142
+ "tasks.md",
143
+ "traceability.md",
144
+ "state.md",
145
+ "decisions.md",
146
+ "assumptions.md",
147
+ "questions.md"
148
+ ],
149
+ "blocksOpenQuestions": true
150
+ },
151
+ {
152
+ "id": "prove",
153
+ "resumeVerb": "prove",
154
+ "aliases": [
155
+ "proving",
156
+ "proven",
157
+ "testing"
158
+ ],
159
+ "workspaceRequired": [
160
+ "brief.md",
161
+ "spec.md",
162
+ "architecture.md",
163
+ "plan.md",
164
+ "tasks.md",
165
+ "traceability.md",
166
+ "state.md",
167
+ "decisions.md",
168
+ "assumptions.md",
169
+ "questions.md",
170
+ "evidence.md",
171
+ "touched-files.md"
172
+ ],
173
+ "proofRequired": true,
174
+ "blocksOpenQuestions": true
175
+ },
176
+ {
177
+ "id": "polish",
178
+ "resumeVerb": "polish",
179
+ "aliases": [
180
+ "polished",
181
+ "polishing"
182
+ ],
183
+ "workspaceRequired": [
184
+ "brief.md",
185
+ "spec.md",
186
+ "architecture.md",
187
+ "plan.md",
188
+ "tasks.md",
189
+ "traceability.md",
190
+ "state.md",
191
+ "decisions.md",
192
+ "assumptions.md",
193
+ "questions.md",
194
+ "evidence.md",
195
+ "touched-files.md"
196
+ ],
197
+ "proofRequired": true,
198
+ "blocksOpenQuestions": true
199
+ },
200
+ {
201
+ "id": "review",
202
+ "resumeVerb": "review",
203
+ "aliases": [
204
+ "reviewed",
205
+ "reviewing"
206
+ ],
207
+ "workspaceRequired": [
208
+ "brief.md",
209
+ "spec.md",
210
+ "architecture.md",
211
+ "plan.md",
212
+ "tasks.md",
213
+ "traceability.md",
214
+ "state.md",
215
+ "decisions.md",
216
+ "assumptions.md",
217
+ "questions.md",
218
+ "evidence.md",
219
+ "touched-files.md"
220
+ ],
221
+ "proofRequired": true,
222
+ "blocksOpenQuestions": true
223
+ },
224
+ {
225
+ "id": "seal",
226
+ "resumeVerb": "seal",
227
+ "aliases": [
228
+ "sealed",
229
+ "sealing"
230
+ ],
231
+ "workspaceRequired": [
232
+ "brief.md",
233
+ "spec.md",
234
+ "architecture.md",
235
+ "plan.md",
236
+ "tasks.md",
237
+ "traceability.md",
238
+ "state.md",
239
+ "decisions.md",
240
+ "assumptions.md",
241
+ "questions.md",
242
+ "evidence.md",
243
+ "touched-files.md"
244
+ ],
245
+ "proofRequired": true,
246
+ "blocksOpenQuestions": true,
247
+ "shippable": true
248
+ },
249
+ {
250
+ "id": "ship",
251
+ "resumeVerb": "ship",
252
+ "aliases": [
253
+ "shipped",
254
+ "shipping"
255
+ ],
256
+ "workspaceRequired": [
257
+ "brief.md",
258
+ "spec.md",
259
+ "architecture.md",
260
+ "plan.md",
261
+ "tasks.md",
262
+ "traceability.md",
263
+ "state.md",
264
+ "decisions.md",
265
+ "assumptions.md",
266
+ "questions.md",
267
+ "evidence.md",
268
+ "touched-files.md"
269
+ ],
270
+ "proofRequired": true,
271
+ "blocksOpenQuestions": true,
272
+ "shippable": true
273
+ },
274
+ {
275
+ "id": "done",
276
+ "aliases": [
277
+ "closed",
278
+ "complete",
279
+ "completed"
280
+ ],
281
+ "workspaceRequired": [
282
+ "brief.md",
283
+ "spec.md",
284
+ "architecture.md",
285
+ "plan.md",
286
+ "tasks.md",
287
+ "traceability.md",
288
+ "state.md",
289
+ "decisions.md",
290
+ "assumptions.md",
291
+ "questions.md",
292
+ "evidence.md",
293
+ "touched-files.md"
294
+ ],
295
+ "proofRequired": true,
296
+ "blocksOpenQuestions": true,
297
+ "shippable": true
298
+ }
299
+ ],
300
+ "cursorKeyAliases": [
301
+ {
302
+ "alias": "nextstep",
303
+ "canonical": "next_action"
304
+ },
305
+ {
306
+ "alias": "qid",
307
+ "canonical": "question_id"
308
+ }
309
+ ]
310
+ }
@@ -12,22 +12,6 @@ type Command struct {
12
12
  Codex string
13
13
  }
14
14
 
15
- var phaseToVerb = map[string]string{
16
- "frame": "frame",
17
- "spec": "spec",
18
- "temper": "temper",
19
- "define": "define",
20
- "plan": "define", // plan is the artifact state produced by rite-define.
21
- "vet": "vet",
22
- "build": "build",
23
- "converge": "converge",
24
- "prove": "prove",
25
- "polish": "polish",
26
- "review": "review",
27
- "seal": "seal",
28
- "ship": "ship",
29
- }
30
-
31
15
  // ForVerb returns the Claude and Codex forms for a public rite verb. The empty
32
16
  // verb returns an empty command so callers can omit suggestions safely.
33
17
  func ForVerb(verb string) Command {
@@ -52,12 +36,6 @@ func ForAction(action string) Command {
52
36
  return Command{}
53
37
  }
54
38
 
55
- // ForPhase returns the next public command for a workflow phase.
56
- func ForPhase(phase string) Command {
57
- verb := phaseToVerb[strings.TrimSpace(strings.ToLower(phase))]
58
- return ForVerb(verb)
59
- }
60
-
61
39
  // Both renders a command in host-neutral prose for engine stderr/stdout messages.
62
40
  func (c Command) Both() string {
63
41
  if c.Verb == "" {
@@ -65,17 +43,3 @@ func (c Command) Both() string {
65
43
  }
66
44
  return c.Claude + " (Claude) / " + c.Codex + " (Codex)"
67
45
  }
68
-
69
- // ClaudeOrCodex returns the command for a known harness. Empty/unknown harnesses
70
- // deliberately get the host-neutral rendering so CLI output remains safe when the
71
- // engine is run outside a hook.
72
- func (c Command) ClaudeOrCodex(harness string) string {
73
- switch strings.ToLower(strings.TrimSpace(harness)) {
74
- case "claude", "claude-code", "claudecode":
75
- return c.Claude
76
- case "codex":
77
- return c.Codex
78
- default:
79
- return c.Both()
80
- }
81
- }
@@ -16,20 +16,3 @@ func TestForActionExtractsCanonicalInvocation(t *testing.T) {
16
16
  }
17
17
  }
18
18
  }
19
-
20
- func TestForPhaseCoversCurrentLifecycle(t *testing.T) {
21
- for _, phase := range []string{
22
- "frame", "spec", "temper", "define", "vet", "build", "converge",
23
- "prove", "polish", "review", "seal", "ship",
24
- } {
25
- if got := ForPhase(phase).Verb; got != phase {
26
- t.Fatalf("ForPhase(%q).Verb=%q, want %q", phase, got, phase)
27
- }
28
- }
29
- if got := ForPhase("plan").Verb; got != "define" {
30
- t.Fatalf("ForPhase(plan).Verb=%q, want define compatibility route", got)
31
- }
32
- if got := ForPhase("done").Verb; got != "" {
33
- t.Fatalf("ForPhase(done).Verb=%q, want empty", got)
34
- }
35
- }
@@ -1,4 +1,4 @@
1
1
  exit 0
2
2
  ── rite-build ─────────
3
3
  Slices 2/2 ██████████ ✅ ALL BUILT
4
- Flow spec ✓ define ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
4
+ Flow spec ✓ define ✓ plan ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,3 +1,3 @@
1
1
  exit 0
2
2
  ── rite-done ─────────
3
- Flow spec ✓ define ✓ build ✓ prove ✓ polish ✓ review ✓ seal ✓ ship ✓
3
+ Flow spec ✓ define ✓ plan ✓ build ✓ prove ✓ polish ✓ review ✓ seal ✓ ship ✓
@@ -1,4 +1,4 @@
1
1
  exit 0
2
2
  ── rite-build ─────────
3
3
  Slice 2/3 ███████░░░ detail ✓
4
- Flow spec ✓ temper ✓ define ✓ vet ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
4
+ Flow spec ✓ temper ✓ define ✓ plan ✓ vet ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,3 +1,3 @@
1
1
  exit 0
2
2
  ── rite-spec ─────────
3
- Flow spec ◉ define ○ build ○ prove ○ polish ○ review ○ seal ○ ship ○
3
+ Flow spec ◉ define ○ plan ○ build ○ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,3 +1,3 @@
1
1
  exit 0
2
2
  ── rite-spec ─────────
3
- Flow spec ◉ define ○ build ○ prove ○ polish ○ review ○ seal ○ ship ○
3
+ Flow spec ◉ define ○ plan ○ build ○ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,4 +1,4 @@
1
1
  exit 0
2
2
  ── rite-plan ─────────
3
3
  Slice 2/3 ███████░░░ detail ✓
4
- Flow spec ✓ define ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
4
+ Flow spec ✓ define ✓ planbuild ○ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,3 +1,3 @@
1
1
  exit 0
2
2
  ── rite-seal ─────────
3
- Flow spec ✓ define ✓ build ✓ prove ✓ polish ✓ review ✓ seal ◉ ship ○
3
+ Flow spec ✓ define ✓ plan ✓ build ✓ prove ✓ polish ✓ review ✓ seal ◉ ship ○
@@ -12,25 +12,8 @@ import (
12
12
  "github.com/devrites/devrites/internal/state"
13
13
  )
14
14
 
15
- // phaseArc is the canonical order the lifecycle advances through (ADR-0004).
16
- var phaseArc = []state.Phase{
17
- state.PhaseFrame,
18
- state.PhaseSpec,
19
- state.PhaseTemper,
20
- state.PhaseDefine,
21
- state.PhasePlan,
22
- state.PhaseVet,
23
- state.PhaseBuild,
24
- state.PhaseConverge,
25
- state.PhaseProve,
26
- state.PhasePolish,
27
- state.PhaseReview,
28
- state.PhaseSeal,
29
- state.PhaseShip,
30
- state.PhaseDone,
31
- }
32
-
33
15
  func TestADR0004RequiredSectionsAreAdditiveDownTheArc(t *testing.T) {
16
+ phaseArc := state.LifecyclePhases()
34
17
  for _, p := range phaseArc {
35
18
  if !state.KnownPhase(p) {
36
19
  t.Fatalf("phase %q in the arc is not a known phase", p)
@@ -7,8 +7,8 @@ import (
7
7
 
8
8
  // TestStatusLiveWorkspace is the P2 acceptance check: `devrites-engine status <slug>`
9
9
  // must report a canonical work/<slug> feature the live pack created without a
10
- // feature.md manifest — phase from the state.md ledger, proof/status via their
11
- // evidence.md/state.md aliases. Before the schema unification this returned
10
+ // workspace-map frontmatter — phase from the canonical state.md ledger and proof/status
11
+ // completeness from canonical evidence.md/state.md files. Before schema unification this returned
12
12
  // "feature not found".
13
13
  func TestStatusLiveWorkspace(t *testing.T) {
14
14
  work := t.TempDir()
@@ -795,7 +795,7 @@ func TestParityPreamble(t *testing.T) {
795
795
  //
796
796
  // Golden: the header rule width, the slice meter (round-half math, ✅ ALL BUILT
797
797
  // vs the last-built tail), and the flow ribbon (conditional temper/vet, the
798
- // plan→build and done cursor rules) are checked against golden snapshots.
798
+ // explicit plan and terminal done cursor rules) are checked against golden snapshots.
799
799
 
800
800
  func TestParityProgress(t *testing.T) {
801
801
  work := t.TempDir()
@@ -1,7 +1,7 @@
1
1
  package main_test
2
2
 
3
- // Cross-cutting assertions the spec calls out explicitly: the engine makes zero
4
- // network calls, and the inline fail-open guard no-ops when the binary is absent.
3
+ // Cross-cutting assertions: network I/O stays inside the one sanctioned package,
4
+ // and the inline fail-open guard no-ops when the binary is absent.
5
5
 
6
6
  import (
7
7
  "bytes"
@@ -11,7 +11,7 @@ import (
11
11
  )
12
12
 
13
13
  // TestFirstPartyMakesNoNetworkCalls asserts that no first-party package imports a
14
- // network client EXCEPT internal/iohooks — the ONE sanctioned network surface,
14
+ // network client EXCEPT internal/iohooks — the one sanctioned network surface,
15
15
  // where the source-citation cache does conditional-HEAD revalidation. Confining
16
16
  // network to that single, auditable package keeps the rest of the engine a
17
17
  // network-free control plane that makes zero model calls (PRD: "zero API"). The