devrites 4.1.0 → 4.2.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 (84) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/docs/engine/commands.md +65 -6
  4. package/engine/go.mod +1 -1
  5. package/engine/internal/gate/gate.go +154 -46
  6. package/engine/internal/gate/gate_test.go +290 -2
  7. package/engine/internal/gate/readiness_binding.go +66 -100
  8. package/engine/internal/gate/readiness_binding_test.go +126 -3
  9. package/engine/internal/lib/resolve.go +6 -6
  10. package/engine/internal/state/cmd/workflowmanifest/main.go +32 -3
  11. package/engine/internal/state/feature.go +1 -100
  12. package/engine/internal/state/observation.go +584 -0
  13. package/engine/internal/state/observation_open_other.go +9 -0
  14. package/engine/internal/state/observation_open_unix.go +12 -0
  15. package/engine/internal/state/observation_test.go +960 -0
  16. package/engine/internal/state/observation_unix_test.go +68 -0
  17. package/engine/internal/state/schema.go +60 -160
  18. package/engine/internal/state/state_test.go +489 -101
  19. package/engine/internal/state/status.go +67 -48
  20. package/engine/tests/adr_0004_required_by_phase_test.go +16 -13
  21. package/engine/tests/adr_0011_define_plan_test.go +14 -12
  22. package/engine/tests/gate_test.go +403 -28
  23. package/engine/tests/workspace_observation_migration_test.go +1065 -0
  24. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +1 -10
  25. package/pack/.claude/skills/devrites-lib/reference/standards/acceptance-preserving-reslice.md +30 -0
  26. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +2 -9
  27. package/pack/.claude/skills/devrites-lib/reference/standards/one-shot-actions.md +1 -6
  28. package/pack/.claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +411 -102
  29. package/pack/.claude/skills/rite-autocomplete/SKILL.md +98 -146
  30. package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +11 -5
  31. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +96 -134
  32. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +92 -131
  33. package/pack/.claude/skills/rite-build/SKILL.md +51 -79
  34. package/pack/.claude/skills/rite-build/reference/phase-contract.md +3 -17
  35. package/pack/.claude/skills/rite-plan/SKILL.md +17 -17
  36. package/pack/.claude/skills/rite-plan/reference/anti-patterns.md +11 -1
  37. package/pack/.claude/skills/rite-plan/reference/replan-and-repair.md +10 -3
  38. package/pack/.claude/skills/rite-prove/SKILL.md +95 -153
  39. package/pack/.claude/skills/rite-vet/SKILL.md +121 -174
  40. package/pack/.claude/skills/rite-vet/reference/anti-patterns.md +14 -2
  41. package/pack/.claude/skills/rite-vet/reference/artifacts.md +10 -6
  42. package/pack/.claude/skills/rite-vet/reference/depth.md +10 -3
  43. package/pack/.claude/skills/rite-vet/reference/review-axes.md +12 -4
  44. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +1 -10
  45. package/pack/generated/claude/skills/devrites-lib/reference/standards/acceptance-preserving-reslice.md +30 -0
  46. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +2 -9
  47. package/pack/generated/claude/skills/devrites-lib/reference/standards/one-shot-actions.md +1 -6
  48. package/pack/generated/claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +411 -102
  49. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +98 -146
  50. package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +11 -5
  51. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +96 -134
  52. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +92 -131
  53. package/pack/generated/claude/skills/rite-build/SKILL.md +51 -79
  54. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +3 -17
  55. package/pack/generated/claude/skills/rite-plan/SKILL.md +17 -17
  56. package/pack/generated/claude/skills/rite-plan/reference/anti-patterns.md +11 -1
  57. package/pack/generated/claude/skills/rite-plan/reference/replan-and-repair.md +10 -3
  58. package/pack/generated/claude/skills/rite-prove/SKILL.md +95 -153
  59. package/pack/generated/claude/skills/rite-vet/SKILL.md +121 -174
  60. package/pack/generated/claude/skills/rite-vet/reference/anti-patterns.md +14 -2
  61. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +10 -6
  62. package/pack/generated/claude/skills/rite-vet/reference/depth.md +10 -3
  63. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +12 -4
  64. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +1 -10
  65. package/pack/generated/codex/skills/devrites-lib/reference/standards/acceptance-preserving-reslice.md +30 -0
  66. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +2 -9
  67. package/pack/generated/codex/skills/devrites-lib/reference/standards/one-shot-actions.md +1 -6
  68. package/pack/generated/codex/skills/devrites-lib/reference/standards/workflow-artifacts.md +411 -102
  69. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +98 -146
  70. package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +11 -5
  71. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +96 -134
  72. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +92 -131
  73. package/pack/generated/codex/skills/rite-build/SKILL.md +51 -79
  74. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +3 -17
  75. package/pack/generated/codex/skills/rite-plan/SKILL.md +17 -17
  76. package/pack/generated/codex/skills/rite-plan/reference/anti-patterns.md +11 -1
  77. package/pack/generated/codex/skills/rite-plan/reference/replan-and-repair.md +10 -3
  78. package/pack/generated/codex/skills/rite-prove/SKILL.md +95 -153
  79. package/pack/generated/codex/skills/rite-vet/SKILL.md +121 -174
  80. package/pack/generated/codex/skills/rite-vet/reference/anti-patterns.md +14 -2
  81. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +10 -6
  82. package/pack/generated/codex/skills/rite-vet/reference/depth.md +10 -3
  83. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +12 -4
  84. package/package.json +1 -1
@@ -0,0 +1,68 @@
1
+ //go:build unix
2
+
3
+ package state
4
+
5
+ import (
6
+ "os"
7
+ "path/filepath"
8
+ "syscall"
9
+ "testing"
10
+ "time"
11
+ )
12
+
13
+ type fifoObservationResult struct {
14
+ observation *WorkspaceObservation
15
+ observationErr error
16
+ setupErr error
17
+ fifoCreated bool
18
+ }
19
+
20
+ func TestWorkspaceObservationFIFORegularSubstitutionDoesNotWaitForWriter(t *testing.T) {
21
+ root, slug := newObservationWorkspace(t)
22
+ writeObservationArtifact(t, root, slug, LedgerFile, []byte("- Phase: build\n"))
23
+ writeObservationArtifact(t, root, slug, "brief.md", []byte("regular\n"))
24
+ artifactPath := filepath.Join(root, "work", slug, "brief.md")
25
+
26
+ result := make(chan fifoObservationResult, 1)
27
+ go func() {
28
+ var setupErr error
29
+ fifoCreated := false
30
+ observation, observationErr := observeWorkspace(root, slug, func(stage observationStage, artifact ArtifactPath) error {
31
+ if stage != observationBeforeOpen || artifact != "brief.md" {
32
+ return nil
33
+ }
34
+ if err := os.Remove(artifactPath); err != nil {
35
+ setupErr = err
36
+ return nil
37
+ }
38
+ if err := syscall.Mkfifo(artifactPath, 0o600); err != nil {
39
+ setupErr = err
40
+ return nil
41
+ }
42
+ fifoCreated = true
43
+ return nil
44
+ })
45
+ result <- fifoObservationResult{
46
+ observation: observation,
47
+ observationErr: observationErr,
48
+ setupErr: setupErr,
49
+ fifoCreated: fifoCreated,
50
+ }
51
+ }()
52
+
53
+ select {
54
+ case got := <-result:
55
+ if got.setupErr != nil {
56
+ t.Fatalf("FIFO setup failed: %v", got.setupErr)
57
+ }
58
+ if !got.fifoCreated {
59
+ t.Fatal("FIFO setup reported success without creating the FIFO")
60
+ }
61
+ if got.observation != nil {
62
+ t.Fatalf("observation = %+v, want nil", got.observation)
63
+ }
64
+ assertObservationFailure(t, got.observationErr, ObservationConcurrentChange, "workspace observation: concurrent_change: workspace changed during acquisition; retry")
65
+ case <-time.After(2 * time.Second):
66
+ t.Fatal("ObserveWorkspace waited for a FIFO writer")
67
+ }
68
+ }
@@ -66,18 +66,19 @@ const (
66
66
  PhaseDone Phase = "done" // archived completion
67
67
  )
68
68
 
69
- // phaseDefinition owns lifecycle order and phase behavior. Workflow topology is
70
- // versioned application logic rather than deployment configuration, so the
71
- // compiler sees changes and every consumer derives the same ordering.
72
- type phaseDefinition struct {
73
- phase Phase
74
- resumeVerb string
75
- transitionRight string
76
- required []Section
77
- workspaceRequired []string
78
- proofRequired bool
79
- blocksOpenQuestions bool
80
- shippable bool
69
+ // ArtifactPath is the logical identity of an observed workspace artifact.
70
+ type ArtifactPath string
71
+
72
+ // PhasePolicy is the complete deterministic policy for one target Phase.
73
+ type PhasePolicy struct {
74
+ Target Phase
75
+ ResumeVerb string
76
+ TransitionRight string
77
+ RequiredSections []Section
78
+ RequiredArtifacts []ArtifactPath
79
+ ProofRequired bool
80
+ BlocksOpenQuestions bool
81
+ Shippable bool
81
82
  }
82
83
 
83
84
  var (
@@ -87,48 +88,43 @@ var (
87
88
  sectionsProof = []Section{SectionSpec, SectionPlan, SectionDecisions, SectionTasks, SectionProof}
88
89
  sectionsComplete = []Section{SectionSpec, SectionPlan, SectionDecisions, SectionTasks, SectionProof, SectionStatus}
89
90
 
90
- workspaceFrame = []string{"state.md"}
91
- workspaceSpec = []string{"brief.md", "spec.md", "state.md", "decisions.md", "assumptions.md", "questions.md"}
92
- workspaceClarify = append(append([]string(nil), workspaceSpec...), "decision-coverage.md")
93
- workspacePlan = append(append([]string(nil), workspaceClarify...), "architecture.md", "plan.md", "tasks.md", "traceability.md")
94
- workspaceVetted = append(append([]string(nil), workspacePlan...), "eng-review.md", "test-plan.md")
95
- workspaceProof = append(append([]string(nil), workspaceVetted...), "evidence.md", "touched-files.md")
96
- workspaceFinal = append(append([]string(nil), workspaceProof...), "review.md", "seal.md")
91
+ artifactsFrame = []ArtifactPath{"state.md"}
92
+ artifactsSpec = []ArtifactPath{"brief.md", "spec.md", "state.md", "decisions.md", "assumptions.md", "questions.md"}
93
+ artifactsClarify = append(append([]ArtifactPath(nil), artifactsSpec...), "decision-coverage.md")
94
+ artifactsPlan = append(append([]ArtifactPath(nil), artifactsClarify...), "architecture.md", "plan.md", "tasks.md", "traceability.md")
95
+ artifactsVetted = append(append([]ArtifactPath(nil), artifactsPlan...), "eng-review.md", "test-plan.md")
96
+ artifactsProof = append(append([]ArtifactPath(nil), artifactsVetted...), "evidence.md", "touched-files.md")
97
+ artifactsFinal = append(append([]ArtifactPath(nil), artifactsProof...), "review.md", "seal.md")
97
98
  )
98
99
 
99
100
  // Completeness is phase-relative: a section not yet required (e.g. proof during
100
101
  // spec) never blocks. Requirements are additive down the arc. Define is active
101
102
  // authoring; Plan is the approved/repaired checkpoint that resumes at Vet.
102
- var phaseDefinitions = []phaseDefinition{
103
- {phase: PhaseFrame, resumeVerb: "frame", transitionRight: "Frame an unstructured request before lifecycle work.", workspaceRequired: workspaceFrame},
104
- {phase: PhaseSpec, resumeVerb: "spec", transitionRight: "Author the product specification.", required: sectionsSpec, workspaceRequired: workspaceSpec},
105
- {phase: PhaseClarify, resumeVerb: "clarify", transitionRight: "Close decision coverage in the written specification.", required: sectionsSpec, workspaceRequired: workspaceClarify, blocksOpenQuestions: true},
106
- {phase: PhaseTemper, resumeVerb: "temper", transitionRight: "Optionally challenge the clarified specification strategy.", required: sectionsSpec, workspaceRequired: workspaceClarify, blocksOpenQuestions: true},
107
- {phase: PhaseDefine, resumeVerb: "define", transitionRight: "Author and approve the initial implementation plan.", required: sectionsPlan, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
108
- {phase: PhasePlan, resumeVerb: "vet", transitionRight: "Hold the approved or repaired plan checkpoint for engineering review.", required: sectionsPlan, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
109
- {phase: PhaseVet, resumeVerb: "vet", transitionRight: "Review implementation readiness before build.", required: sectionsBuild, workspaceRequired: workspaceVetted, blocksOpenQuestions: true},
110
- {phase: PhaseBuild, resumeVerb: "build", transitionRight: "Implement the next approved vertical slice.", required: sectionsBuild, workspaceRequired: workspaceVetted, blocksOpenQuestions: true},
111
- {phase: PhaseConverge, resumeVerb: "converge", transitionRight: "Recover unmet clarified intent into new slices.", required: sectionsBuild, workspaceRequired: workspaceVetted, blocksOpenQuestions: true},
112
- {phase: PhaseProve, resumeVerb: "prove", transitionRight: "Produce acceptance evidence for the implementation.", required: sectionsProof, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
113
- {phase: PhasePolish, resumeVerb: "polish", transitionRight: "Apply the bounded quality pass.", required: sectionsProof, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
114
- {phase: PhaseReview, resumeVerb: "review", transitionRight: "Review the proven implementation.", required: sectionsProof, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
115
- {phase: PhaseSeal, resumeVerb: "seal", transitionRight: "Decide the final GO or NO-GO.", required: sectionsComplete, workspaceRequired: workspaceFinal, proofRequired: true, blocksOpenQuestions: true, shippable: true},
116
- {phase: PhaseShip, resumeVerb: "ship", transitionRight: "Perform authorized release and close-out mutations.", required: sectionsComplete, workspaceRequired: workspaceFinal, proofRequired: true, blocksOpenQuestions: true, shippable: true},
117
- {phase: PhaseDone, transitionRight: "Represent archived completion with no resume command.", required: sectionsComplete, workspaceRequired: workspaceFinal, proofRequired: true, blocksOpenQuestions: true, shippable: true},
118
- }
119
-
120
- // WorkflowPhase is the read-only cross-format view used to generate the compact
121
- // manifest consumed by non-Go release tooling.
122
- type WorkflowPhase struct {
123
- ID Phase `json:"id"`
124
- ResumeVerb string `json:"resumeVerb,omitempty"`
125
- TransitionRight string `json:"transitionRight"`
126
- RequiredSections []Section `json:"requiredSections,omitempty"`
127
- WorkspaceRequired []string `json:"workspaceRequired"`
128
- ProofRequired bool `json:"proofRequired,omitempty"`
129
- BlocksOpenQuestions bool `json:"blocksOpenQuestions,omitempty"`
130
- Shippable bool `json:"shippable,omitempty"`
131
- }
103
+ var orderedPhasePolicies = []PhasePolicy{
104
+ {Target: PhaseFrame, ResumeVerb: "frame", TransitionRight: "Frame an unstructured request before lifecycle work.", RequiredArtifacts: artifactsFrame},
105
+ {Target: PhaseSpec, ResumeVerb: "spec", TransitionRight: "Author the product specification.", RequiredSections: sectionsSpec, RequiredArtifacts: artifactsSpec},
106
+ {Target: PhaseClarify, ResumeVerb: "clarify", TransitionRight: "Close decision coverage in the written specification.", RequiredSections: sectionsSpec, RequiredArtifacts: artifactsClarify, BlocksOpenQuestions: true},
107
+ {Target: PhaseTemper, ResumeVerb: "temper", TransitionRight: "Optionally challenge the clarified specification strategy.", RequiredSections: sectionsSpec, RequiredArtifacts: artifactsClarify, BlocksOpenQuestions: true},
108
+ {Target: PhaseDefine, ResumeVerb: "define", TransitionRight: "Author and approve the initial implementation plan.", RequiredSections: sectionsPlan, RequiredArtifacts: artifactsPlan, BlocksOpenQuestions: true},
109
+ {Target: PhasePlan, ResumeVerb: "vet", TransitionRight: "Hold the approved or repaired plan checkpoint for engineering review.", RequiredSections: sectionsPlan, RequiredArtifacts: artifactsPlan, BlocksOpenQuestions: true},
110
+ {Target: PhaseVet, ResumeVerb: "vet", TransitionRight: "Review implementation readiness before build.", RequiredSections: sectionsBuild, RequiredArtifacts: artifactsVetted, BlocksOpenQuestions: true},
111
+ {Target: PhaseBuild, ResumeVerb: "build", TransitionRight: "Implement the next approved vertical slice.", RequiredSections: sectionsBuild, RequiredArtifacts: artifactsVetted, BlocksOpenQuestions: true},
112
+ {Target: PhaseConverge, ResumeVerb: "converge", TransitionRight: "Recover unmet clarified intent into new slices.", RequiredSections: sectionsBuild, RequiredArtifacts: artifactsVetted, BlocksOpenQuestions: true},
113
+ {Target: PhaseProve, ResumeVerb: "prove", TransitionRight: "Produce acceptance evidence for the implementation.", RequiredSections: sectionsProof, RequiredArtifacts: artifactsProof, ProofRequired: true, BlocksOpenQuestions: true},
114
+ {Target: PhasePolish, ResumeVerb: "polish", TransitionRight: "Apply the bounded quality pass.", RequiredSections: sectionsProof, RequiredArtifacts: artifactsProof, ProofRequired: true, BlocksOpenQuestions: true},
115
+ {Target: PhaseReview, ResumeVerb: "review", TransitionRight: "Review the proven implementation.", RequiredSections: sectionsProof, RequiredArtifacts: artifactsProof, ProofRequired: true, BlocksOpenQuestions: true},
116
+ {Target: PhaseSeal, ResumeVerb: "seal", TransitionRight: "Decide the final GO or NO-GO.", RequiredSections: sectionsComplete, RequiredArtifacts: artifactsFinal, ProofRequired: true, BlocksOpenQuestions: true, Shippable: true},
117
+ {Target: PhaseShip, ResumeVerb: "ship", TransitionRight: "Perform authorized release and close-out mutations.", RequiredSections: sectionsComplete, RequiredArtifacts: artifactsFinal, ProofRequired: true, BlocksOpenQuestions: true, Shippable: true},
118
+ {Target: PhaseDone, TransitionRight: "Represent archived completion with no resume command.", RequiredSections: sectionsComplete, RequiredArtifacts: artifactsFinal, ProofRequired: true, BlocksOpenQuestions: true, Shippable: true},
119
+ }
120
+
121
+ var phasePolicyIndex = func() map[Phase]int {
122
+ index := make(map[Phase]int, len(orderedPhasePolicies))
123
+ for i, policy := range orderedPhasePolicies {
124
+ index[policy.Target] = i
125
+ }
126
+ return index
127
+ }()
132
128
 
133
129
  // AuthorityPolicy owns the small cross-format trust and tracking assertions
134
130
  // that otherwise drift between current docs.
@@ -147,122 +143,26 @@ func WorkflowAuthorityPolicy() AuthorityPolicy {
147
143
  }
148
144
  }
149
145
 
150
- // WorkflowPhases returns copied metadata suitable for deterministic generation.
151
- func WorkflowPhases() []WorkflowPhase {
152
- out := make([]WorkflowPhase, 0, len(phaseDefinitions))
153
- for _, definition := range phaseDefinitions {
154
- out = append(out, WorkflowPhase{
155
- ID: definition.phase,
156
- ResumeVerb: definition.resumeVerb,
157
- TransitionRight: definition.transitionRight,
158
- RequiredSections: append([]Section(nil), definition.required...),
159
- WorkspaceRequired: append([]string(nil), definition.workspaceRequired...),
160
- ProofRequired: definition.proofRequired,
161
- BlocksOpenQuestions: definition.blocksOpenQuestions,
162
- Shippable: definition.shippable,
163
- })
164
- }
165
- return out
166
- }
167
-
168
- // LifecyclePhases returns the ordered lifecycle. The returned slice is a copy,
169
- // so callers cannot mutate the registry.
170
- func LifecyclePhases() []Phase {
171
- phases := make([]Phase, len(phaseDefinitions))
172
- for i, definition := range phaseDefinitions {
173
- phases[i] = definition.phase
174
- }
175
- return phases
176
- }
177
-
178
- func definitionFor(p Phase) (phaseDefinition, bool) {
179
- for _, definition := range phaseDefinitions {
180
- if definition.phase == p {
181
- return definition, true
182
- }
183
- }
184
- return phaseDefinition{}, false
185
- }
186
-
187
- // KnownPhase reports whether p is a phase the engine understands.
188
- func KnownPhase(p Phase) bool {
189
- _, ok := definitionFor(p)
190
- return ok
191
- }
192
-
193
- // ResumeVerb returns the public rite verb that resumes p. Terminal and unknown
194
- // phases return an empty string.
195
- func ResumeVerb(p Phase) string {
196
- definition, ok := definitionFor(p)
197
- if !ok {
198
- return ""
199
- }
200
- return definition.resumeVerb
201
- }
202
-
203
- // ShippablePhase reports whether p is allowed to claim a sealed/shipped result.
204
- func ShippablePhase(p Phase) bool {
205
- definition, ok := definitionFor(p)
206
- return ok && definition.shippable
207
- }
208
-
209
- // PhaseForName resolves a canonical phase ID.
210
- func PhaseForName(name string) (Phase, bool) {
211
- for _, definition := range phaseDefinitions {
212
- if string(definition.phase) == name {
213
- return definition.phase, true
214
- }
215
- }
216
- return "", false
217
- }
218
-
219
- // RequiredSections returns the sections required to complete the given phase,
220
- // in canonical Sections order.
221
- func RequiredSections(p Phase) []Section {
222
- definition, ok := definitionFor(p)
223
- if !ok {
224
- return nil
225
- }
226
- want := definition.required
227
- set := make(map[Section]bool, len(want))
228
- for _, s := range want {
229
- set[s] = true
230
- }
231
- out := make([]Section, 0, len(want))
232
- for _, s := range Sections {
233
- if set[s] {
234
- out = append(out, s)
235
- }
236
- }
237
- return out
146
+ func copyPhasePolicy(policy PhasePolicy) PhasePolicy {
147
+ policy.RequiredSections = append([]Section(nil), policy.RequiredSections...)
148
+ policy.RequiredArtifacts = append([]ArtifactPath(nil), policy.RequiredArtifacts...)
149
+ return policy
238
150
  }
239
151
 
240
- // RequiredWorkspaceFiles returns the canonical per-file completeness contract
241
- // for phase p. Unlike the legacy Section view, this includes every durable
242
- // workflow artifact (for example decision-coverage.md and test-plan.md), so
243
- // runtime gates cannot silently ignore files added to the lifecycle registry.
244
- func RequiredWorkspaceFiles(p Phase) []string {
245
- definition, ok := definitionFor(p)
152
+ // PolicyFor returns the complete policy for target. Unknown phases have no fallback.
153
+ func PolicyFor(target Phase) (PhasePolicy, bool) {
154
+ index, ok := phasePolicyIndex[target]
246
155
  if !ok {
247
- return nil
156
+ return PhasePolicy{}, false
248
157
  }
249
- return append([]string(nil), definition.workspaceRequired...)
158
+ return copyPhasePolicy(orderedPhasePolicies[index]), true
250
159
  }
251
160
 
252
- // WorkspaceFiles returns every lifecycle-owned workspace filename once, in
253
- // first-required order. It is derived from phaseDefinitions so the runtime
254
- // presence model and the generated workflow manifest share one authority.
255
- func WorkspaceFiles() []string {
256
- seen := make(map[string]bool)
257
- var out []string
258
- for _, definition := range phaseDefinitions {
259
- for _, name := range definition.workspaceRequired {
260
- if seen[name] {
261
- continue
262
- }
263
- seen[name] = true
264
- out = append(out, name)
265
- }
161
+ // PhasePolicies returns all policies in lifecycle order.
162
+ func PhasePolicies() []PhasePolicy {
163
+ policies := make([]PhasePolicy, len(orderedPhasePolicies))
164
+ for i, policy := range orderedPhasePolicies {
165
+ policies[i] = copyPhasePolicy(policy)
266
166
  }
267
- return out
167
+ return policies
268
168
  }