devrites 7.2.3 → 7.2.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.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/engine/internal/gate/gate.go +6 -0
- package/engine/internal/gate/workflow_only.go +335 -0
- package/engine/internal/state/taskgraph.go +19 -4
- package/pack/.claude/agents/devrites-plan-drafter.md +5 -1
- package/pack/.claude/agents/devrites-plan-reviewer.md +7 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
- package/pack/.claude/skills/rite-define/SKILL.md +7 -0
- package/pack/.claude/skills/rite-define/reference/plan-template.md +6 -0
- package/pack/.claude/skills/rite-vet/SKILL.md +9 -2
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +7 -0
- package/pack/generated/claude/agents/devrites-plan-drafter.md +5 -1
- package/pack/generated/claude/agents/devrites-plan-reviewer.md +7 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
- package/pack/generated/claude/skills/rite-define/SKILL.md +7 -0
- package/pack/generated/claude/skills/rite-define/reference/plan-template.md +6 -0
- package/pack/generated/claude/skills/rite-vet/SKILL.md +9 -2
- package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +7 -0
- package/pack/generated/codex/agents/devrites-plan-drafter.toml +5 -1
- package/pack/generated/codex/agents/devrites-plan-reviewer.toml +7 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
- package/pack/generated/codex/skills/rite-define/SKILL.md +7 -0
- package/pack/generated/codex/skills/rite-define/reference/plan-template.md +6 -0
- package/pack/generated/codex/skills/rite-vet/SKILL.md +9 -2
- package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +7 -0
- package/pack/generated/devin/agents/devrites-plan-drafter.md +5 -1
- package/pack/generated/devin/agents/devrites-plan-reviewer.md +7 -0
- package/pack/generated/devin/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
- package/pack/generated/devin/skills/rite-define/SKILL.md +7 -0
- package/pack/generated/devin/skills/rite-define/reference/plan-template.md +6 -0
- package/pack/generated/devin/skills/rite-vet/SKILL.md +9 -2
- package/pack/generated/devin/skills/rite-vet/reference/artifacts.md +7 -0
- package/pack/generated/dsh/agents/devrites-plan-drafter.md +5 -1
- package/pack/generated/dsh/agents/devrites-plan-reviewer.md +7 -0
- package/pack/generated/dsh/runtime/cordis.patch.yml +2 -2
- package/pack/generated/dsh/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
- package/pack/generated/dsh/skills/rite-define/SKILL.md +7 -0
- package/pack/generated/dsh/skills/rite-define/reference/plan-template.md +6 -0
- package/pack/generated/dsh/skills/rite-vet/SKILL.md +9 -2
- package/pack/generated/dsh/skills/rite-vet/reference/artifacts.md +7 -0
- package/pack/generated/omp/agents/devrites-plan-drafter.md +5 -1
- package/pack/generated/omp/agents/devrites-plan-reviewer.md +7 -0
- package/pack/generated/omp/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
- package/pack/generated/omp/skills/rite-define/SKILL.md +7 -0
- package/pack/generated/omp/skills/rite-define/reference/plan-template.md +6 -0
- package/pack/generated/omp/skills/rite-vet/SKILL.md +9 -2
- package/pack/generated/omp/skills/rite-vet/reference/artifacts.md +7 -0
- package/pack/generated/pi/agents/devrites-plan-drafter.md +5 -1
- package/pack/generated/pi/agents/devrites-plan-reviewer.md +7 -0
- package/pack/generated/pi/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
- package/pack/generated/pi/skills/rite-define/SKILL.md +7 -0
- package/pack/generated/pi/skills/rite-define/reference/plan-template.md +6 -0
- package/pack/generated/pi/skills/rite-vet/SKILL.md +9 -2
- package/pack/generated/pi/skills/rite-vet/reference/artifacts.md +7 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to DevRites are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and DevRites adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Releases are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from Conventional Commits on `main`.
|
|
4
4
|
|
|
5
|
+
## [7.2.4](https://github.com/ViktorsBaikers/DevRites/compare/v7.2.3...v7.2.4) (2026-10-30)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
* **devrites:** support workflow-only Define and Vet ([#188](https://github.com/ViktorsBaikers/DevRites/issues/188)) ([51dcaf3](https://github.com/ViktorsBaikers/DevRites/commit/51dcaf3197e6abdb5987aec1a6da7de0cc71238f))
|
|
10
|
+
|
|
5
11
|
## [7.2.3](https://github.com/ViktorsBaikers/DevRites/compare/v7.2.2...v7.2.3) (2026-10-29)
|
|
6
12
|
|
|
7
13
|
### Fixed
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ history can't lose it. After you clear the conversation, switch hosts, or hand
|
|
|
31
31
|
the feature to another engineer, the next session reads the same plan,
|
|
32
32
|
decisions, and evidence before it continues.
|
|
33
33
|
|
|
34
|
-
**Status:** [`v7.2.
|
|
34
|
+
**Status:** [`v7.2.4`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v7.2.4): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
|
|
35
35
|
|
|
36
36
|
This is the latest published release; `main` may contain unreleased work.
|
|
37
37
|
|
|
@@ -154,6 +154,12 @@ func checkObservation(kind Kind, observation *state.WorkspaceObservation) (*Resu
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
+
if len(missingFiles) == 0 {
|
|
158
|
+
for _, problem := range workflowOnlyProblems(observation, policy) {
|
|
159
|
+
stateProblems = append(stateProblems, "workflow-only: "+problem)
|
|
160
|
+
blocked = true
|
|
161
|
+
}
|
|
162
|
+
}
|
|
157
163
|
if len(missingFiles) == 0 {
|
|
158
164
|
for _, problem := range acceptanceMapProblems(observation, policy) {
|
|
159
165
|
stateProblems = append(stateProblems, "acceptance-map: "+problem)
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
package gate
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"math"
|
|
6
|
+
"path"
|
|
7
|
+
"regexp"
|
|
8
|
+
"strconv"
|
|
9
|
+
"strings"
|
|
10
|
+
|
|
11
|
+
"github.com/devrites/devrites/internal/markdowntext"
|
|
12
|
+
"github.com/devrites/devrites/internal/state"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
var workflowBehaviorRE = regexp.MustCompile(`\bWA-BEH-[A-Z0-9][A-Z0-9-]*\b`)
|
|
16
|
+
var workflowModeRE = regexp.MustCompile(`^0[0-7]{3}$`)
|
|
17
|
+
var workflowReviewedIdentityRE = regexp.MustCompile(`^Workflow Artifact reviewed identity: [0-9a-f]{64}$`)
|
|
18
|
+
var workflowEvidenceRE = regexp.MustCompile(`^[a-z][a-z0-9_]*$`)
|
|
19
|
+
var workflowActionRE = regexp.MustCompile(`^/rite-[a-z0-9-]+$`)
|
|
20
|
+
var workflowPlaceholderRE = regexp.MustCompile(`(?i)^(?:todo|tbd|placeholder|<[^>]+>)$`)
|
|
21
|
+
|
|
22
|
+
// workflowOnlyProblems checks planning authority, not executable bytes or runtime
|
|
23
|
+
// safety. Native preflight still owns no-follow source/target and content checks.
|
|
24
|
+
func workflowOnlyProblems(observation *state.WorkspaceObservation, policy state.PhasePolicy) []string {
|
|
25
|
+
tasks, ok := observation.Fact("tasks.md")
|
|
26
|
+
if !ok || !phaseRequiresTasks(policy) || !state.ParseTaskGraph(tasks.Bytes()).WorkflowOnly {
|
|
27
|
+
return nil
|
|
28
|
+
}
|
|
29
|
+
spec, _ := observation.Fact("spec.md")
|
|
30
|
+
ids := state.ParseAcceptanceMap(spec.Bytes(), nil, nil, false, false).SpecIDs
|
|
31
|
+
var problems []string
|
|
32
|
+
var refs map[string]bool
|
|
33
|
+
if phaseRequiresTestPlan(policy) {
|
|
34
|
+
plan, _ := observation.Fact("test-plan.md")
|
|
35
|
+
var err error
|
|
36
|
+
refs, err = workflowAdmission(plan.Bytes(), observation.Slug())
|
|
37
|
+
if err != nil {
|
|
38
|
+
problems = append(problems, err.Error())
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for _, name := range []state.ArtifactPath{"tasks.md", "test-plan.md"} {
|
|
42
|
+
if name == "test-plan.md" && !phaseRequiresTestPlan(policy) {
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
fact, _ := observation.Fact(name)
|
|
46
|
+
structural, _ := markdowntext.Structural(fact.Bytes())
|
|
47
|
+
mapped := map[string]bool{}
|
|
48
|
+
for _, line := range strings.Split(string(structural), "\n") {
|
|
49
|
+
_, cells, table := workflowTableCells(line)
|
|
50
|
+
if !table || len(cells) != 2 {
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
id := strings.TrimSpace(cells[0])
|
|
54
|
+
if id == "" || specACTokenRE.FindString(id) != id {
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
mapped[id] = true
|
|
58
|
+
target := strings.Trim(strings.TrimSpace(cells[1]), "`")
|
|
59
|
+
if target == "" || workflowBehaviorRE.FindString(target) != target || (phaseRequiresTestPlan(policy) && !refs[target]) {
|
|
60
|
+
problems = append(problems, fmt.Sprintf("%s maps %s to invalid or unadmitted Workflow Artifact behavior %q", name, id, target))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for _, id := range ids {
|
|
64
|
+
if !mapped[id] {
|
|
65
|
+
problems = append(problems, fmt.Sprintf("%s needs an AC-to-Workflow-Artifact row | %s | WA-BEH-ID | naming an admitted behavior", name, id))
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return problems
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var workflowAdmissionFields = strings.Fields("active_slug readiness_binding_command return_phase return_next_action target_order target_count_limit per_target_bytes_limit aggregate_bytes_limit transaction_file_limit diagnostic_bytes_limit journal_line_limit attempt_epoch_limit proof_command_timeout_seconds proof_aggregate_timeout_seconds proof_terminate_grace_seconds")
|
|
73
|
+
var workflowTargetColumns = strings.Split("Index|Path|Mode|Behavior ref|Interface ref|Positive fixture|Failure fixtures|Proof command|Proof cwd|Proof signal|Rollback|Evidence fields", "|")
|
|
74
|
+
|
|
75
|
+
func workflowAdmission(raw []byte, slug string) (map[string]bool, error) {
|
|
76
|
+
fail := func() (map[string]bool, error) {
|
|
77
|
+
return nil, fmt.Errorf("workflow artifact admission is missing or malformed; repair in /rite-plan and rerun /rite-vet")
|
|
78
|
+
}
|
|
79
|
+
structural, err := markdowntext.Structural(raw)
|
|
80
|
+
if err != nil {
|
|
81
|
+
return fail()
|
|
82
|
+
}
|
|
83
|
+
text := strings.ReplaceAll(string(structural), "\r\n", "\n")
|
|
84
|
+
sections := map[string][]string{}
|
|
85
|
+
current := ""
|
|
86
|
+
for _, line := range strings.Split(text, "\n") {
|
|
87
|
+
if strings.HasPrefix(line, "## ") {
|
|
88
|
+
current = strings.TrimPrefix(line, "## ")
|
|
89
|
+
if _, duplicate := sections[current]; duplicate {
|
|
90
|
+
return fail()
|
|
91
|
+
}
|
|
92
|
+
sections[current] = nil
|
|
93
|
+
} else if current != "" && strings.TrimSpace(line) != "" {
|
|
94
|
+
sections[current] = append(sections[current], line)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
block := sections["Workflow Artifact admission"]
|
|
98
|
+
if len(block) == 0 || block[0] != "DevRites contract: devrites.workflow-artifact-admission.v1" {
|
|
99
|
+
return fail()
|
|
100
|
+
}
|
|
101
|
+
fields, rest, ok := workflowFields(block[1:], workflowAdmissionFields)
|
|
102
|
+
if !ok || fields["active_slug"] != slug || fields["readiness_binding_command"] != "devrites-engine check readiness --emit-binding "+slug || fields["target_order"] != "utf8-bytewise-path-ascending" {
|
|
103
|
+
return fail()
|
|
104
|
+
}
|
|
105
|
+
if _, ok := state.PolicyFor(state.Phase(fields["return_phase"])); !ok {
|
|
106
|
+
return fail()
|
|
107
|
+
}
|
|
108
|
+
action, actionSlug, ok := strings.Cut(fields["return_next_action"], " ")
|
|
109
|
+
if !ok || !workflowActionRE.MatchString(action) || actionSlug != slug {
|
|
110
|
+
return fail()
|
|
111
|
+
}
|
|
112
|
+
bounds := map[string]uint64{}
|
|
113
|
+
for _, key := range workflowAdmissionFields[5:] {
|
|
114
|
+
value := fields[key]
|
|
115
|
+
if value == "" || strings.Trim(value, "0123456789") != "" {
|
|
116
|
+
return fail()
|
|
117
|
+
}
|
|
118
|
+
n, err := strconv.ParseUint(value, 10, 64)
|
|
119
|
+
if err != nil || n == 0 {
|
|
120
|
+
return fail()
|
|
121
|
+
}
|
|
122
|
+
bounds[key] = n
|
|
123
|
+
}
|
|
124
|
+
count, epochs := bounds["target_count_limit"], bounds["attempt_epoch_limit"]
|
|
125
|
+
if count > (math.MaxUint64-6)/3 || epochs < 3 || epochs > math.MaxUint64-30 || count > math.MaxUint64-30-epochs || bounds["transaction_file_limit"] < 3*count+6 || bounds["diagnostic_bytes_limit"] != 256 || bounds["journal_line_limit"] < 30+count+epochs || bounds["journal_line_limit"] > 280 || bounds["proof_command_timeout_seconds"] <= bounds["proof_terminate_grace_seconds"] || bounds["proof_aggregate_timeout_seconds"] < bounds["proof_command_timeout_seconds"] {
|
|
126
|
+
return fail()
|
|
127
|
+
}
|
|
128
|
+
if len(rest) < 3 || !workflowHeader(rest[0], workflowTargetColumns) || !delimiterRow(rest[1]) {
|
|
129
|
+
return fail()
|
|
130
|
+
}
|
|
131
|
+
rows := rest[2:]
|
|
132
|
+
if workflowReviewedIdentityRE.MatchString(rows[len(rows)-1]) {
|
|
133
|
+
rows = rows[:len(rows)-1]
|
|
134
|
+
}
|
|
135
|
+
if len(rows) == 0 {
|
|
136
|
+
return fail()
|
|
137
|
+
}
|
|
138
|
+
if uint64(len(rows)) > count {
|
|
139
|
+
return fail()
|
|
140
|
+
}
|
|
141
|
+
refs := map[string]bool{}
|
|
142
|
+
previous := ""
|
|
143
|
+
for i, line := range rows {
|
|
144
|
+
row, ok := workflowCells(line, len(workflowTargetColumns))
|
|
145
|
+
if !ok || row[0] != fmt.Sprintf("%08x", i) || !workflowTargetPath(row[1]) || row[1] <= previous || !workflowModeRE.MatchString(row[2]) {
|
|
146
|
+
return fail()
|
|
147
|
+
}
|
|
148
|
+
previous = row[1]
|
|
149
|
+
if !workflowSingleCommand(row[7]) || (row[8] != "repository-root" && row[8] != "active-workspace") || len(row[9]) > 128 || row[10] != "restore-preimage-or-absence" {
|
|
150
|
+
return fail()
|
|
151
|
+
}
|
|
152
|
+
for _, c := range row[9] {
|
|
153
|
+
if c < 32 || c > 126 {
|
|
154
|
+
return fail()
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
seen := map[string]bool{}
|
|
158
|
+
for _, field := range strings.Split(row[11], ",") {
|
|
159
|
+
field = strings.TrimSpace(field)
|
|
160
|
+
if !workflowEvidenceRE.MatchString(field) || seen[field] {
|
|
161
|
+
return fail()
|
|
162
|
+
}
|
|
163
|
+
seen[field] = true
|
|
164
|
+
}
|
|
165
|
+
referenceIDs := []string{row[3], row[4], row[5]}
|
|
166
|
+
referenceIDs = append(referenceIDs, strings.Split(row[6], ",")...)
|
|
167
|
+
for j, id := range referenceIDs {
|
|
168
|
+
id = strings.TrimSpace(id)
|
|
169
|
+
kind, prefix, keys := "failure-fixture", "WA-FIX-F", strings.Fields("setup fault expected")
|
|
170
|
+
switch j {
|
|
171
|
+
case 0:
|
|
172
|
+
kind, prefix, keys = "behavior", "WA-BEH-", strings.Fields("success observable_effect")
|
|
173
|
+
case 1:
|
|
174
|
+
kind, prefix, keys = "interface", "WA-IF-", strings.Fields("inputs invariants ordering errors configuration performance")
|
|
175
|
+
case 2:
|
|
176
|
+
kind, prefix, keys = "positive-fixture", "WA-FIX-P", strings.Fields("setup action expected")
|
|
177
|
+
}
|
|
178
|
+
suffix := strings.TrimPrefix(id, prefix)
|
|
179
|
+
if suffix == id || suffix == "" || strings.Trim(suffix, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-") != "" || suffix[0] == '-' || refs[id] {
|
|
180
|
+
return fail()
|
|
181
|
+
}
|
|
182
|
+
ref := sections[id]
|
|
183
|
+
if len(ref) == 0 || ref[0] != "DevRites workflow reference: "+kind {
|
|
184
|
+
return fail()
|
|
185
|
+
}
|
|
186
|
+
_, extra, ok := workflowFields(ref[1:], keys)
|
|
187
|
+
if !ok || len(extra) != 0 {
|
|
188
|
+
return fail()
|
|
189
|
+
}
|
|
190
|
+
refs[id] = true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return refs, nil
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
func workflowHeader(line string, columns []string) bool {
|
|
197
|
+
_, cells, ok := workflowTableCells(line)
|
|
198
|
+
if !ok || len(cells) != len(columns) {
|
|
199
|
+
return false
|
|
200
|
+
}
|
|
201
|
+
for i := range cells {
|
|
202
|
+
if strings.TrimSpace(cells[i]) != columns[i] {
|
|
203
|
+
return false
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return true
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
func workflowFields(lines, keys []string) (map[string]string, []string, bool) {
|
|
210
|
+
if len(lines) < len(keys)+2 || !workflowHeader(lines[0], []string{"Field", "Value"}) || !delimiterRow(lines[1]) {
|
|
211
|
+
return nil, nil, false
|
|
212
|
+
}
|
|
213
|
+
fields := map[string]string{}
|
|
214
|
+
for i, key := range keys {
|
|
215
|
+
_, cells, ok := workflowTableCells(lines[i+2])
|
|
216
|
+
if !ok || len(cells) != 2 || strings.TrimSpace(cells[0]) != key {
|
|
217
|
+
return nil, nil, false
|
|
218
|
+
}
|
|
219
|
+
value, ok := workflowCell(cells[1])
|
|
220
|
+
if !ok {
|
|
221
|
+
return nil, nil, false
|
|
222
|
+
}
|
|
223
|
+
fields[key] = value
|
|
224
|
+
}
|
|
225
|
+
return fields, lines[len(keys)+2:], true
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
func workflowCells(line string, count int) ([]string, bool) {
|
|
229
|
+
_, cells, ok := workflowTableCells(line)
|
|
230
|
+
if !ok || len(cells) != count {
|
|
231
|
+
return nil, false
|
|
232
|
+
}
|
|
233
|
+
for i := range cells {
|
|
234
|
+
cells[i], ok = workflowCell(cells[i])
|
|
235
|
+
if !ok {
|
|
236
|
+
return nil, false
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return cells, true
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
func workflowCell(raw string) (string, bool) {
|
|
243
|
+
raw = strings.TrimSpace(raw)
|
|
244
|
+
if len(raw) < 3 || raw[0] != '`' || raw[len(raw)-1] != '`' {
|
|
245
|
+
return "", false
|
|
246
|
+
}
|
|
247
|
+
value := raw[1 : len(raw)-1]
|
|
248
|
+
return value, strings.TrimSpace(value) != "" && !strings.ContainsAny(value, "`\r\n") && !workflowPlaceholderRE.MatchString(value)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// This checks command shape only; native preflight still owns execution safety.
|
|
252
|
+
func workflowSingleCommand(command string) bool {
|
|
253
|
+
var quote byte
|
|
254
|
+
for i := 0; i < len(command); i++ {
|
|
255
|
+
c := command[i]
|
|
256
|
+
if c == '\r' || c == '\n' {
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
259
|
+
if quote == '\'' {
|
|
260
|
+
if c == quote {
|
|
261
|
+
quote = 0
|
|
262
|
+
}
|
|
263
|
+
continue
|
|
264
|
+
}
|
|
265
|
+
if c == '\\' {
|
|
266
|
+
if i+1 == len(command) {
|
|
267
|
+
return false
|
|
268
|
+
}
|
|
269
|
+
if quote == 0 || strings.ContainsRune("$`\"\\", rune(command[i+1])) {
|
|
270
|
+
i++
|
|
271
|
+
}
|
|
272
|
+
continue
|
|
273
|
+
}
|
|
274
|
+
// shortcut: substitutions reject even literal operators; use a shell parser if needed.
|
|
275
|
+
if c == '$' && i+1 < len(command) && command[i+1] == '(' && strings.ContainsAny(command, ";|&") {
|
|
276
|
+
return false
|
|
277
|
+
}
|
|
278
|
+
if quote != 0 {
|
|
279
|
+
if c == quote {
|
|
280
|
+
quote = 0
|
|
281
|
+
}
|
|
282
|
+
} else if c == '\'' || c == '"' {
|
|
283
|
+
quote = c
|
|
284
|
+
} else if strings.ContainsRune(";|&", rune(c)) {
|
|
285
|
+
return false
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return quote == 0
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
func workflowTargetPath(value string) bool {
|
|
292
|
+
if value == "." || path.IsAbs(value) || path.Clean(value) != value || strings.ContainsAny(value, "\\:*?[]{}|\t\r\n") {
|
|
293
|
+
return false
|
|
294
|
+
}
|
|
295
|
+
parts := strings.Split(value, "/")
|
|
296
|
+
for _, part := range parts {
|
|
297
|
+
if part == ".." || strings.HasPrefix(part, ".") || part == "node_modules" || part == "vendor" {
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Canonical workspace ledgers and cross-workspace roots are not executable targets.
|
|
302
|
+
return parts[0] != "work" && parts[0] != "archive" && !strings.HasSuffix(value, "/") && !strings.HasSuffix(value, ".md")
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Admission escaping counts the whole backslash run; other Markdown tables do not.
|
|
306
|
+
func workflowTableCells(line string) (string, []string, bool) {
|
|
307
|
+
line = strings.TrimSpace(line)
|
|
308
|
+
if len(line) < 2 || line[0] != '|' || line[len(line)-1] != '|' {
|
|
309
|
+
return "", nil, false
|
|
310
|
+
}
|
|
311
|
+
var cells []string
|
|
312
|
+
var cell strings.Builder
|
|
313
|
+
backslashes := 0
|
|
314
|
+
for _, c := range line[1:] {
|
|
315
|
+
if c == '|' {
|
|
316
|
+
if backslashes%2 == 1 {
|
|
317
|
+
value := cell.String()
|
|
318
|
+
cell.Reset()
|
|
319
|
+
cell.WriteString(value[:len(value)-1])
|
|
320
|
+
cell.WriteRune(c)
|
|
321
|
+
} else {
|
|
322
|
+
cells = append(cells, cell.String())
|
|
323
|
+
cell.Reset()
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
cell.WriteRune(c)
|
|
327
|
+
}
|
|
328
|
+
if c == '\\' {
|
|
329
|
+
backslashes++
|
|
330
|
+
} else {
|
|
331
|
+
backslashes = 0
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return "", cells, cell.Len() == 0
|
|
335
|
+
}
|
|
@@ -21,10 +21,11 @@ type TaskSlice struct {
|
|
|
21
21
|
|
|
22
22
|
// TaskGraphResult is the deterministic outcome of parsing a tasks.md slice graph.
|
|
23
23
|
type TaskGraphResult struct {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
WorkflowOnly bool // explicit work scope, never inferred from an empty graph
|
|
25
|
+
Slices []TaskSlice
|
|
26
|
+
Cycle []string // empty when acyclic
|
|
27
|
+
Unknown []string // dependency ids with no defining slice
|
|
28
|
+
Problems []string // human-readable blockers
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
var (
|
|
@@ -72,6 +73,20 @@ func ParseTaskGraph(tasksMarkdown []byte) TaskGraphResult {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
headers := sliceHeaderRE.FindAllStringSubmatchIndex(text, -1)
|
|
76
|
+
markers := 0
|
|
77
|
+
for _, line := range strings.Split(strings.ReplaceAll(text, "\r\n", "\n"), "\n") {
|
|
78
|
+
if line == "Work scope: workflow-only" {
|
|
79
|
+
markers++
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if markers > 0 {
|
|
83
|
+
if markers != 1 || len(headers) != 0 {
|
|
84
|
+
result.Problems = append(result.Problems, "workflow-only scope requires exactly one marker and zero SLICE-### sections")
|
|
85
|
+
return result
|
|
86
|
+
}
|
|
87
|
+
result.WorkflowOnly = true
|
|
88
|
+
return result
|
|
89
|
+
}
|
|
75
90
|
if len(headers) == 0 {
|
|
76
91
|
result.Problems = append(result.Problems, "no SLICE-### sections found")
|
|
77
92
|
return result
|
|
@@ -23,7 +23,11 @@ seams supplied by the orchestrator.
|
|
|
23
23
|
Reuse existing seams and dependencies before proposing new ones. Per
|
|
24
24
|
`.claude/skills/devrites-lib/reference/standards/code-navigation.md`, map seams with `get_architecture`, find reuse
|
|
25
25
|
with `search_graph`, and size blast radius with `trace_path` (`direction: "inbound"`). Build vertical
|
|
26
|
-
slices in dependency order
|
|
26
|
+
slices in dependency order for product work. For explicit workflow-only scope,
|
|
27
|
+
follow [workflow-artifacts.md](../skills/devrites-lib/reference/standards/workflow-artifacts.md#workflow-only-planning):
|
|
28
|
+
map every REQ/AC to WA behavior and proof, retain zero product slices and empty
|
|
29
|
+
product candidate, never fabricate slices. Product slice rules below apply only
|
|
30
|
+
to product work; put a hub file's shared edit in one seam slice
|
|
27
31
|
([slicing.md](../skills/rite-plan/reference/slicing.md#slice-independence)). Map every REQ and AC to a slice and executable proof,
|
|
28
32
|
name rollback and failure paths; keep acceptance-changing proposals separate. Each proof
|
|
29
33
|
row quotes the spec line or standard rule it proves (`Proves`); proof that proves none
|
|
@@ -32,6 +32,13 @@ Per `.claude/skills/devrites-lib/reference/standards/code-navigation.md`, check
|
|
|
32
32
|
placement with `get_architecture`, reuse with `search_graph`, and blast radius with
|
|
33
33
|
`trace_path` (`direction: "inbound"`); fall back to Read/Grep/Glob.
|
|
34
34
|
|
|
35
|
+
For explicit workflow-only tasks, also read `test-plan.md` and follow
|
|
36
|
+
[workflow-artifacts.md](../skills/devrites-lib/reference/standards/workflow-artifacts.md#workflow-only-planning).
|
|
37
|
+
Review REQ/AC → admitted WA behavior/proof, admission completeness and root ownership
|
|
38
|
+
instead of product slice writer/closure checks. Zero product slices and empty
|
|
39
|
+
candidate are valid; do not demand fabricated slices or Build dispatch. Apply
|
|
40
|
+
all seven dimensions to the workflow behaviors and proof.
|
|
41
|
+
|
|
35
42
|
## Score the seven dimensions
|
|
36
43
|
For each dimension, **cite the evidence first**, including an absent plan or spec
|
|
37
44
|
line, and then assign the band. Do not choose a score and justify it afterward:
|
|
@@ -4,9 +4,37 @@
|
|
|
4
4
|
|
|
5
5
|
> Applies when: a plan declares root-authored executable workflow files.
|
|
6
6
|
|
|
7
|
-
Workflow Artifact plans/isolates/proves; never enters product candidate
|
|
8
|
-
built count
|
|
9
|
-
|
|
7
|
+
Workflow Artifact plans/isolates/proves; never enters the product candidate or
|
|
8
|
+
built count and never authorizes consumption. Workspace planning readiness binds
|
|
9
|
+
its admission and proof references, not executable bytes as product changes.
|
|
10
|
+
Module owns semantics; callers retain only link and entry/return.
|
|
11
|
+
|
|
12
|
+
## Workflow-only planning
|
|
13
|
+
|
|
14
|
+
For exclusively root-owned executable workflow work, put the exact standalone
|
|
15
|
+
line `Work scope: workflow-only` outside fences in `tasks.md`, with zero
|
|
16
|
+
`## SLICE-###` sections. Empty tasks never imply this scope; mixed marker plus
|
|
17
|
+
product slices is invalid. Keep ordinary spec AC IDs. In both `tasks.md` and
|
|
18
|
+
`test-plan.md`, map every AC in two-cell rows `| AC-001 | WA-BEH-ID |` (backticks
|
|
19
|
+
around the behavior ID are optional); `traceability.md` maps REQ/AC → that same
|
|
20
|
+
behavior → proof → evidence. Define may name the planned behaviors before Vet
|
|
21
|
+
admits them; Vet requires every mapped behavior in the admission below.
|
|
22
|
+
|
|
23
|
+
Use the existing empty product representation in `touched-files.md`:
|
|
24
|
+
`## Candidate manifest`, then `No project files.`, plus required `## Touched files`.
|
|
25
|
+
Do not add workflow targets to the product manifest or invent slices. Define
|
|
26
|
+
retains architecture/plan/tasks/traceability; Vet checks Workflow Artifact
|
|
27
|
+
admission instead of product slice writer/closure checks. No Build/wright dispatch,
|
|
28
|
+
built checkpoint, or ACTIVE switch. Product and mixed product work keep ordinary
|
|
29
|
+
slice contracts; product writers still cannot write `.devrites`.
|
|
30
|
+
|
|
31
|
+
Readiness at Vet and later checks admission structure, bounds, workspace-relative
|
|
32
|
+
paths, ownership namespace, and referenced behavior/interface/fixtures. Ordinary
|
|
33
|
+
`Readiness inputs SHA-256` binds current tasks and test-plan; editing admission or
|
|
34
|
+
references makes READY stale. Do not embed that digest into its own bound inputs.
|
|
35
|
+
This planning check does not open targets or authorize execution: native preflight
|
|
36
|
+
still validates resolved paths, symlinks, trusted bytes, content limits and all
|
|
37
|
+
transaction safety before mutation.
|
|
10
38
|
|
|
11
39
|
## Contents
|
|
12
40
|
|
|
@@ -72,12 +100,14 @@ configuration,performance`; positive `setup,action,expected`; failure
|
|
|
72
100
|
mutation reject placeholders, missing/extra/reordered fields, duplicates, and
|
|
73
101
|
referenced-empty blocks.
|
|
74
102
|
|
|
75
|
-
Proof command=one unchanged trusted Vet-approved repository command; exclude
|
|
76
|
-
|
|
103
|
+
Proof command=one unchanged trusted Vet-approved repository command; exclude newline
|
|
104
|
+
and unquoted/unescaped `;|&`. Planning permits quoted/escaped operators;
|
|
105
|
+
substitutions reject all `;|&` data. Cwd=`repository-root` or
|
|
77
106
|
`active-workspace`. Signal=fixed printable ASCII without CR/LF, 1..128 bytes.
|
|
78
107
|
Rollback=`restore-preimage-or-absence`. Evidence=unique comma-separated lowercase
|
|
79
|
-
identifiers. `return_phase`=lifecycle phase;
|
|
80
|
-
|
|
108
|
+
identifiers. `return_phase`=lifecycle phase;
|
|
109
|
+
`return_next_action`=`/rite-[a-z0-9-]+ <active_slug>` exactly.
|
|
110
|
+
Malformed admission/reference routes Plan/Vet before mutation.
|
|
81
111
|
|
|
82
112
|
Checked nonnegative arithmetic rejects overflow. Bounds: rows
|
|
83
113
|
`1..target_count_limit`; per-target/aggregate content; `transaction_file_limit >=
|
|
@@ -34,6 +34,13 @@ Pull these via `Read` when shaping the plan:
|
|
|
34
34
|
- Author one `architecture.md`/`plan.md` section at a time. Before slicing, use
|
|
35
35
|
[`elicitation.md`](../devrites-lib/reference/standards/elicitation.md) Tournament for open
|
|
36
36
|
designs and Delphi for uncertain estimates.
|
|
37
|
+
- **Workflow-only exception.** Follow
|
|
38
|
+
[`workflow-artifacts.md`](../devrites-lib/reference/standards/workflow-artifacts.md#workflow-only-planning)
|
|
39
|
+
before applying product slice rules below: explicit scope, zero product slices,
|
|
40
|
+
existing empty candidate, AC/REQ → WA behavior/proof mapping in tasks, traceability
|
|
41
|
+
and test-plan. Keep every AC; never fabricate a slice or dispatch Build for
|
|
42
|
+
root-owned workflow artifacts. Slice-count, slice-format, writer and cross-slice
|
|
43
|
+
requirements below apply only when product slices exist.
|
|
37
44
|
- **Derive the slice count from the work.**
|
|
38
45
|
One per independently-shippable increment, sized by [`slicing.md`](../rite-plan/reference/slicing.md); map every acceptance
|
|
39
46
|
criterion. User counts are hints: explain honest differences, never pad/compress.
|
|
@@ -40,6 +40,12 @@ State strategy and rationale. For costly/hard-to-reverse boundaries, models, con
|
|
|
40
40
|
or dependencies compare ≥2 `Option · Drivers · Trade-offs · Consequence`.
|
|
41
41
|
|
|
42
42
|
## Slice strategy
|
|
43
|
+
For explicit workflow-only scope, use zero product slices and the existing empty
|
|
44
|
+
candidate manifest (`No project files.` plus `## Touched files`). Map AC/REQ to
|
|
45
|
+
Workflow Artifact behaviors and proof, not invented slices; follow
|
|
46
|
+
[`workflow-artifacts.md`](../../devrites-lib/reference/standards/workflow-artifacts.md#workflow-only-planning).
|
|
47
|
+
The following slice strategy applies to product work.
|
|
48
|
+
|
|
43
49
|
List vertical `SLICE-###` increments, AC coverage, and risk-first order within dependency
|
|
44
50
|
tiers. Wide refactors use expand → green migrate batches → contract, or an integration
|
|
45
51
|
branch + final verify slice.
|
|
@@ -138,7 +138,14 @@ plan declares a root-authored executable workflow file, read
|
|
|
138
138
|
`devrites-engine check preconditions <slug>` (rows with `Run at` `vet`, including each
|
|
139
139
|
`dependency-dry-run` and preserve-baseline row). A failure folds into Plan repair; only an
|
|
140
140
|
owner-owned decision becomes a question, batched under the afk-hitl decision-chain rule.
|
|
141
|
-
4. **Audit readiness.**
|
|
141
|
+
4. **Audit readiness.** For explicit workflow-only tasks, follow
|
|
142
|
+
[`workflow-artifacts.md`](../devrites-lib/reference/standards/workflow-artifacts.md#workflow-only-planning):
|
|
143
|
+
check AC/REQ → admitted WA behavior and executable proof, all admission fields,
|
|
144
|
+
fixtures, bounds and root ownership, not product slice writer/closure checks.
|
|
145
|
+
Keep empty product candidate and zero built slices; no Build/wright dispatch.
|
|
146
|
+
Missing/malformed admission routes Plan; stale admission requires fresh Vet and
|
|
147
|
+
ordinary readiness SHA binding. Never switch ACTIVE for a named workspace.
|
|
148
|
+
Otherwise goal-backward map every requirement, criterion, NFR,
|
|
142
149
|
interaction, edge/prohibition, and decision row to one slice and executable
|
|
143
150
|
proof. Verify UX/spec/architecture alignment, contracts, dependency order,
|
|
144
151
|
slice independence/wiring, prerequisites, failure/observability/rollback, and
|
|
@@ -208,7 +215,7 @@ plan declares a root-authored executable workflow file, read
|
|
|
208
215
|
`marker sweep:` line is recorded in `eng-review.md`; a kept hit blocks READY. Write phase/next step and emit one
|
|
209
216
|
`Readiness inputs SHA-256` with
|
|
210
217
|
`devrites-engine check readiness --emit-binding <slug>`; normal readiness check
|
|
211
|
-
must pass
|
|
218
|
+
must pass; workflow-only checks admission instead. For product work it replays Build-entry `check slice` on every unbuilt slice, so
|
|
212
219
|
READY needs each to pass (fix failures in `/rite-plan`). READY also needs every
|
|
213
220
|
unbuilt slice's write set closed against the live tree (plan-reviewer
|
|
214
221
|
dimension 6), so frontier gaps fold in one Plan repair, not one per slice; an
|
|
@@ -9,6 +9,13 @@ Authority: `.claude/skills/devrites-lib/reference/standards/acceptance-preservin
|
|
|
9
9
|
<!-- include:../../devrites-lib/reference/_shared/reslice-vet.md -->
|
|
10
10
|
<!-- END RESLICE ROUTE-TO-ACTION -->
|
|
11
11
|
|
|
12
|
+
For explicit workflow-only tasks, use
|
|
13
|
+
[`workflow-artifacts.md`](../../devrites-lib/reference/standards/workflow-artifacts.md#workflow-only-planning):
|
|
14
|
+
AC → admitted WA behavior rows in tasks/test-plan; root-owned admission/proof
|
|
15
|
+
instead of product writer/slice rows. Keep readiness SHA binding, empty product
|
|
16
|
+
candidate and built counts unchanged. No Build dispatch. Product templates below
|
|
17
|
+
remain unchanged for product work.
|
|
18
|
+
|
|
12
19
|
## `eng-review.md`: the record
|
|
13
20
|
|
|
14
21
|
Write/update `.devrites/work/<slug>/eng-review.md`; never clobber.
|
|
@@ -23,7 +23,11 @@ seams supplied by the orchestrator.
|
|
|
23
23
|
Reuse existing seams and dependencies before proposing new ones. Per
|
|
24
24
|
`.claude/skills/devrites-lib/reference/standards/code-navigation.md`, map seams with `get_architecture`, find reuse
|
|
25
25
|
with `search_graph`, and size blast radius with `trace_path` (`direction: "inbound"`). Build vertical
|
|
26
|
-
slices in dependency order
|
|
26
|
+
slices in dependency order for product work. For explicit workflow-only scope,
|
|
27
|
+
follow [workflow-artifacts.md](../skills/devrites-lib/reference/standards/workflow-artifacts.md#workflow-only-planning):
|
|
28
|
+
map every REQ/AC to WA behavior and proof, retain zero product slices and empty
|
|
29
|
+
product candidate, never fabricate slices. Product slice rules below apply only
|
|
30
|
+
to product work; put a hub file's shared edit in one seam slice
|
|
27
31
|
([slicing.md](../skills/rite-plan/reference/slicing.md#slice-independence)). Map every REQ and AC to a slice and executable proof,
|
|
28
32
|
name rollback and failure paths; keep acceptance-changing proposals separate. Each proof
|
|
29
33
|
row quotes the spec line or standard rule it proves (`Proves`); proof that proves none
|
|
@@ -38,6 +38,13 @@ Per `.claude/skills/devrites-lib/reference/standards/code-navigation.md`, check
|
|
|
38
38
|
placement with `get_architecture`, reuse with `search_graph`, and blast radius with
|
|
39
39
|
`trace_path` (`direction: "inbound"`); fall back to Read/Grep/Glob.
|
|
40
40
|
|
|
41
|
+
For explicit workflow-only tasks, also read `test-plan.md` and follow
|
|
42
|
+
[workflow-artifacts.md](../skills/devrites-lib/reference/standards/workflow-artifacts.md#workflow-only-planning).
|
|
43
|
+
Review REQ/AC → admitted WA behavior/proof, admission completeness and root ownership
|
|
44
|
+
instead of product slice writer/closure checks. Zero product slices and empty
|
|
45
|
+
candidate are valid; do not demand fabricated slices or Build dispatch. Apply
|
|
46
|
+
all seven dimensions to the workflow behaviors and proof.
|
|
47
|
+
|
|
41
48
|
## Score the seven dimensions
|
|
42
49
|
For each dimension, **cite the evidence first**, including an absent plan or spec
|
|
43
50
|
line, and then assign the band. Do not choose a score and justify it afterward:
|
|
@@ -4,9 +4,37 @@
|
|
|
4
4
|
|
|
5
5
|
> Applies when: a plan declares root-authored executable workflow files.
|
|
6
6
|
|
|
7
|
-
Workflow Artifact plans/isolates/proves; never enters product candidate
|
|
8
|
-
built count
|
|
9
|
-
|
|
7
|
+
Workflow Artifact plans/isolates/proves; never enters the product candidate or
|
|
8
|
+
built count and never authorizes consumption. Workspace planning readiness binds
|
|
9
|
+
its admission and proof references, not executable bytes as product changes.
|
|
10
|
+
Module owns semantics; callers retain only link and entry/return.
|
|
11
|
+
|
|
12
|
+
## Workflow-only planning
|
|
13
|
+
|
|
14
|
+
For exclusively root-owned executable workflow work, put the exact standalone
|
|
15
|
+
line `Work scope: workflow-only` outside fences in `tasks.md`, with zero
|
|
16
|
+
`## SLICE-###` sections. Empty tasks never imply this scope; mixed marker plus
|
|
17
|
+
product slices is invalid. Keep ordinary spec AC IDs. In both `tasks.md` and
|
|
18
|
+
`test-plan.md`, map every AC in two-cell rows `| AC-001 | WA-BEH-ID |` (backticks
|
|
19
|
+
around the behavior ID are optional); `traceability.md` maps REQ/AC → that same
|
|
20
|
+
behavior → proof → evidence. Define may name the planned behaviors before Vet
|
|
21
|
+
admits them; Vet requires every mapped behavior in the admission below.
|
|
22
|
+
|
|
23
|
+
Use the existing empty product representation in `touched-files.md`:
|
|
24
|
+
`## Candidate manifest`, then `No project files.`, plus required `## Touched files`.
|
|
25
|
+
Do not add workflow targets to the product manifest or invent slices. Define
|
|
26
|
+
retains architecture/plan/tasks/traceability; Vet checks Workflow Artifact
|
|
27
|
+
admission instead of product slice writer/closure checks. No Build/wright dispatch,
|
|
28
|
+
built checkpoint, or ACTIVE switch. Product and mixed product work keep ordinary
|
|
29
|
+
slice contracts; product writers still cannot write `.devrites`.
|
|
30
|
+
|
|
31
|
+
Readiness at Vet and later checks admission structure, bounds, workspace-relative
|
|
32
|
+
paths, ownership namespace, and referenced behavior/interface/fixtures. Ordinary
|
|
33
|
+
`Readiness inputs SHA-256` binds current tasks and test-plan; editing admission or
|
|
34
|
+
references makes READY stale. Do not embed that digest into its own bound inputs.
|
|
35
|
+
This planning check does not open targets or authorize execution: native preflight
|
|
36
|
+
still validates resolved paths, symlinks, trusted bytes, content limits and all
|
|
37
|
+
transaction safety before mutation.
|
|
10
38
|
|
|
11
39
|
## Contents
|
|
12
40
|
|
|
@@ -72,12 +100,14 @@ configuration,performance`; positive `setup,action,expected`; failure
|
|
|
72
100
|
mutation reject placeholders, missing/extra/reordered fields, duplicates, and
|
|
73
101
|
referenced-empty blocks.
|
|
74
102
|
|
|
75
|
-
Proof command=one unchanged trusted Vet-approved repository command; exclude
|
|
76
|
-
|
|
103
|
+
Proof command=one unchanged trusted Vet-approved repository command; exclude newline
|
|
104
|
+
and unquoted/unescaped `;|&`. Planning permits quoted/escaped operators;
|
|
105
|
+
substitutions reject all `;|&` data. Cwd=`repository-root` or
|
|
77
106
|
`active-workspace`. Signal=fixed printable ASCII without CR/LF, 1..128 bytes.
|
|
78
107
|
Rollback=`restore-preimage-or-absence`. Evidence=unique comma-separated lowercase
|
|
79
|
-
identifiers. `return_phase`=lifecycle phase;
|
|
80
|
-
|
|
108
|
+
identifiers. `return_phase`=lifecycle phase;
|
|
109
|
+
`return_next_action`=`/rite-[a-z0-9-]+ <active_slug>` exactly.
|
|
110
|
+
Malformed admission/reference routes Plan/Vet before mutation.
|
|
81
111
|
|
|
82
112
|
Checked nonnegative arithmetic rejects overflow. Bounds: rows
|
|
83
113
|
`1..target_count_limit`; per-target/aggregate content; `transaction_file_limit >=
|