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
@@ -6,78 +6,94 @@ import (
6
6
  )
7
7
 
8
8
  // Report is the computed completeness status of a feature at its current phase.
9
- // It embeds the loaded Feature (Slug, Phase, Present) and adds the phase-relative
10
- // required set and the missing sections.
11
9
  type Report struct {
12
- *Feature
10
+ Slug string
11
+ Phase Phase
13
12
  Required map[Section]bool
14
- Missing []Section // legacy required-but-empty sections, in Sections order
13
+ Missing []Section // required-but-empty sections, in policy order
15
14
  RequiredFiles map[string]bool
16
15
  MissingFiles []string // required-but-empty workspace files, in lifecycle order
16
+ present map[Section]bool
17
+ diagnostics []ArtifactDiagnostic
17
18
  }
18
19
 
19
- // Status computes the status report for feature <slug> under root by reading
20
- // the files directly. It reports completeness relative to the feature's current
21
- // phase only.
20
+ // Status computes phase-relative completeness from one retained workspace observation.
22
21
  func Status(root, slug string) (*Report, error) {
23
- f, err := LoadFeature(root, slug)
22
+ return statusWithCallback(root, slug, nil)
23
+ }
24
+
25
+ func statusWithCallback(root, slug string, callback observationCallback) (*Report, error) {
26
+ observation, err := observeWorkspace(root, slug, callback)
27
+ if err != nil {
28
+ return nil, err
29
+ }
30
+ phase, err := observation.DeclaredPhase()
24
31
  if err != nil {
25
32
  return nil, fmt.Errorf("compute status: %w", err)
26
33
  }
27
- return NewReport(f), nil
34
+ policy, _ := PolicyFor(phase)
35
+ return newObservationReport(observation, phase, policy), nil
28
36
  }
29
37
 
30
- // NewReport computes the phase-relative required set and missing sections for a
31
- // loaded Feature.
32
- func NewReport(f *Feature) *Report {
33
- required := make(map[Section]bool)
34
- for _, s := range RequiredSections(f.Phase) {
35
- required[s] = true
38
+ func newObservationReport(observation *WorkspaceObservation, phase Phase, policy PhasePolicy) *Report {
39
+ present := observationSectionPresence(observation)
40
+ required := requiredSections(policy)
41
+ requiredFiles := requiredArtifactSet(policy)
42
+ missingArtifacts, diagnostics := observation.Missing(policy.RequiredArtifacts)
43
+ missingFiles := make([]string, len(missingArtifacts))
44
+ for i, artifact := range missingArtifacts {
45
+ missingFiles[i] = string(artifact)
36
46
  }
37
- requiredFiles := make(map[string]bool)
38
- for _, name := range RequiredWorkspaceFiles(f.Phase) {
39
- requiredFiles[name] = true
40
- }
41
- missingFiles := MissingWorkspaceFiles(f, f.Phase)
42
47
  return &Report{
43
- Feature: f,
48
+ Slug: observation.Slug(),
49
+ Phase: phase,
44
50
  Required: required,
45
- Missing: MissingFor(f, f.Phase),
51
+ Missing: missingObservationSections(present, policy.RequiredSections),
46
52
  RequiredFiles: requiredFiles,
47
53
  MissingFiles: missingFiles,
54
+ present: present,
55
+ diagnostics: diagnostics,
48
56
  }
49
57
  }
50
58
 
51
- // MissingFor returns the sections required to complete phase p that the feature
52
- // does not yet have real content for, in canonical Sections order. Gates use it
53
- // to check completeness against a phase other than the feature's current one
54
- // (e.g. seal always checks the full seal-phase set), so its result is
55
- // independent of f.Phase.
56
- func MissingFor(f *Feature, p Phase) []Section {
57
- required := make(map[Section]bool)
58
- for _, s := range RequiredSections(p) {
59
- required[s] = true
59
+ func observationSectionPresence(observation *WorkspaceObservation) map[Section]bool {
60
+ present := make(map[Section]bool, len(Sections))
61
+ for _, section := range Sections {
62
+ for _, name := range sectionFiles[section] {
63
+ fact, ok := observation.Fact(ArtifactPath(name))
64
+ if ok && fact.State() == ArtifactPresent {
65
+ present[section] = true
66
+ break
67
+ }
68
+ }
60
69
  }
70
+ return present
71
+ }
72
+
73
+ func missingObservationSections(present map[Section]bool, required []Section) []Section {
61
74
  var missing []Section
62
- for _, s := range Sections {
63
- if required[s] && !f.Present[s] {
64
- missing = append(missing, s)
75
+ for _, section := range required {
76
+ if !present[section] {
77
+ missing = append(missing, section)
65
78
  }
66
79
  }
67
80
  return missing
68
81
  }
69
82
 
70
- // MissingWorkspaceFiles returns the concrete lifecycle artifacts required for
71
- // p that do not contain real content. It is the authoritative runtime
72
- // completeness view; Section completeness remains only a compact legacy view.
73
- func MissingWorkspaceFiles(f *Feature, p Phase) []string {
74
- var missing []string
75
- for _, name := range RequiredWorkspaceFiles(p) {
76
- if !f.PresentFiles[name] {
77
- missing = append(missing, name)
78
- }
83
+ func requiredSections(policy PhasePolicy) map[Section]bool {
84
+ required := make(map[Section]bool, len(policy.RequiredSections))
85
+ for _, section := range policy.RequiredSections {
86
+ required[section] = true
79
87
  }
80
- return missing
88
+ return required
89
+ }
90
+
91
+ func requiredArtifactSet(policy PhasePolicy) map[string]bool {
92
+ required := make(map[string]bool, len(policy.RequiredArtifacts))
93
+ for _, artifact := range policy.RequiredArtifacts {
94
+ required[string(artifact)] = true
95
+ }
96
+ return required
81
97
  }
82
98
 
83
99
  // Complete reports whether every concrete canonical-workspace file required by
@@ -92,19 +108,22 @@ func (r *Report) Render() string {
92
108
  var b strings.Builder
93
109
  fmt.Fprintf(&b, "feature: %s\n", r.Slug)
94
110
  fmt.Fprintf(&b, "phase: %s\n", r.Phase)
95
- for _, s := range Sections {
111
+ for _, section := range Sections {
96
112
  state := "empty"
97
- if r.Present[s] {
113
+ if r.present[section] {
98
114
  state = "present"
99
115
  }
100
116
  mark := ""
101
- if r.Required[s] {
117
+ if r.Required[section] {
102
118
  mark = "required"
103
119
  }
104
- line := fmt.Sprintf(" %-10s %-8s %s", s, state, mark)
120
+ line := fmt.Sprintf(" %-10s %-8s %s", section, state, mark)
105
121
  b.WriteString(strings.TrimRight(line, " "))
106
122
  b.WriteByte('\n')
107
123
  }
124
+ for _, diagnostic := range r.diagnostics {
125
+ fmt.Fprintf(&b, "artifact: %s: %s (%s)\n", diagnostic.Path, diagnostic.State, diagnostic.Code)
126
+ }
108
127
  if r.Complete() {
109
128
  b.WriteString("result: complete\n")
110
129
  } else {
@@ -13,31 +13,34 @@ import (
13
13
  )
14
14
 
15
15
  func TestADR0004RequiredSectionsAreAdditiveDownTheArc(t *testing.T) {
16
- phaseArc := state.LifecyclePhases()
17
- for _, p := range phaseArc {
18
- if !state.KnownPhase(p) {
19
- t.Fatalf("phase %q in the arc is not a known phase", p)
16
+ policies := state.PhasePolicies()
17
+ for _, policy := range policies {
18
+ lookedUp, ok := state.PolicyFor(policy.Target)
19
+ if !ok || lookedUp.Target != policy.Target {
20
+ t.Fatalf("phase %q in the arc is not a known phase", policy.Target)
20
21
  }
21
22
  }
22
23
 
23
24
  // Each phase's required set must contain everything the previous phase
24
25
  // required: completeness only accumulates, never regresses.
25
- for i := 1; i < len(phaseArc); i++ {
26
- prev := asSet(state.RequiredSections(phaseArc[i-1]))
27
- cur := asSet(state.RequiredSections(phaseArc[i]))
28
- for s := range prev {
29
- if !cur[s] {
26
+ for i := 1; i < len(policies); i++ {
27
+ previous := asSet(policies[i-1].RequiredSections)
28
+ current := asSet(policies[i].RequiredSections)
29
+ for section := range previous {
30
+ if !current[section] {
30
31
  t.Errorf("phase %q dropped section %q that %q required: arc must be additive",
31
- phaseArc[i], s, phaseArc[i-1])
32
+ policies[i].Target, section, policies[i-1].Target)
32
33
  }
33
34
  }
34
35
  }
35
36
 
36
37
  // Anchors: framing requires nothing; seal requires the full section set.
37
- if got := state.RequiredSections(state.PhaseFrame); len(got) != 0 {
38
- t.Errorf("frame requires %v, want none", got)
38
+ frame, _ := state.PolicyFor(state.PhaseFrame)
39
+ if len(frame.RequiredSections) != 0 {
40
+ t.Errorf("frame requires %v, want none", frame.RequiredSections)
39
41
  }
40
- if got, want := len(state.RequiredSections(state.PhaseSeal)), len(state.Sections); got != want {
42
+ seal, _ := state.PolicyFor(state.PhaseSeal)
43
+ if got, want := len(seal.RequiredSections), len(state.Sections); got != want {
41
44
  t.Errorf("seal requires %d sections, want the full set of %d", got, want)
42
45
  }
43
46
  }
@@ -7,24 +7,26 @@ import (
7
7
  )
8
8
 
9
9
  func TestADR0011DefineAndPlanHaveDistinctTransitionRights(t *testing.T) {
10
- if got := state.ResumeVerb(state.PhaseDefine); got != "define" {
11
- t.Fatalf("define resumes %q, want define", got)
10
+ define, ok := state.PolicyFor(state.PhaseDefine)
11
+ if !ok || define.ResumeVerb != "define" {
12
+ t.Fatalf("define policy=(%+v,%v), want resume verb define", define, ok)
12
13
  }
13
- if got := state.ResumeVerb(state.PhasePlan); got != "vet" {
14
- t.Fatalf("plan resumes %q, want vet", got)
14
+ plan, ok := state.PolicyFor(state.PhasePlan)
15
+ if !ok || plan.ResumeVerb != "vet" {
16
+ t.Fatalf("plan policy=(%+v,%v), want resume verb vet", plan, ok)
15
17
  }
16
- if got, ok := state.PhaseForName("planning"); ok || got != "" {
17
- t.Fatalf("planning alias=(%q,%v), want speculative alias rejected", got, ok)
18
+ if policy, ok := state.PolicyFor(state.Phase("planning")); ok {
19
+ t.Fatalf("planning alias=(%+v,true), want speculative alias rejected", policy)
18
20
  }
19
21
 
20
22
  rights := map[string]state.Phase{}
21
- for _, phase := range state.WorkflowPhases() {
22
- if phase.TransitionRight == "" {
23
- t.Fatalf("phase %q has no transition right", phase.ID)
23
+ for _, policy := range state.PhasePolicies() {
24
+ if policy.TransitionRight == "" {
25
+ t.Fatalf("phase %q has no transition right", policy.Target)
24
26
  }
25
- if prior := rights[phase.TransitionRight]; prior != "" {
26
- t.Fatalf("phases %q and %q share transition right %q", prior, phase.ID, phase.TransitionRight)
27
+ if prior := rights[policy.TransitionRight]; prior != "" {
28
+ t.Fatalf("phases %q and %q share transition right %q", prior, policy.Target, policy.TransitionRight)
27
29
  }
28
- rights[phase.TransitionRight] = phase.ID
30
+ rights[policy.TransitionRight] = policy.Target
29
31
  }
30
32
  }