devrites 4.1.0 → 4.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 +13 -0
- package/README.md +1 -1
- package/docs/engine/commands.md +69 -6
- package/docs/orchestration.md +6 -3
- package/engine/go.mod +1 -1
- package/engine/internal/gate/gate.go +154 -46
- package/engine/internal/gate/gate_test.go +290 -2
- package/engine/internal/gate/readiness_binding.go +66 -100
- package/engine/internal/gate/readiness_binding_test.go +126 -3
- package/engine/internal/lib/cli_observe.go +66 -0
- package/engine/internal/lib/observe_summary.go +79 -0
- package/engine/internal/lib/resolve.go +6 -6
- package/engine/internal/lib/skilltrust.go +196 -0
- package/engine/internal/lib/taskgraph.go +162 -0
- package/engine/internal/lib/taskgraph_test.go +150 -0
- package/engine/internal/lib/workspace_read.go +29 -0
- package/engine/internal/parallel/cli.go +467 -0
- package/engine/internal/parallel/git.go +181 -0
- package/engine/internal/parallel/lease.go +244 -0
- package/engine/internal/parallel/ops.go +416 -0
- package/engine/internal/parallel/parallel_test.go +242 -0
- package/engine/internal/parallel/pathdisjoint.go +129 -0
- package/engine/internal/parallel/pathdisjoint_test.go +97 -0
- package/engine/internal/state/cmd/workflowmanifest/main.go +32 -3
- package/engine/internal/state/feature.go +1 -100
- package/engine/internal/state/observation.go +584 -0
- package/engine/internal/state/observation_open_other.go +9 -0
- package/engine/internal/state/observation_open_unix.go +12 -0
- package/engine/internal/state/observation_test.go +960 -0
- package/engine/internal/state/observation_unix_test.go +68 -0
- package/engine/internal/state/schema.go +60 -160
- package/engine/internal/state/state_test.go +489 -101
- package/engine/internal/state/status.go +97 -53
- package/engine/main.go +72 -3
- package/engine/root_routing_test.go +1 -1
- package/engine/tests/adr_0004_required_by_phase_test.go +16 -13
- package/engine/tests/adr_0011_define_plan_test.go +14 -12
- package/engine/tests/gate_test.go +403 -28
- package/engine/tests/workspace_observation_migration_test.go +1065 -0
- package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +1 -10
- package/pack/.claude/skills/devrites-lib/reference/standards/acceptance-preserving-reslice.md +30 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +2 -9
- package/pack/.claude/skills/devrites-lib/reference/standards/one-shot-actions.md +1 -6
- package/pack/.claude/skills/devrites-lib/reference/standards/skill-authoring.md +22 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +411 -102
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +98 -146
- package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +11 -5
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +96 -134
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +92 -131
- package/pack/.claude/skills/rite-build/SKILL.md +46 -81
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +19 -25
- package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +29 -59
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +8 -3
- package/pack/.claude/skills/rite-build/reference/output.md +2 -0
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +64 -0
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +9 -22
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +7 -3
- package/pack/.claude/skills/rite-clarify/reference/anti-patterns.md +24 -0
- package/pack/.claude/skills/rite-customize/SKILL.md +3 -2
- package/pack/.claude/skills/rite-doctor/SKILL.md +5 -1
- package/pack/.claude/skills/rite-plan/SKILL.md +17 -17
- package/pack/.claude/skills/rite-plan/reference/anti-patterns.md +11 -1
- package/pack/.claude/skills/rite-plan/reference/dependency-graph.md +3 -0
- package/pack/.claude/skills/rite-plan/reference/replan-and-repair.md +10 -3
- package/pack/.claude/skills/rite-prove/SKILL.md +95 -153
- package/pack/.claude/skills/rite-vet/SKILL.md +121 -174
- package/pack/.claude/skills/rite-vet/reference/anti-patterns.md +14 -2
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +10 -6
- package/pack/.claude/skills/rite-vet/reference/depth.md +10 -3
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +12 -4
- package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +1 -10
- package/pack/generated/claude/skills/devrites-lib/reference/standards/acceptance-preserving-reslice.md +30 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +2 -9
- package/pack/generated/claude/skills/devrites-lib/reference/standards/one-shot-actions.md +1 -6
- package/pack/generated/claude/skills/devrites-lib/reference/standards/skill-authoring.md +22 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +411 -102
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +98 -146
- package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +11 -5
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +96 -134
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +92 -131
- package/pack/generated/claude/skills/rite-build/SKILL.md +46 -81
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +19 -25
- package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +29 -59
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +8 -3
- package/pack/generated/claude/skills/rite-build/reference/output.md +2 -0
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +64 -0
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +9 -22
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +7 -3
- package/pack/generated/claude/skills/rite-clarify/reference/anti-patterns.md +24 -0
- package/pack/generated/claude/skills/rite-customize/SKILL.md +3 -2
- package/pack/generated/claude/skills/rite-doctor/SKILL.md +5 -1
- package/pack/generated/claude/skills/rite-plan/SKILL.md +17 -17
- package/pack/generated/claude/skills/rite-plan/reference/anti-patterns.md +11 -1
- package/pack/generated/claude/skills/rite-plan/reference/dependency-graph.md +3 -0
- package/pack/generated/claude/skills/rite-plan/reference/replan-and-repair.md +10 -3
- package/pack/generated/claude/skills/rite-prove/SKILL.md +95 -153
- package/pack/generated/claude/skills/rite-vet/SKILL.md +121 -174
- package/pack/generated/claude/skills/rite-vet/reference/anti-patterns.md +14 -2
- package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +10 -6
- package/pack/generated/claude/skills/rite-vet/reference/depth.md +10 -3
- package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +12 -4
- package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +1 -10
- package/pack/generated/codex/skills/devrites-lib/reference/standards/acceptance-preserving-reslice.md +30 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +2 -9
- package/pack/generated/codex/skills/devrites-lib/reference/standards/one-shot-actions.md +1 -6
- package/pack/generated/codex/skills/devrites-lib/reference/standards/skill-authoring.md +22 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/workflow-artifacts.md +411 -102
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +98 -146
- package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +11 -5
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +96 -134
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +92 -131
- package/pack/generated/codex/skills/rite-build/SKILL.md +46 -81
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +19 -25
- package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +29 -59
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +8 -3
- package/pack/generated/codex/skills/rite-build/reference/output.md +2 -0
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +64 -0
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +9 -22
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +7 -3
- package/pack/generated/codex/skills/rite-clarify/reference/anti-patterns.md +24 -0
- package/pack/generated/codex/skills/rite-customize/SKILL.md +3 -2
- package/pack/generated/codex/skills/rite-doctor/SKILL.md +5 -1
- package/pack/generated/codex/skills/rite-plan/SKILL.md +17 -17
- package/pack/generated/codex/skills/rite-plan/reference/anti-patterns.md +11 -1
- package/pack/generated/codex/skills/rite-plan/reference/dependency-graph.md +3 -0
- package/pack/generated/codex/skills/rite-plan/reference/replan-and-repair.md +10 -3
- package/pack/generated/codex/skills/rite-prove/SKILL.md +95 -153
- package/pack/generated/codex/skills/rite-vet/SKILL.md +121 -174
- package/pack/generated/codex/skills/rite-vet/reference/anti-patterns.md +14 -2
- package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +10 -6
- package/pack/generated/codex/skills/rite-vet/reference/depth.md +10 -3
- package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +12 -4
- package/package.json +1 -1
|
@@ -6,78 +6,119 @@ 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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
Slug string
|
|
11
|
+
Phase Phase
|
|
12
|
+
Status string
|
|
13
|
+
NextAction string
|
|
14
|
+
PrinciplesPresent bool
|
|
15
|
+
Required map[Section]bool
|
|
16
|
+
Missing []Section // required-but-empty sections, in policy order
|
|
17
|
+
RequiredFiles map[string]bool
|
|
18
|
+
MissingFiles []string // required-but-empty workspace files, in lifecycle order
|
|
19
|
+
present map[Section]bool
|
|
20
|
+
diagnostics []ArtifactDiagnostic
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
// Status computes
|
|
20
|
-
// the files directly. It reports completeness relative to the feature's current
|
|
21
|
-
// phase only.
|
|
23
|
+
// Status computes phase-relative completeness from one retained workspace observation.
|
|
22
24
|
func Status(root, slug string) (*Report, error) {
|
|
23
|
-
|
|
25
|
+
return statusWithCallback(root, slug, nil)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func statusWithCallback(root, slug string, callback observationCallback) (*Report, error) {
|
|
29
|
+
observation, err := observeWorkspace(root, slug, callback)
|
|
30
|
+
if err != nil {
|
|
31
|
+
return nil, err
|
|
32
|
+
}
|
|
33
|
+
phase, err := observation.DeclaredPhase()
|
|
24
34
|
if err != nil {
|
|
25
35
|
return nil, fmt.Errorf("compute status: %w", err)
|
|
26
36
|
}
|
|
27
|
-
|
|
37
|
+
policy, _ := PolicyFor(phase)
|
|
38
|
+
return newObservationReport(observation, phase, policy), nil
|
|
28
39
|
}
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
func observationCursorFields(observation *WorkspaceObservation) (status, nextAction string, principlesPresent bool) {
|
|
42
|
+
if fact, ok := observation.Fact(".devrites/principles.md"); ok && fact.State() == ArtifactPresent {
|
|
43
|
+
principlesPresent = true
|
|
44
|
+
}
|
|
45
|
+
fact, ok := observation.Fact(LedgerFile)
|
|
46
|
+
if !ok || fact.State() != ArtifactPresent {
|
|
47
|
+
return "", "", principlesPresent
|
|
36
48
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
lines := strings.Split(string(fact.Bytes()), "\n")
|
|
50
|
+
if value, ok := CursorField(lines, CursorStatus); ok {
|
|
51
|
+
status = value
|
|
52
|
+
}
|
|
53
|
+
if value, ok := CursorField(lines, CursorNextAction); ok {
|
|
54
|
+
nextAction = value
|
|
55
|
+
}
|
|
56
|
+
return status, nextAction, principlesPresent
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func newObservationReport(observation *WorkspaceObservation, phase Phase, policy PhasePolicy) *Report {
|
|
60
|
+
present := observationSectionPresence(observation)
|
|
61
|
+
required := requiredSections(policy)
|
|
62
|
+
requiredFiles := requiredArtifactSet(policy)
|
|
63
|
+
missingArtifacts, diagnostics := observation.Missing(policy.RequiredArtifacts)
|
|
64
|
+
missingFiles := make([]string, len(missingArtifacts))
|
|
65
|
+
for i, artifact := range missingArtifacts {
|
|
66
|
+
missingFiles[i] = string(artifact)
|
|
40
67
|
}
|
|
41
|
-
|
|
68
|
+
status, nextAction, principlesPresent := observationCursorFields(observation)
|
|
42
69
|
return &Report{
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
70
|
+
Slug: observation.Slug(),
|
|
71
|
+
Phase: phase,
|
|
72
|
+
Status: status,
|
|
73
|
+
NextAction: nextAction,
|
|
74
|
+
PrinciplesPresent: principlesPresent,
|
|
75
|
+
Required: required,
|
|
76
|
+
Missing: missingObservationSections(present, policy.RequiredSections),
|
|
77
|
+
RequiredFiles: requiredFiles,
|
|
78
|
+
MissingFiles: missingFiles,
|
|
79
|
+
present: present,
|
|
80
|
+
diagnostics: diagnostics,
|
|
48
81
|
}
|
|
49
82
|
}
|
|
50
83
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
84
|
+
func observationSectionPresence(observation *WorkspaceObservation) map[Section]bool {
|
|
85
|
+
present := make(map[Section]bool, len(Sections))
|
|
86
|
+
for _, section := range Sections {
|
|
87
|
+
for _, name := range sectionFiles[section] {
|
|
88
|
+
fact, ok := observation.Fact(ArtifactPath(name))
|
|
89
|
+
if ok && fact.State() == ArtifactPresent {
|
|
90
|
+
present[section] = true
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
}
|
|
60
94
|
}
|
|
95
|
+
return present
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
func missingObservationSections(present map[Section]bool, required []Section) []Section {
|
|
61
99
|
var missing []Section
|
|
62
|
-
for _,
|
|
63
|
-
if
|
|
64
|
-
missing = append(missing,
|
|
100
|
+
for _, section := range required {
|
|
101
|
+
if !present[section] {
|
|
102
|
+
missing = append(missing, section)
|
|
65
103
|
}
|
|
66
104
|
}
|
|
67
105
|
return missing
|
|
68
106
|
}
|
|
69
107
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
var missing []string
|
|
75
|
-
for _, name := range RequiredWorkspaceFiles(p) {
|
|
76
|
-
if !f.PresentFiles[name] {
|
|
77
|
-
missing = append(missing, name)
|
|
78
|
-
}
|
|
108
|
+
func requiredSections(policy PhasePolicy) map[Section]bool {
|
|
109
|
+
required := make(map[Section]bool, len(policy.RequiredSections))
|
|
110
|
+
for _, section := range policy.RequiredSections {
|
|
111
|
+
required[section] = true
|
|
79
112
|
}
|
|
80
|
-
return
|
|
113
|
+
return required
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func requiredArtifactSet(policy PhasePolicy) map[string]bool {
|
|
117
|
+
required := make(map[string]bool, len(policy.RequiredArtifacts))
|
|
118
|
+
for _, artifact := range policy.RequiredArtifacts {
|
|
119
|
+
required[string(artifact)] = true
|
|
120
|
+
}
|
|
121
|
+
return required
|
|
81
122
|
}
|
|
82
123
|
|
|
83
124
|
// Complete reports whether every concrete canonical-workspace file required by
|
|
@@ -92,19 +133,22 @@ func (r *Report) Render() string {
|
|
|
92
133
|
var b strings.Builder
|
|
93
134
|
fmt.Fprintf(&b, "feature: %s\n", r.Slug)
|
|
94
135
|
fmt.Fprintf(&b, "phase: %s\n", r.Phase)
|
|
95
|
-
for _,
|
|
136
|
+
for _, section := range Sections {
|
|
96
137
|
state := "empty"
|
|
97
|
-
if r.
|
|
138
|
+
if r.present[section] {
|
|
98
139
|
state = "present"
|
|
99
140
|
}
|
|
100
141
|
mark := ""
|
|
101
|
-
if r.Required[
|
|
142
|
+
if r.Required[section] {
|
|
102
143
|
mark = "required"
|
|
103
144
|
}
|
|
104
|
-
line := fmt.Sprintf(" %-10s %-8s %s",
|
|
145
|
+
line := fmt.Sprintf(" %-10s %-8s %s", section, state, mark)
|
|
105
146
|
b.WriteString(strings.TrimRight(line, " "))
|
|
106
147
|
b.WriteByte('\n')
|
|
107
148
|
}
|
|
149
|
+
for _, diagnostic := range r.diagnostics {
|
|
150
|
+
fmt.Fprintf(&b, "artifact: %s: %s (%s)\n", diagnostic.Path, diagnostic.State, diagnostic.Code)
|
|
151
|
+
}
|
|
108
152
|
if r.Complete() {
|
|
109
153
|
b.WriteString("result: complete\n")
|
|
110
154
|
} else {
|
package/engine/main.go
CHANGED
|
@@ -10,6 +10,7 @@ import (
|
|
|
10
10
|
"github.com/devrites/devrites/internal/gate"
|
|
11
11
|
"github.com/devrites/devrites/internal/install"
|
|
12
12
|
"github.com/devrites/devrites/internal/lib"
|
|
13
|
+
"github.com/devrites/devrites/internal/parallel"
|
|
13
14
|
"github.com/devrites/devrites/internal/state"
|
|
14
15
|
"github.com/devrites/devrites/internal/version"
|
|
15
16
|
)
|
|
@@ -24,6 +25,12 @@ Usage:
|
|
|
24
25
|
devrites-engine check readiness <slug> Check required files and the stable Build-input binding
|
|
25
26
|
devrites-engine check readiness --emit-binding <slug> Emit the stable Build-input binding for Vet
|
|
26
27
|
devrites-engine check seal <slug> Recheck the Build-input binding, final files, and evidence freshness
|
|
28
|
+
devrites-engine check path-disjoint [--root <dir>] [<json-file>|-]
|
|
29
|
+
Verify slice path sets are pairwise disjoint
|
|
30
|
+
devrites-engine check task-graph <slug> Validate tasks.md slice dependency graph
|
|
31
|
+
devrites-engine check skill-trust <path> Scan one skill/agent Markdown for trust violations
|
|
32
|
+
devrites-engine observe summary <slug> Emit sanitized JSON workspace summary
|
|
33
|
+
devrites-engine parallel <subcommand> Deterministic parallel worktree lease/create/integrate/cleanup
|
|
27
34
|
devrites-engine state resolve <qid> "<ans>" Resolve an open question and update state atomically
|
|
28
35
|
devrites-engine state close <slug> Archive a shipped feature and clear ACTIVE
|
|
29
36
|
devrites-engine secret-scan [--staged] [--stdin] [slug] Scan exact staged blobs, stdin, or touched files; HIGH blocks
|
|
@@ -71,7 +78,11 @@ func run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
|
|
|
71
78
|
case "uninstall":
|
|
72
79
|
return install.Run(args[1:], stdout, stderr, install.ModeUninstall)
|
|
73
80
|
case "check":
|
|
74
|
-
return cmdCheck(args[1:], stdout, stderr)
|
|
81
|
+
return cmdCheck(root, args[1:], stdin, stdout, stderr)
|
|
82
|
+
case "parallel":
|
|
83
|
+
return parallel.Run("parallel", args[1:], stdin, stdout, stderr)
|
|
84
|
+
case "observe":
|
|
85
|
+
return cmdObserve(root, args[1:], stdout, stderr)
|
|
75
86
|
case "state":
|
|
76
87
|
return cmdState(root, args[1:], stdout, stderr)
|
|
77
88
|
case "secret-scan":
|
|
@@ -88,9 +99,9 @@ func run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
|
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
|
|
91
|
-
func cmdCheck(args []string, stdout, stderr io.Writer) int {
|
|
102
|
+
func cmdCheck(root string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
|
|
92
103
|
if len(args) == 0 {
|
|
93
|
-
fmt.Fprintln(stderr, "usage: devrites-engine check <candidate|readiness|seal> ...")
|
|
104
|
+
fmt.Fprintln(stderr, "usage: devrites-engine check <candidate|readiness|seal|path-disjoint|task-graph|skill-trust> ...")
|
|
94
105
|
return exitUsage
|
|
95
106
|
}
|
|
96
107
|
sub, rest := args[0], args[1:]
|
|
@@ -99,6 +110,12 @@ func cmdCheck(args []string, stdout, stderr io.Writer) int {
|
|
|
99
110
|
return cmdCandidate(rest, stdout, stderr)
|
|
100
111
|
case "readiness", "seal":
|
|
101
112
|
return cmdGate(gate.Kind(sub), rest, stdout, stderr)
|
|
113
|
+
case "path-disjoint":
|
|
114
|
+
return parallel.Run("path-disjoint", rest, stdin, stdout, stderr)
|
|
115
|
+
case "task-graph":
|
|
116
|
+
return cmdTaskGraph(root, rest, stdout, stderr)
|
|
117
|
+
case "skill-trust":
|
|
118
|
+
return cmdSkillTrust(rest, stdout, stderr)
|
|
102
119
|
default:
|
|
103
120
|
fmt.Fprintf(stderr, "devrites: unknown check %q\n", sub)
|
|
104
121
|
return exitUsage
|
|
@@ -185,3 +202,55 @@ func cmdGate(kind gate.Kind, args []string, stdout, stderr io.Writer) int {
|
|
|
185
202
|
fmt.Fprint(stdout, result.Render())
|
|
186
203
|
return exitOK
|
|
187
204
|
}
|
|
205
|
+
|
|
206
|
+
func cmdTaskGraph(root string, args []string, stdout, stderr io.Writer) int {
|
|
207
|
+
if len(args) != 1 {
|
|
208
|
+
fmt.Fprintln(stderr, "usage: devrites-engine check task-graph <slug>")
|
|
209
|
+
return exitUsage
|
|
210
|
+
}
|
|
211
|
+
if root == "" {
|
|
212
|
+
var err error
|
|
213
|
+
root, err = state.ResolveRoot(os.Getenv("DEVRITES_ROOT"))
|
|
214
|
+
if err != nil {
|
|
215
|
+
fmt.Fprintf(stderr, "devrites: %v\n", err)
|
|
216
|
+
return exitUsage
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return lib.RunTaskGraphCheck(root, args[0], stdout, stderr)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
func cmdSkillTrust(args []string, stdout, stderr io.Writer) int {
|
|
223
|
+
if len(args) != 1 {
|
|
224
|
+
fmt.Fprintln(stderr, "usage: devrites-engine check skill-trust <path>")
|
|
225
|
+
return exitUsage
|
|
226
|
+
}
|
|
227
|
+
return lib.RunSkillTrustCheck(args[0], stdout, stderr)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
func cmdObserve(root string, args []string, stdout, stderr io.Writer) int {
|
|
231
|
+
if len(args) == 0 {
|
|
232
|
+
fmt.Fprintln(stderr, "usage: devrites-engine observe summary <slug>")
|
|
233
|
+
return exitUsage
|
|
234
|
+
}
|
|
235
|
+
if args[0] != "summary" {
|
|
236
|
+
fmt.Fprintf(stderr, "devrites: unknown observe command %q\n", args[0])
|
|
237
|
+
return exitUsage
|
|
238
|
+
}
|
|
239
|
+
if root == "" {
|
|
240
|
+
var err error
|
|
241
|
+
root, err = state.ResolveRoot(os.Getenv("DEVRITES_ROOT"))
|
|
242
|
+
if err != nil {
|
|
243
|
+
fmt.Fprintf(stderr, "devrites: %v\n", err)
|
|
244
|
+
return exitUsage
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
slug, code, err := lib.ActiveSlug(root, args[1:])
|
|
248
|
+
if err != nil {
|
|
249
|
+
fmt.Fprintf(stderr, "observe: %v\n", err)
|
|
250
|
+
if code == 0 {
|
|
251
|
+
code = exitUsage
|
|
252
|
+
}
|
|
253
|
+
return code
|
|
254
|
+
}
|
|
255
|
+
return lib.RunObserveSummary(root, slug, stdout, stderr)
|
|
256
|
+
}
|
|
@@ -126,7 +126,7 @@ func TestNestedCommandFamilyUsageListsOnlyRetainedCommands(t *testing.T) {
|
|
|
126
126
|
want string
|
|
127
127
|
removed []string
|
|
128
128
|
}{
|
|
129
|
-
{args: []string{"check"}, want: "check <candidate|readiness|seal>", removed: []string{"spec"}},
|
|
129
|
+
{args: []string{"check"}, want: "check <candidate|readiness|seal|path-disjoint|task-graph|skill-trust>", removed: []string{"spec"}},
|
|
130
130
|
{args: []string{"state"}, want: "state <resolve|close>", removed: []string{"clarify", "tick-afk", "recovery"}},
|
|
131
131
|
} {
|
|
132
132
|
t.Run(test.args[0], func(t *testing.T) {
|
|
@@ -13,31 +13,34 @@ import (
|
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
func TestADR0004RequiredSectionsAreAdditiveDownTheArc(t *testing.T) {
|
|
16
|
-
|
|
17
|
-
for _,
|
|
18
|
-
|
|
19
|
-
|
|
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(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
for
|
|
29
|
-
if !
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
|
17
|
-
t.Fatalf("planning alias=(
|
|
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 _,
|
|
22
|
-
if
|
|
23
|
-
t.Fatalf("phase %q has no transition right",
|
|
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[
|
|
26
|
-
t.Fatalf("phases %q and %q share transition right %q", prior,
|
|
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[
|
|
30
|
+
rights[policy.TransitionRight] = policy.Target
|
|
29
31
|
}
|
|
30
32
|
}
|