devrites 7.3.5 → 7.3.6
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 +16 -0
- package/README.md +1 -1
- package/docs/adr/0040-early-preparation-admission.md +76 -0
- package/docs/adr/README.md +1 -0
- package/docs/engine/commands.md +48 -6
- package/engine/internal/lib/context.go +50 -2
- package/engine/internal/lib/continuation.go +2 -0
- package/engine/internal/lib/dispatch.go +75 -20
- package/engine/internal/lib/frontier.go +40 -16
- package/engine/internal/lib/frontier_controller.go +53 -38
- package/engine/internal/lib/frontier_preparation.go +210 -0
- package/engine/internal/lib/next.go +5 -2
- package/engine/internal/lib/triggers.go +9 -0
- package/pack/.claude/skills/devrites-lib/SKILL.md +2 -0
- package/pack/.claude/skills/devrites-lib/reference/parallel-workspaces.md +12 -10
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +7 -5
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +55 -42
- package/pack/generated/claude/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-workspaces.md +12 -10
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +7 -5
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +55 -42
- package/pack/generated/codex/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-workspaces.md +12 -10
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +7 -5
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +55 -42
- package/pack/generated/devin/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-workspaces.md +12 -10
- package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +7 -5
- package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +55 -42
- package/pack/generated/dsh/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/dsh/skills/devrites-lib/reference/parallel-workspaces.md +12 -10
- package/pack/generated/dsh/skills/rite-autocomplete/SKILL.md +7 -5
- package/pack/generated/dsh/skills/rite-autocomplete/reference/loop.md +55 -42
- package/pack/generated/omp/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-workspaces.md +12 -10
- package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +7 -5
- package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +55 -42
- package/pack/generated/pi/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-workspaces.md +12 -10
- package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +7 -5
- package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +55 -42
- package/package.json +1 -1
|
@@ -134,19 +134,14 @@ func frontierSerialReason(records frontierRecords, source, workspace, id string)
|
|
|
134
134
|
return records.serial[parallel.SliceKey(workspace, "SLICE-"+sliceNumber(id))]
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
func frontierPendingPreparation(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return false, nil
|
|
141
|
-
}
|
|
142
|
-
if err != nil {
|
|
143
|
-
return false, err
|
|
144
|
-
}
|
|
145
|
-
dispatch, err := decodeDispatch(raw)
|
|
146
|
-
if err != nil {
|
|
147
|
-
return false, err
|
|
137
|
+
func frontierPendingPreparation(dispatch *dispatchFile) bool {
|
|
138
|
+
if dispatch == nil {
|
|
139
|
+
return false
|
|
148
140
|
}
|
|
149
141
|
for _, wave := range dispatch.Waves {
|
|
142
|
+
if wave.SequenceResearch {
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
150
145
|
if wave.State != "open" && wave.State != "sealed" {
|
|
151
146
|
continue
|
|
152
147
|
}
|
|
@@ -155,23 +150,12 @@ func frontierPendingPreparation(n *frontierWorkspace) (bool, error) {
|
|
|
155
150
|
continue
|
|
156
151
|
}
|
|
157
152
|
base := roleBase(role)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if base == "evidence-scout" || base == "strategy-reviewer" {
|
|
161
|
-
return true, nil
|
|
162
|
-
}
|
|
163
|
-
case "define", "plan":
|
|
164
|
-
if base == "plan-drafter" || base == "evidence-scout" {
|
|
165
|
-
return true, nil
|
|
166
|
-
}
|
|
167
|
-
case "vet":
|
|
168
|
-
if base == "plan-reviewer" || base == "devex-reviewer" || base == "strategy-reviewer" || base == "code-reviewer" {
|
|
169
|
-
return true, nil
|
|
170
|
-
}
|
|
153
|
+
if preparationRole(strings.ToLower(wave.Phase), base) {
|
|
154
|
+
return true
|
|
171
155
|
}
|
|
172
156
|
}
|
|
173
157
|
}
|
|
174
|
-
return false
|
|
158
|
+
return false
|
|
175
159
|
}
|
|
176
160
|
|
|
177
161
|
func frontierSuccessor(n *frontierWorkspace, nodes map[string]*frontierWorkspace, mapping map[int]string) (string, error) {
|
|
@@ -199,8 +183,8 @@ func frontierSuccessor(n *frontierWorkspace, nodes map[string]*frontierWorkspace
|
|
|
199
183
|
|
|
200
184
|
func frontierPreparationAdmission(root, source string, nodes map[string]*frontierWorkspace, mapping map[int]string, problem string, allow bool, records frontierRecords) ([]string, error) {
|
|
201
185
|
n := nodes[source]
|
|
202
|
-
if
|
|
203
|
-
return nil,
|
|
186
|
+
if n == nil {
|
|
187
|
+
return nil, fmt.Errorf("source %s is not an existing sequence workspace", source)
|
|
204
188
|
}
|
|
205
189
|
if hold, err := frontierQuestionHold(n); err != nil || hold != "" {
|
|
206
190
|
return nil, err
|
|
@@ -222,15 +206,20 @@ func frontierPreparationAdmission(root, source string, nodes map[string]*frontie
|
|
|
222
206
|
if reason == "" {
|
|
223
207
|
reason = "positive remaining budget has no exact recorded successor"
|
|
224
208
|
}
|
|
225
|
-
return nil, fmt.Errorf("source %s native authority-normalization required before
|
|
209
|
+
return nil, fmt.Errorf("source %s native authority-normalization required before forward-phase admission: %s; read sequence artifacts, record current roadmap through its owning decision, or retain frontier-blocked: sequence — <specific constraint>", source, reason)
|
|
226
210
|
}
|
|
227
|
-
if target == "" {
|
|
211
|
+
if target == "" || nodes[target] == nil && (!allow || n.hasRemaining && n.remaining == 0) {
|
|
228
212
|
return nil, nil
|
|
229
213
|
}
|
|
214
|
+
var successorDispatch *dispatchFile
|
|
230
215
|
if child := nodes[target]; child != nil {
|
|
231
216
|
if _, err := frontierChain(nodes, target); err != nil {
|
|
232
217
|
return nil, err
|
|
233
218
|
}
|
|
219
|
+
successorDispatch, err = frontierPreparationBindings(child, nil)
|
|
220
|
+
if err != nil {
|
|
221
|
+
return nil, err
|
|
222
|
+
}
|
|
234
223
|
}
|
|
235
224
|
if reason := records.blocked[target]; reason != "" {
|
|
236
225
|
return []string{"frontier-blocked: " + target + " — " + reason + "; preparation deferred, not approved"}, nil
|
|
@@ -242,8 +231,8 @@ func frontierPreparationAdmission(root, source string, nodes map[string]*frontie
|
|
|
242
231
|
if frontierTerminal(child) {
|
|
243
232
|
return nil, nil
|
|
244
233
|
}
|
|
245
|
-
if
|
|
246
|
-
return nil,
|
|
234
|
+
if frontierPendingPreparation(successorDispatch) {
|
|
235
|
+
return nil, nil
|
|
247
236
|
}
|
|
248
237
|
ready, notices, err := WorkspaceReady(filepath.Dir(root), source, target)
|
|
249
238
|
if err != nil {
|
|
@@ -264,7 +253,7 @@ func frontierPreparationAdmission(root, source string, nodes map[string]*frontie
|
|
|
264
253
|
return nil, nil
|
|
265
254
|
}
|
|
266
255
|
}
|
|
267
|
-
return nil, fmt.Errorf("successor %s preparation has not actually started; invoke /rite-spec %s or its native Plan/Vet cursor and retain launched planning/review handles before
|
|
256
|
+
return nil, fmt.Errorf("successor %s preparation has not actually started; invoke /rite-spec %s or its native Plan/Vet cursor and retain launched planning/review handles before continuing forward phases of %s; alternatively retain frontier-blocked: %s — <specific constraint> in evidence.md", target, frontierQuote(target), source, target)
|
|
268
257
|
}
|
|
269
258
|
|
|
270
259
|
func frontierNormalizePaths(paths []string) error {
|
|
@@ -321,7 +310,7 @@ func frontierLeaseReserved(lease *parallel.Lease, controller, source string, ids
|
|
|
321
310
|
return len(ids) > 0
|
|
322
311
|
}
|
|
323
312
|
|
|
324
|
-
func frontierWriterRetention(root, source, controller, binding string, lease *parallel.Lease, nodes map[string]*frontierWorkspace, ordered, ids []string, current *dispatchFile) error {
|
|
313
|
+
func frontierWriterRetention(root, source, controller, binding string, lease *parallel.Lease, nodes map[string]*frontierWorkspace, ordered, ids []string, current *dispatchFile, checkPreparation bool) error {
|
|
325
314
|
var claims map[string]*claimRecord
|
|
326
315
|
for _, slug := range ordered {
|
|
327
316
|
if _, err := frontierChain(nodes, slug); err != nil {
|
|
@@ -340,6 +329,11 @@ func frontierWriterRetention(root, source, controller, binding string, lease *pa
|
|
|
340
329
|
if err != nil {
|
|
341
330
|
return err
|
|
342
331
|
}
|
|
332
|
+
if checkPreparation {
|
|
333
|
+
if _, err := frontierPreparationBindings(nodes[slug], dispatch); err != nil {
|
|
334
|
+
return err
|
|
335
|
+
}
|
|
336
|
+
}
|
|
343
337
|
}
|
|
344
338
|
for name, wave := range dispatch.Waves {
|
|
345
339
|
if wave.State == "complete" || wave.TerminalAt != "" {
|
|
@@ -475,6 +469,12 @@ func autocompleteWriterAdmission(root, source string, wave *dispatchWave, curren
|
|
|
475
469
|
if err != nil {
|
|
476
470
|
return "", nil, err
|
|
477
471
|
}
|
|
472
|
+
checkPreparation := capN > 1 && (lease == nil || lease.Capacity != 1)
|
|
473
|
+
if checkPreparation {
|
|
474
|
+
if _, err := frontierPreparationBindings(nodes[source], current); err != nil {
|
|
475
|
+
return "", nil, err
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
478
|
chain, err := frontierChain(nodes, source)
|
|
479
479
|
if err != nil {
|
|
480
480
|
return "", nil, err
|
|
@@ -498,9 +498,12 @@ func autocompleteWriterAdmission(root, source string, wave *dispatchWave, curren
|
|
|
498
498
|
if err != nil {
|
|
499
499
|
return "", nil, err
|
|
500
500
|
}
|
|
501
|
-
notices
|
|
502
|
-
if
|
|
503
|
-
|
|
501
|
+
var notices []string
|
|
502
|
+
if checkPreparation {
|
|
503
|
+
notices, err = frontierPreparationAdmission(root, source, nodes, mapping, problem, allow, records)
|
|
504
|
+
if err != nil {
|
|
505
|
+
return "", nil, err
|
|
506
|
+
}
|
|
504
507
|
}
|
|
505
508
|
local, withheld, err := WorkspaceReady(filepath.Dir(root), controller, source)
|
|
506
509
|
if err != nil {
|
|
@@ -531,7 +534,7 @@ func autocompleteWriterAdmission(root, source string, wave *dispatchWave, curren
|
|
|
531
534
|
if reserved {
|
|
532
535
|
binding = controller + "/" + lease.BatchID + "@" + lease.CreatedAt
|
|
533
536
|
}
|
|
534
|
-
if err := frontierWriterRetention(root, source, controller, binding, lease, nodes, ordered, ids, current); err != nil {
|
|
537
|
+
if err := frontierWriterRetention(root, source, controller, binding, lease, nodes, ordered, ids, current, checkPreparation); err != nil {
|
|
535
538
|
return "", nil, err
|
|
536
539
|
}
|
|
537
540
|
var candidates []parallel.SlicePaths
|
|
@@ -549,7 +552,7 @@ func autocompleteWriterAdmission(root, source string, wave *dispatchWave, curren
|
|
|
549
552
|
} else if hold != "" {
|
|
550
553
|
continue
|
|
551
554
|
}
|
|
552
|
-
if _, _, err := frontierLocal(root, controller, n, &candidates); err != nil {
|
|
555
|
+
if _, _, err := frontierLocal(root, controller, n, &candidates, nil); err != nil {
|
|
553
556
|
return "", nil, err
|
|
554
557
|
}
|
|
555
558
|
}
|
|
@@ -613,6 +616,18 @@ func autocompleteWriterAdmission(root, source string, wave *dispatchWave, curren
|
|
|
613
616
|
return binding, notices, nil
|
|
614
617
|
}
|
|
615
618
|
|
|
619
|
+
const frontierInheritedPreparationWait = "preparatory descendant; continue immediate-parent preparation before this native cursor"
|
|
620
|
+
|
|
621
|
+
func frontierSourcePreparationWait(source string, lines []string) bool {
|
|
622
|
+
wait := "frontier-workspace: " + source + " location=live action=wait reason=" + frontierInheritedPreparationWait
|
|
623
|
+
for _, line := range lines {
|
|
624
|
+
if line == wait {
|
|
625
|
+
return true
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return false
|
|
629
|
+
}
|
|
630
|
+
|
|
616
631
|
func frontierGlobalPriority(source string, lines []string) bool {
|
|
617
632
|
controller := source
|
|
618
633
|
for _, line := range lines {
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
package lib
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
|
|
9
|
+
"github.com/devrites/devrites/internal/devritespaths"
|
|
10
|
+
"github.com/devrites/devrites/internal/parallel"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
type preparationPurpose struct {
|
|
14
|
+
parent string
|
|
15
|
+
research bool
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func preparationPhase(phase string) bool {
|
|
19
|
+
switch phase {
|
|
20
|
+
case "spec", "clarify", "temper", "define", "plan", "vet":
|
|
21
|
+
return true
|
|
22
|
+
}
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
func preparationRole(phase, role string) bool {
|
|
27
|
+
switch phase {
|
|
28
|
+
case "spec", "clarify", "temper":
|
|
29
|
+
return role == "evidence-scout" || role == "strategy-reviewer"
|
|
30
|
+
case "define", "plan":
|
|
31
|
+
return role == "plan-drafter" || role == "evidence-scout"
|
|
32
|
+
case "vet":
|
|
33
|
+
return role == "plan-reviewer" || role == "devex-reviewer" || role == "strategy-reviewer" || role == "code-reviewer"
|
|
34
|
+
}
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
func (p preparationPurpose) validate(phase string, roles []string, context bool) error {
|
|
39
|
+
phase = strings.ToLower(phase)
|
|
40
|
+
if p.parent == "" && !p.research {
|
|
41
|
+
return nil
|
|
42
|
+
}
|
|
43
|
+
if p.parent != "" && p.research {
|
|
44
|
+
return fmt.Errorf("--prepare-for and --sequence-research are mutually exclusive")
|
|
45
|
+
}
|
|
46
|
+
if !preparationPhase(phase) {
|
|
47
|
+
return fmt.Errorf("preparation purpose requires a native preparation phase")
|
|
48
|
+
}
|
|
49
|
+
if len(roles) == 0 && (!context || p.research) {
|
|
50
|
+
return fmt.Errorf("preparation dispatch/research requires an allowed named role")
|
|
51
|
+
}
|
|
52
|
+
for _, role := range roles {
|
|
53
|
+
if p.research && role != "evidence-scout" || !p.research && !preparationRole(phase, roleBase(role)) {
|
|
54
|
+
return fmt.Errorf("role %q is not permitted for this preparation purpose", role)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return nil
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func earlyPreparationPhase(phase string) bool {
|
|
61
|
+
phase = strings.ToLower(phase)
|
|
62
|
+
return phase == "temper" || phase == "define" || phase == "plan" || phase == "vet"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Context and dispatch share admission; neither a bundle nor a purpose flag proves a native launch.
|
|
66
|
+
func preparationAdmission(root, source, phase string, purpose preparationPurpose, current *dispatchFile) ([]string, error) {
|
|
67
|
+
phase = strings.ToLower(phase)
|
|
68
|
+
if purpose.research {
|
|
69
|
+
if current != nil {
|
|
70
|
+
return nil, nil
|
|
71
|
+
}
|
|
72
|
+
dir, err := devritespaths.ExistingFeatureDirChecked(root, source)
|
|
73
|
+
if err != nil {
|
|
74
|
+
return nil, err
|
|
75
|
+
}
|
|
76
|
+
raw, err := readBoundedRegularFile(filepath.Join(dir, "dispatch.json"), workspaceArtifactLimit)
|
|
77
|
+
if os.IsNotExist(err) {
|
|
78
|
+
return nil, nil
|
|
79
|
+
}
|
|
80
|
+
if err != nil {
|
|
81
|
+
return nil, err
|
|
82
|
+
}
|
|
83
|
+
_, err = decodeDispatch(raw)
|
|
84
|
+
return nil, err
|
|
85
|
+
}
|
|
86
|
+
if purpose.parent == "" && !earlyPreparationPhase(phase) {
|
|
87
|
+
return nil, nil
|
|
88
|
+
}
|
|
89
|
+
mode, err := autocompleteMode(root)
|
|
90
|
+
if err != nil {
|
|
91
|
+
return nil, err
|
|
92
|
+
}
|
|
93
|
+
if purpose.parent == "" && !mode {
|
|
94
|
+
return nil, nil
|
|
95
|
+
}
|
|
96
|
+
allow, capacity, err := frontierPolicy(root)
|
|
97
|
+
if err != nil {
|
|
98
|
+
return nil, err
|
|
99
|
+
}
|
|
100
|
+
if mode {
|
|
101
|
+
_, lease, leaseErr := parallel.ControllerLease(filepath.Dir(root))
|
|
102
|
+
if leaseErr != nil {
|
|
103
|
+
return nil, leaseErr
|
|
104
|
+
}
|
|
105
|
+
if lease != nil && lease.Capacity > 0 && lease.Capacity < capacity {
|
|
106
|
+
capacity = lease.Capacity
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if purpose.parent == "" && capacity <= 1 {
|
|
110
|
+
return nil, nil
|
|
111
|
+
}
|
|
112
|
+
nodes, err := frontierScan(root)
|
|
113
|
+
if err != nil {
|
|
114
|
+
return nil, err
|
|
115
|
+
}
|
|
116
|
+
chain, err := frontierChain(nodes, source)
|
|
117
|
+
if err != nil {
|
|
118
|
+
return nil, err
|
|
119
|
+
}
|
|
120
|
+
if mode && capacity > 1 {
|
|
121
|
+
if _, err := frontierPreparationBindings(nodes[source], current); err != nil {
|
|
122
|
+
return nil, err
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
ordered, _ := frontierRelated(nodes, chain)
|
|
126
|
+
mapping, _, problem, invalid := frontierRoadmap(nodes, ordered)
|
|
127
|
+
if invalid {
|
|
128
|
+
return nil, fmt.Errorf("invalid canonical roadmap: %s", problem)
|
|
129
|
+
}
|
|
130
|
+
if purpose.parent != "" {
|
|
131
|
+
parent, child := nodes[purpose.parent], nodes[source]
|
|
132
|
+
if parent == nil || child == nil || parent == child || child.parent != parent.slug || !parent.hasPosition || !child.hasPosition || child.position != parent.position+1 {
|
|
133
|
+
return nil, fmt.Errorf("--prepare-for requires an existing bound immediate parent with adjacent sequence positions")
|
|
134
|
+
}
|
|
135
|
+
if _, err := frontierChain(nodes, parent.slug); err != nil {
|
|
136
|
+
return nil, err
|
|
137
|
+
}
|
|
138
|
+
target, err := frontierSuccessor(parent, nodes, mapping)
|
|
139
|
+
if problem != "" || err != nil || target != source {
|
|
140
|
+
return nil, fmt.Errorf("--prepare-for parent/successor authority does not agree: %s %v", problem, err)
|
|
141
|
+
}
|
|
142
|
+
return nil, nil
|
|
143
|
+
}
|
|
144
|
+
records, err := frontierEvidence(root, source)
|
|
145
|
+
if err != nil {
|
|
146
|
+
return nil, err
|
|
147
|
+
}
|
|
148
|
+
return frontierPreparationAdmission(root, source, nodes, mapping, problem, allow && capacity > 1, records)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Validate the whole retained ledger before any current-scope classification or credit.
|
|
152
|
+
// A decoded account may be supplied by dispatch admission; recovery uses decode alone.
|
|
153
|
+
func frontierPreparationBindings(n *frontierWorkspace, account *dispatchFile) (*dispatchFile, error) {
|
|
154
|
+
if n == nil {
|
|
155
|
+
return nil, fmt.Errorf("preparation binding source is not an existing sequence workspace")
|
|
156
|
+
}
|
|
157
|
+
if account == nil {
|
|
158
|
+
raw, err := readBoundedRegularFile(filepath.Join(n.dir, "dispatch.json"), workspaceArtifactLimit)
|
|
159
|
+
if os.IsNotExist(err) {
|
|
160
|
+
return nil, nil
|
|
161
|
+
}
|
|
162
|
+
if err != nil {
|
|
163
|
+
return nil, err
|
|
164
|
+
}
|
|
165
|
+
account, err = decodeDispatch(raw)
|
|
166
|
+
if err != nil {
|
|
167
|
+
return nil, err
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for _, wave := range account.Waves {
|
|
171
|
+
if wave.PreparationFor != "" && wave.PreparationFor != n.parent && wave.State != "complete" && (wave.State != "abandoned" || wave.TerminalAt == "") {
|
|
172
|
+
return nil, fmt.Errorf("workspace %s preparation account does not match its immediate parent", n.slug)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return account, nil
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func frontierPreparatoryOnly(root string, n *frontierWorkspace, nodes map[string]*frontierWorkspace, mapping map[int]string, problem string) (bool, *dispatchFile, error) {
|
|
179
|
+
account, err := frontierPreparationBindings(n, nil)
|
|
180
|
+
if err != nil {
|
|
181
|
+
return false, nil, err
|
|
182
|
+
}
|
|
183
|
+
parent := nodes[n.parent]
|
|
184
|
+
if parent == nil || frontierTerminal(parent) || frontierTerminal(n) || problem != "" || !parent.hasPosition || !n.hasPosition || n.position != parent.position+1 {
|
|
185
|
+
return false, account, nil
|
|
186
|
+
}
|
|
187
|
+
if _, err := frontierChain(nodes, n.slug); err != nil {
|
|
188
|
+
return false, account, nil
|
|
189
|
+
}
|
|
190
|
+
target, err := frontierSuccessor(parent, nodes, mapping)
|
|
191
|
+
if err != nil || target != n.slug {
|
|
192
|
+
return false, account, nil
|
|
193
|
+
}
|
|
194
|
+
active, err := readBoundedRegularFile(filepath.Join(root, "ACTIVE"), workspaceArtifactLimit)
|
|
195
|
+
if err != nil && !os.IsNotExist(err) {
|
|
196
|
+
return false, account, err
|
|
197
|
+
}
|
|
198
|
+
if strings.TrimSpace(string(active)) == n.slug {
|
|
199
|
+
return false, account, nil
|
|
200
|
+
}
|
|
201
|
+
if account == nil {
|
|
202
|
+
return true, nil, nil
|
|
203
|
+
}
|
|
204
|
+
for _, wave := range account.Waves {
|
|
205
|
+
if wave.PreparationFor == "" && !wave.SequenceResearch && (earlyPreparationPhase(wave.Phase) || isBuildWrightWave(wave)) {
|
|
206
|
+
return false, account, nil
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return true, account, nil
|
|
210
|
+
}
|
|
@@ -91,6 +91,9 @@ func RunNext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
91
91
|
fmt.Fprintln(stdout, "next-command: /rite-autocomplete")
|
|
92
92
|
return exit
|
|
93
93
|
}
|
|
94
|
+
if mode && frontierSourcePreparationWait(slug, lines) {
|
|
95
|
+
return exit
|
|
96
|
+
}
|
|
94
97
|
}
|
|
95
98
|
if report.Phase == state.PhaseBuild {
|
|
96
99
|
printPlanBatch(stdout, root, slug)
|
|
@@ -118,11 +121,11 @@ func RunNext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
118
121
|
}
|
|
119
122
|
missing = report.MissingFiles
|
|
120
123
|
} else {
|
|
121
|
-
|
|
124
|
+
missing = missingArtifacts(featureDir, policy.RequiredArtifacts)
|
|
125
|
+
if policy.Target == state.PhaseClarify && openQuestions == 0 && len(missing) == 0 {
|
|
122
126
|
skips = append(skips, "clarify (0 open questions)")
|
|
123
127
|
continue
|
|
124
128
|
}
|
|
125
|
-
missing = missingArtifacts(featureDir, policy.RequiredArtifacts)
|
|
126
129
|
// A phase that adds no required artifact over the previous one
|
|
127
130
|
// (build after vet, polish after prove) cannot be proven done by
|
|
128
131
|
// artifact presence, so it is never skipped. Converge is the
|
|
@@ -176,5 +176,14 @@ func triggerApplyCommand(opts contextOpts, suggested []string) string {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
parts = append(parts, "--trigger", strings.Join(suggested, ","))
|
|
179
|
+
if opts.purpose.parent != "" {
|
|
180
|
+
parts = append(parts, "--prepare-for", frontierQuote(opts.purpose.parent))
|
|
181
|
+
}
|
|
182
|
+
if opts.purpose.research {
|
|
183
|
+
parts = append(parts, "--sequence-research")
|
|
184
|
+
}
|
|
185
|
+
if opts.skillsRoot != "" {
|
|
186
|
+
parts = append(parts, "--skills-root", frontierQuote(opts.skillsRoot))
|
|
187
|
+
}
|
|
179
188
|
return strings.Join(parts, " ")
|
|
180
189
|
}
|
|
@@ -39,6 +39,8 @@ lifecycle state from chat or optional `README.md`.
|
|
|
39
39
|
completion states. The host renders the response normally.
|
|
40
40
|
- [`reference/parallel-workspaces.md`](reference/parallel-workspaces.md): explicit
|
|
41
41
|
phase targets, one isolated global controller, and source-owned proof.
|
|
42
|
+
- [Autocomplete loop](../rite-autocomplete/reference/loop.md#early-preparation-admission):
|
|
43
|
+
controller-only early preparation, bounded successor routing and normalization research.
|
|
42
44
|
- [`reference/visual-playbooks/index.md`](reference/visual-playbooks/index.md): progressive
|
|
43
45
|
visual HTML playbook router (load matching ids only; dual-read outline).
|
|
44
46
|
|
|
@@ -101,16 +101,18 @@ return cursors and native handles: one controller, not another phase or queue.
|
|
|
101
101
|
Normalize unresolved legacy sequence authority through its owning decision/
|
|
102
102
|
phase. Positive budget, ambiguous prose or path disjointness grants nothing.
|
|
103
103
|
A current invalid canonical map is an error, never a fallback to history.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
104
|
+
At controller cap>1, resolve sequence authority and start actual immediate-
|
|
105
|
+
successor preparation before ordinary local Temper/Define/Plan/Vet context or
|
|
106
|
+
dispatch open, and retain the full Build gate before claims/rounds/history.
|
|
107
|
+
Follow [early preparation](../../rite-autocomplete/reference/loop.md#early-preparation-admission)
|
|
108
|
+
for native launch/start, internal markers and bounded preparatory-only routing.
|
|
109
|
+
Missing targets need native Spec → Clarify → Temper → Define/Plan → affected
|
|
110
|
+
Vet; existing targets need current completed/readiness state, registered pending
|
|
111
|
+
native preparation, or an existing human/dependency hold. Completed live/
|
|
112
|
+
archived successors need no reopening; valid finite tails discharge the
|
|
113
|
+
obligation. Placeholders, reports and unopened waves prove nothing.
|
|
114
|
+
Opt-out, zero opening budget or source human/question holds prevent new
|
|
115
|
+
openings, not existing child preparation or independent work.
|
|
114
116
|
If preparation cannot start, retain `frontier-blocked: <successor> — <constraint>`
|
|
115
117
|
in source `evidence.md`. `frontier-blocked: sequence — <constraint>` may defer
|
|
116
118
|
unresolved historical normalization for valid independent local work. Each row
|
|
@@ -115,11 +115,13 @@ next sequence continuation through Vet in a second session, then stop.
|
|
|
115
115
|
Temper → Define → Vet → Build → Prove → Polish → Review → Seal. Durable files
|
|
116
116
|
carry state. Apply the mutable post-vet budget before Build. Consume fresh scheduling
|
|
117
117
|
emitted by recorded `dispatch return`; rerun frontier after further changes
|
|
118
|
-
and before waits.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
and before waits. Before ordinary local Temper/Define/Plan/Vet context/dispatch,
|
|
119
|
+
resolve sequence authority and start actual immediate-successor preparation
|
|
120
|
+
under [early admission](reference/loop.md#early-preparation-admission),
|
|
121
|
+
or retain the global pass's concrete deferral; keep the full Build gate.
|
|
122
|
+
Derive initial preparatory-only routing before any marked wave; do not recurse
|
|
123
|
+
through the roadmap. Preserve local cursors, protected validation windows and
|
|
124
|
+
every admission/isolation gate; pass Build the resolved global cap.
|
|
123
125
|
**Completion:** Seal GO or a durable valid stop precedes later phases.
|
|
124
126
|
5. **Apply stops.** At every gate use
|
|
125
127
|
[stop-conditions.md](reference/stop-conditions.md). A `blocked` label alone is not a stop condition:
|
|
@@ -39,18 +39,41 @@ remaining. AFK configuration itself stays unchanged.
|
|
|
39
39
|
|
|
40
40
|
### Admit each unattended cycle
|
|
41
41
|
|
|
42
|
-
Before each phase
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
Before each phase/fan-out/recovery/writer, check readiness/overlap and apply early
|
|
43
|
+
admission and the global pass below; local readiness is insufficient. Count every leaf.
|
|
44
|
+
Explicit activation resets only activation-local counters, not durable recovery.
|
|
45
|
+
Pass resolved `--parallel N` to [Build](../../rite-build/reference/output.md#caller-owned-continuation).
|
|
46
|
+
Legacy or invented execution ceilings never stop this controller; human limits bind.
|
|
47
|
+
Resolve ranked validating/blocking picks via `devrites-engine state resolve`, not
|
|
48
|
+
a user handoff. Escalating, irreversible-risk, access, unrecommended blocking picks
|
|
49
|
+
and Seal judgment (`slice: seal`) still pause.
|
|
50
|
+
|
|
51
|
+
## Early preparation admission
|
|
52
|
+
|
|
53
|
+
Internal exclusive `context`/`dispatch open` flags:
|
|
54
|
+
- `--prepare-for <parent-source>`: permitted native non-Build preparation roles
|
|
55
|
+
(only controlling-root context may be roleless), exact existing immediate
|
|
56
|
+
successor/parent agreeing on canonical identity, binding, adjacency and chain.
|
|
57
|
+
Exempts the child's successor gate, never product writes.
|
|
58
|
+
- `--sequence-research`: only canonical `evidence-scout`, Spec/Clarify/Temper/Define/
|
|
59
|
+
Plan/Vet, exact existing source and normal path/role/ledger constraints. Exempts
|
|
60
|
+
authority/successor admission solely to investigate broken authority read-only:
|
|
61
|
+
no acceptance, scope/approval/phase advance, successor creation, writers,
|
|
62
|
+
readiness or preparation credit.
|
|
63
|
+
|
|
64
|
+
Context (`--skill rite-<phase>` too) checks before writes; no account required.
|
|
65
|
+
Use context → packet → open → native launch → `dispatch start` actual handles
|
|
66
|
+
before parent admission; openings alone do not count.
|
|
67
|
+
|
|
68
|
+
Derive preparatory-only B before any marked wave from nonterminal parent A:
|
|
69
|
+
until ordinary unmarked B Temper/Define/Plan/Vet or Build-wright admission
|
|
70
|
+
(checking C first), A becoming terminal or ordered handoff to B, C waits—even
|
|
71
|
+
explicit C `next`/`check continuation`. No C creation/rerouting before A's eligible
|
|
72
|
+
phase. Completed B history retains this bound; Spec/Clarify/research/read-only
|
|
73
|
+
Build do not release it. Independent work stays visible; all global-pass gates bind.
|
|
74
|
+
|
|
75
|
+
Shared registry edits alone do not serialize disjoint modules. Isolate their
|
|
76
|
+
writer; refusal-only tests never replace positive requirements or defer their band.
|
|
54
77
|
|
|
55
78
|
## Phase arc
|
|
56
79
|
|
|
@@ -70,12 +93,10 @@ Workspace files, not chat, carry state. Read/execute each phase skill:
|
|
|
70
93
|
| 10 | `/rite-seal` | GO/NO-GO; no Git |
|
|
71
94
|
| 11 | `/rite-ship` | only after GO; `--ship` never authorizes Git; stop at literal-GO/native approval |
|
|
72
95
|
|
|
73
|
-
At phase entry, before
|
|
96
|
+
At phase entry, before recovery/waits, execute the
|
|
74
97
|
[global pass](../../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier).
|
|
75
|
-
Consume
|
|
76
|
-
|
|
77
|
-
authorized preparation/selection, preserving pending handles, local cursors and
|
|
78
|
-
protected validation windows. Reporting or creating a placeholder is not progress.
|
|
98
|
+
Consume persisted return frontiers; rerun after landing/artifact/capacity changes.
|
|
99
|
+
Execute authorized routes, retaining handles, cursors and protected proof windows.
|
|
79
100
|
|
|
80
101
|
Check [stop-conditions.md](stop-conditions.md) before advancing. Discard stale
|
|
81
102
|
pass evidence after source edits. Re-read the selected source before each phase per [phase target binding](../../devrites-lib/reference/parallel-workspaces.md#phase-target-binding). On a cold resume or
|
|
@@ -88,14 +109,12 @@ floor; `--update` never excuses an unreconciled regression.
|
|
|
88
109
|
|
|
89
110
|
## Sequence continuation
|
|
90
111
|
|
|
91
|
-
With a recorded successor,
|
|
92
|
-
leave the owner sealed/unshipped with `/rite-ship
|
|
93
|
-
or
|
|
94
|
-
[shared grammar](../../devrites-lib/reference/workspace-artifact-schema.md#what-each-file-owns)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Reuse the child's durable cursor and controller. ACTIVE changes only at normal
|
|
98
|
-
ordered handoff; early planning or isolated independent Build does not advance it.
|
|
112
|
+
With a recorded successor, budget and no opt-out, Seal GO stays internal:
|
|
113
|
+
leave the owner sealed/unshipped with `/rite-ship`; resume the child's cursor/
|
|
114
|
+
controller or authorized native Spec. Use the
|
|
115
|
+
[shared grammar](../../devrites-lib/reference/workspace-artifact-schema.md#what-each-file-owns).
|
|
116
|
+
Charge once; pipeline only immediate successors. ACTIVE changes at ordered handoff,
|
|
117
|
+
not early planning or isolated independent Build.
|
|
99
118
|
|
|
100
119
|
Before invoking a continuation, require durable `state.md`
|
|
101
120
|
`sequence_workspaces_remaining > 0`; if absent, first write
|
|
@@ -149,20 +168,14 @@ and park it after three escalations while independent work continues.
|
|
|
149
168
|
|
|
150
169
|
## Continuous caller obligation
|
|
151
170
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
A distinct invariant is progress, not exhaustion; repeated failed resolver calls
|
|
164
|
-
without correction are not progress.
|
|
165
|
-
|
|
166
|
-
The number of completed repair/Vet cycles, context pressure, compaction, session
|
|
167
|
-
duration, and nested completion never turn checkpoints into handoffs. Persist and
|
|
168
|
-
resume to the requested rest point or shared human/safety/access/parked-recovery stop.
|
|
171
|
+
Execute each green slice/phase's `next_action` internally. Before replying, run
|
|
172
|
+
`devrites-engine check continuation`: consume `required`'s `invoke:`; only `clear`
|
|
173
|
+
permits reply. Controller normalization/preparation/foreign work precedes local
|
|
174
|
+
Build; local repair/picks/reconciliation/discharged deferrals keep their phase,
|
|
175
|
+
not recursive Autocomplete. Route agent-owned NEEDS_REPLAN, budgeted Critical/Important recovery,
|
|
176
|
+
independent work and recommended blocking answers internally; invoke repair or
|
|
177
|
+
`state resolve` unless the [resolver-failure stop](stop-conditions.md#other-stop-classes)
|
|
178
|
+
applies. Distinct invariants are progress; unchanged resolver retries are not.
|
|
179
|
+
Cycles, context pressure, compaction, duration and nested completion never create
|
|
180
|
+
handoffs. Persist/resume until requested completion or a shared human/safety/
|
|
181
|
+
access/parked-recovery stop.
|
|
@@ -39,6 +39,8 @@ lifecycle state from chat or optional `README.md`.
|
|
|
39
39
|
completion states. The host renders the response normally.
|
|
40
40
|
- [`reference/parallel-workspaces.md`](reference/parallel-workspaces.md): explicit
|
|
41
41
|
phase targets, one isolated global controller, and source-owned proof.
|
|
42
|
+
- [Autocomplete loop](../rite-autocomplete/reference/loop.md#early-preparation-admission):
|
|
43
|
+
controller-only early preparation, bounded successor routing and normalization research.
|
|
42
44
|
- [`reference/visual-playbooks/index.md`](reference/visual-playbooks/index.md): progressive
|
|
43
45
|
visual HTML playbook router (load matching ids only; dual-read outline).
|
|
44
46
|
|