devrites 7.2.2 → 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.
Files changed (60) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/dsh.md +8 -2
  4. package/engine/internal/gate/gate.go +6 -0
  5. package/engine/internal/gate/workflow_only.go +335 -0
  6. package/engine/internal/state/taskgraph.go +19 -4
  7. package/pack/.claude/agents/devrites-plan-drafter.md +5 -1
  8. package/pack/.claude/agents/devrites-plan-reviewer.md +7 -0
  9. package/pack/.claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
  10. package/pack/.claude/skills/rite-define/SKILL.md +7 -0
  11. package/pack/.claude/skills/rite-define/reference/plan-template.md +6 -0
  12. package/pack/.claude/skills/rite-vet/SKILL.md +9 -2
  13. package/pack/.claude/skills/rite-vet/reference/artifacts.md +7 -0
  14. package/pack/generated/claude/agents/devrites-plan-drafter.md +5 -1
  15. package/pack/generated/claude/agents/devrites-plan-reviewer.md +7 -0
  16. package/pack/generated/claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
  17. package/pack/generated/claude/skills/rite-define/SKILL.md +7 -0
  18. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +6 -0
  19. package/pack/generated/claude/skills/rite-vet/SKILL.md +9 -2
  20. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +7 -0
  21. package/pack/generated/codex/agents/devrites-plan-drafter.toml +5 -1
  22. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +7 -0
  23. package/pack/generated/codex/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
  24. package/pack/generated/codex/skills/rite-define/SKILL.md +7 -0
  25. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +6 -0
  26. package/pack/generated/codex/skills/rite-vet/SKILL.md +9 -2
  27. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +7 -0
  28. package/pack/generated/devin/agents/devrites-plan-drafter.md +5 -1
  29. package/pack/generated/devin/agents/devrites-plan-reviewer.md +7 -0
  30. package/pack/generated/devin/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
  31. package/pack/generated/devin/skills/rite-define/SKILL.md +7 -0
  32. package/pack/generated/devin/skills/rite-define/reference/plan-template.md +6 -0
  33. package/pack/generated/devin/skills/rite-vet/SKILL.md +9 -2
  34. package/pack/generated/devin/skills/rite-vet/reference/artifacts.md +7 -0
  35. package/pack/generated/dsh/agents/devrites-plan-drafter.md +5 -1
  36. package/pack/generated/dsh/agents/devrites-plan-reviewer.md +7 -0
  37. package/pack/generated/dsh/runtime/cordis.patch.yml +3 -3
  38. package/pack/generated/dsh/runtime/specialist.mjs +3 -1
  39. package/pack/generated/dsh/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
  40. package/pack/generated/dsh/skills/rite-define/SKILL.md +7 -0
  41. package/pack/generated/dsh/skills/rite-define/reference/plan-template.md +6 -0
  42. package/pack/generated/dsh/skills/rite-vet/SKILL.md +9 -2
  43. package/pack/generated/dsh/skills/rite-vet/reference/artifacts.md +7 -0
  44. package/pack/generated/omp/agents/devrites-plan-drafter.md +5 -1
  45. package/pack/generated/omp/agents/devrites-plan-reviewer.md +7 -0
  46. package/pack/generated/omp/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
  47. package/pack/generated/omp/skills/rite-define/SKILL.md +7 -0
  48. package/pack/generated/omp/skills/rite-define/reference/plan-template.md +6 -0
  49. package/pack/generated/omp/skills/rite-vet/SKILL.md +9 -2
  50. package/pack/generated/omp/skills/rite-vet/reference/artifacts.md +7 -0
  51. package/pack/generated/pi/agents/devrites-plan-drafter.md +5 -1
  52. package/pack/generated/pi/agents/devrites-plan-reviewer.md +7 -0
  53. package/pack/generated/pi/skills/devrites-lib/reference/standards/workflow-artifacts.md +37 -7
  54. package/pack/generated/pi/skills/rite-define/SKILL.md +7 -0
  55. package/pack/generated/pi/skills/rite-define/reference/plan-template.md +6 -0
  56. package/pack/generated/pi/skills/rite-vet/SKILL.md +9 -2
  57. package/pack/generated/pi/skills/rite-vet/reference/artifacts.md +7 -0
  58. package/package.json +1 -1
  59. package/scripts/dsh-generate.py +1 -1
  60. package/scripts/dsh-specialist.mjs +3 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
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
+
11
+ ## [7.2.3](https://github.com/ViktorsBaikers/DevRites/compare/v7.2.2...v7.2.3) (2026-10-29)
12
+
13
+ ### Fixed
14
+
15
+ * **agents:** honor DSH orchestrator permission selection ([#187](https://github.com/ViktorsBaikers/DevRites/issues/187)) ([1030093](https://github.com/ViktorsBaikers/DevRites/commit/10300933ac688c0e06217026c89c3ed657b58ec0))
16
+
5
17
  ## [7.2.2](https://github.com/ViktorsBaikers/DevRites/compare/v7.2.1...v7.2.2) (2026-10-29)
6
18
 
7
19
  ### 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.2`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v7.2.2): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
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
 
package/docs/dsh.md CHANGED
@@ -32,10 +32,16 @@ settlement notice. A returned child id means dispatch was admitted; `inactive`
32
32
  activity alone does not prove successful completion. The orchestrator collects
33
33
  and reconciles every terminal result, including failures, before dependent work.
34
34
 
35
+ The DevRites orchestrator honors DSH's selected permission mode, including
36
+ **Full access**, without changing approval settings or enabling Full access
37
+ automatically. After updating an older installation and restarting DSH, select
38
+ your desired permission preset again if the session already shows **Custom**.
39
+
35
40
  Read-only roles retain only their mapped tools and run shell commands under the
36
41
  read-only sandbox. Shell-capable roles also receive native job tools to collect
37
- background command output and stop their jobs. Writers have a workspace-write ceiling. The guard narrows
38
- DSH's inherited parent override before each allowed call, preserves an existing
42
+ background command output and stop their jobs. Specialist writers have a
43
+ workspace-write ceiling. For specialists, the guard narrows DSH's inherited
44
+ parent override before each allowed call, preserves an existing
39
45
  read-only parent boundary, and denies explicit sandbox escalation. Confining
40
46
  shell and filesystem providers are required; an unconfined composition fails
41
47
  closed. Tool access derives from canonical agent frontmatter; unsupported
@@ -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
- Slices []TaskSlice
25
- Cycle []string // empty when acyclic
26
- Unknown []string // dependency ids with no defining slice
27
- Problems []string // human-readable blockers
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; put a hub file's shared edit in one seam slice
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/readiness/
8
- built count or authorizes consumption. Module owns semantics; callers retain only
9
- link and entry/return.
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
- `&&`, `||`, newline, unescaped/list-separator `|`. Cwd=`repository-root` or
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; `return_next_action`=exact slash action
80
- bound to `active_slug`. Malformed admission/reference routes Plan/Vet before mutation.
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.** Goal-backward map every requirement, criterion, NFR,
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, and it replays Build-entry `check slice` on every unbuilt slice, so
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; put a hub file's shared edit in one seam slice
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: