devrites 7.3.3 → 7.3.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 (46) hide show
  1. package/CHANGELOG.md +26 -3
  2. package/README.md +21 -1
  3. package/docs/cli.md +83 -6
  4. package/docs/command-map.md +2 -2
  5. package/docs/engine/commands.md +8 -3
  6. package/engine/commands.go +1 -1
  7. package/engine/help.go +3 -1
  8. package/engine/internal/lib/continuation.go +33 -4
  9. package/engine/internal/lib/dispatch.go +46 -31
  10. package/engine/internal/lib/frontier.go +808 -0
  11. package/engine/internal/lib/handoff.go +2 -5
  12. package/engine/internal/lib/next.go +10 -0
  13. package/engine/internal/parallel/cli.go +16 -5
  14. package/engine/internal/parallel/ops.go +7 -5
  15. package/engine/internal/parallel/repository.go +13 -0
  16. package/engine/internal/release/release.go +96 -5
  17. package/engine/main.go +4 -0
  18. package/pack/.claude/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
  19. package/pack/.claude/skills/rite-autocomplete/SKILL.md +10 -10
  20. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +6 -6
  21. package/pack/.claude/skills/rite-build/reference/parallel-batch.md +7 -7
  22. package/pack/generated/claude/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
  23. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +10 -10
  24. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +6 -6
  25. package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +7 -7
  26. package/pack/generated/codex/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
  27. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +10 -10
  28. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +6 -6
  29. package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +7 -7
  30. package/pack/generated/devin/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
  31. package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +10 -10
  32. package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +6 -6
  33. package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +7 -7
  34. package/pack/generated/dsh/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
  35. package/pack/generated/dsh/skills/rite-autocomplete/SKILL.md +10 -10
  36. package/pack/generated/dsh/skills/rite-autocomplete/reference/loop.md +6 -6
  37. package/pack/generated/dsh/skills/rite-build/reference/parallel-batch.md +7 -7
  38. package/pack/generated/omp/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
  39. package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +10 -10
  40. package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +6 -6
  41. package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +7 -7
  42. package/pack/generated/pi/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
  43. package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +10 -10
  44. package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +6 -6
  45. package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +7 -7
  46. package/package.json +1 -1
@@ -89,12 +89,9 @@ func RunHandoff(root string, args []string, stdout, stderr io.Writer) int {
89
89
  fmt.Fprintf(stdout, "missing_required: %s\n", strings.Join(report.MissingFiles, ", "))
90
90
  }
91
91
  fmt.Fprintf(stdout, "read_next: %s\n", strings.Join(readNext(featureDir), ", "))
92
- verdict, err := classifyContinuation(root, slug)
93
- if err != nil {
94
- fmt.Fprintf(stderr, "handoff: continuation: %v\n", err)
95
- return 2
92
+ if code := RunCheckContinuation(root, []string{slug}, stdout, stderr); code != 0 {
93
+ return code
96
94
  }
97
- writeContinuation(stdout, verdict)
98
95
  if resume {
99
96
  recordMetric(root, slug, string(report.Phase), "resume", "root", 0)
100
97
  }
@@ -73,6 +73,16 @@ func RunNext(root string, args []string, stdout, stderr io.Writer) int {
73
73
  fmt.Fprintf(stdout, "blocked: %s\n", unattributedMessage(slug, flagged))
74
74
  exit = 3
75
75
  }
76
+ if afkPresent(root) {
77
+ lines, err := FrontierLines(root, slug)
78
+ if err != nil {
79
+ fmt.Fprintf(stderr, "next: frontier: %v\n", err)
80
+ return 2
81
+ }
82
+ for _, line := range lines {
83
+ fmt.Fprintln(stdout, line)
84
+ }
85
+ }
76
86
  if report.Phase == state.PhaseBuild {
77
87
  printPlanBatch(stdout, root, slug)
78
88
  }
@@ -73,6 +73,10 @@ func runPathDisjoint(args []string, stdin io.Reader, stdout, stderr io.Writer) i
73
73
  return ExitOK
74
74
  }
75
75
 
76
+ // FrontierNotices exposes related work without broadening source-local selection.
77
+ // main wires it from lib to avoid an import cycle.
78
+ var FrontierNotices func(root, slug string) ([]string, error)
79
+
76
80
  func cmdSelect(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
77
81
  root := ""
78
82
  slug := ""
@@ -148,21 +152,18 @@ func cmdSelect(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
148
152
  return ExitUsage
149
153
  }
150
154
  if sawWorkspaces {
151
- order := []string{slug}
155
+ order := strings.Split(workspaces, ",")
152
156
  seen := make(map[string]bool)
153
157
  if err := validateSlug(slug); err != nil {
154
158
  fmt.Fprintf(stderr, "parallel select: %v\n", err)
155
159
  return ExitUsage
156
160
  }
157
- for _, target := range strings.Split(workspaces, ",") {
161
+ for _, target := range order {
158
162
  if err := validateSlug(target); err != nil || seen[target] {
159
163
  fmt.Fprintf(stderr, "parallel select: invalid or duplicate workspace %q\n", target)
160
164
  return ExitUsage
161
165
  }
162
166
  seen[target] = true
163
- if target != slug {
164
- order = append(order, target)
165
- }
166
167
  }
167
168
  if _, err := controllerLease(root, slug); err != nil {
168
169
  fmt.Fprintf(stderr, "parallel select: %v\n", err)
@@ -199,6 +200,16 @@ func cmdSelect(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
199
200
  slices = append(slices, ready...)
200
201
  }
201
202
  } else {
203
+ if FrontierNotices != nil {
204
+ notices, err := FrontierNotices(root, slug)
205
+ for _, notice := range notices {
206
+ fmt.Fprintln(stderr, notice)
207
+ }
208
+ if err != nil {
209
+ fmt.Fprintf(stderr, "parallel select: frontier: %v\n", err)
210
+ return ExitBlocked
211
+ }
212
+ }
202
213
  if PlanReady == nil {
203
214
  fmt.Fprintln(stderr, "parallel select: --from-plan is unavailable in this build")
204
215
  return ExitBlocked
@@ -217,8 +217,8 @@ type AddOpts struct {
217
217
  Capacity int
218
218
  }
219
219
 
220
- // Add refills freed slots of a running lease: each new slice gets a worktree
221
- // from the current control tip, under the same path-disjoint and cap rules as
220
+ // Add refills freed slots of a running, integrate-failed, or complete lease:
221
+ // each new slice gets a worktree from the current control tip, under the same path-disjoint and cap rules as
222
222
  // the slices still active. Landed (integrated) slices no longer claim paths.
223
223
  // An explicit Capacity with no Slices changes only the bound, not lease status.
224
224
  func Add(opts AddOpts) (lease *Lease, err error) {
@@ -262,8 +262,8 @@ func addLocked(opts AddOpts) (*Lease, error) {
262
262
  if opts.Capacity == 0 {
263
263
  opts.Capacity = leaseCapacity(lease)
264
264
  }
265
- if lease.Status != StatusRunning && lease.Status != StatusComplete {
266
- return nil, fmt.Errorf("add requires status=running|complete (have %s)", lease.Status)
265
+ if lease.Status != StatusRunning && lease.Status != StatusIntegrateFailed && lease.Status != StatusComplete {
266
+ return nil, fmt.Errorf("add requires status=running|integrate-failed|complete (have %s)", lease.Status)
267
267
  }
268
268
  head, err := revParse(opts.Root, expectedControl(lease))
269
269
  if err != nil {
@@ -329,7 +329,9 @@ func addLocked(opts AddOpts) (*Lease, error) {
329
329
  lease.Slices = append(lease.Slices, made...)
330
330
  lease.N = len(lease.Slices)
331
331
  lease.Capacity = opts.Capacity
332
- lease.Status = StatusRunning
332
+ if lease.Status == StatusComplete {
333
+ lease.Status = StatusRunning
334
+ }
333
335
  if err := WriteLease(leasePath, lease); err != nil {
334
336
  rollback()
335
337
  return nil, err
@@ -167,6 +167,19 @@ func controllerLease(root, controller string) (*repositoryLease, error) {
167
167
  return found, nil
168
168
  }
169
169
 
170
+ // ControllerLease returns the retained repository controller without changing its lease.
171
+ // A lease in another checkout requires recovery at that explicit control root.
172
+ func ControllerLease(repoRoot string) (string, *Lease, error) {
173
+ found, err := controllerLease(repoRoot, "")
174
+ if err != nil || found == nil {
175
+ return "", nil, err
176
+ }
177
+ if found.root != resolvedPath(repoRoot) {
178
+ return "", nil, fmt.Errorf("repository controller lease retained by %s at %s; recover at its explicit root", found.controller, found.root)
179
+ }
180
+ return found.controller, found.lease, nil
181
+ }
182
+
170
183
  // WorkspaceLeased protects both controller and source workspaces until cleanup.
171
184
  // devritesRoot is explicit; closeout holds WithControlLock through its rename.
172
185
  func WorkspaceLeased(devritesRoot, workspace string) (bool, error) {
@@ -16,10 +16,12 @@ import (
16
16
  "net/netip"
17
17
  "net/url"
18
18
  "os"
19
+ "os/exec"
19
20
  "path"
20
21
  "path/filepath"
21
22
  "regexp"
22
23
  "runtime"
24
+ "strconv"
23
25
  "strings"
24
26
  "time"
25
27
  )
@@ -162,13 +164,13 @@ func binaryNameFor(goos, goarch string) (string, error) {
162
164
  }
163
165
 
164
166
  func fetchJSON(ctx context.Context, rawURL string, out any) error {
165
- resp, err := get(ctx, rawURL)
167
+ resp, err := get(ctx, rawURL, true)
166
168
  if err != nil {
167
169
  return err
168
170
  }
169
171
  defer func() { _ = resp.Body.Close() }()
170
172
  if resp.StatusCode < 200 || resp.StatusCode > 299 {
171
- return fmt.Errorf("%s returned %s", resp.Request.URL.Redacted(), resp.Status)
173
+ return responseError(resp)
172
174
  }
173
175
  if resp.ContentLength > maxJSONBytes {
174
176
  return fmt.Errorf("JSON response exceeds %d bytes", maxJSONBytes)
@@ -228,13 +230,13 @@ func downloadVerified(ctx context.Context, rawURL, destination, filename string,
228
230
  }
229
231
 
230
232
  func download(ctx context.Context, rawURL, destination string, maxBytes int64, mode fs.FileMode) error {
231
- resp, err := get(ctx, rawURL)
233
+ resp, err := get(ctx, rawURL, false)
232
234
  if err != nil {
233
235
  return err
234
236
  }
235
237
  defer func() { _ = resp.Body.Close() }()
236
238
  if resp.StatusCode < 200 || resp.StatusCode > 299 {
237
- return fmt.Errorf("%s returned %s", resp.Request.URL.Redacted(), resp.Status)
239
+ return responseError(resp)
238
240
  }
239
241
  if resp.ContentLength > maxBytes {
240
242
  return fmt.Errorf("download exceeds %d bytes", maxBytes)
@@ -274,7 +276,7 @@ func download(ctx context.Context, rawURL, destination string, maxBytes int64, m
274
276
  return nil
275
277
  }
276
278
 
277
- func get(ctx context.Context, rawURL string) (*http.Response, error) {
279
+ func get(ctx context.Context, rawURL string, authenticate bool) (*http.Response, error) {
278
280
  req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
279
281
  if err != nil {
280
282
  return nil, fmt.Errorf("build release request: %w", err)
@@ -284,9 +286,94 @@ func get(ctx context.Context, rawURL string) (*http.Response, error) {
284
286
  }
285
287
  req.Header.Set("Accept", "application/vnd.github+json")
286
288
  req.Header.Set("User-Agent", "devrites-engine")
289
+ if authenticate && isGitHubAPI(req.URL) {
290
+ token, err := githubToken(ctx)
291
+ if err != nil {
292
+ return nil, err
293
+ }
294
+ if token != "" {
295
+ req.Header.Set("Authorization", "Bearer "+token)
296
+ }
297
+ }
287
298
  return httpClient.Do(req)
288
299
  }
289
300
 
301
+ func isGitHubAPI(u *url.URL) bool {
302
+ return u != nil && u.User == nil && strings.EqualFold(u.Scheme, "https") &&
303
+ (strings.EqualFold(u.Host, "api.github.com") || strings.EqualFold(u.Host, "api.github.com:443"))
304
+ }
305
+
306
+ // credentialOutput bounds untrusted helper output without accepting a truncated token.
307
+ type credentialOutput struct {
308
+ data [4096]byte
309
+ n int
310
+ }
311
+
312
+ func (out *credentialOutput) Write(p []byte) (int, error) {
313
+ if len(p) > len(out.data)-out.n {
314
+ return 0, errors.New("GitHub credential helper output exceeds limit")
315
+ }
316
+ out.n += copy(out.data[out.n:], p)
317
+ return len(p), nil
318
+ }
319
+
320
+ // githubToken follows gh's environment precedence without requiring gh to be installed.
321
+ func githubToken(ctx context.Context) (string, error) {
322
+ token := os.Getenv("GH_TOKEN")
323
+ if token == "" {
324
+ token = os.Getenv("GITHUB_TOKEN")
325
+ }
326
+ if token == "" {
327
+ lookupCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
328
+ defer cancel()
329
+ cmd := exec.CommandContext(lookupCtx, "gh", "auth", "token", "--hostname", "github.com")
330
+ cmd.Env = append(os.Environ(), "GH_PROMPT_DISABLED=1", "GIT_TERMINAL_PROMPT=0")
331
+ cmd.Stderr = io.Discard
332
+ cmd.WaitDelay = 100 * time.Millisecond
333
+ var output credentialOutput
334
+ cmd.Stdout = &output
335
+ err := cmd.Run()
336
+ if ctx.Err() != nil {
337
+ return "", ctx.Err()
338
+ }
339
+ if err != nil {
340
+ return "", nil
341
+ }
342
+ token = strings.TrimSpace(string(output.data[:output.n]))
343
+ }
344
+ for _, char := range token {
345
+ if char < '!' || char > '~' {
346
+ return "", errors.New("invalid GitHub credential; check GH_TOKEN, GITHUB_TOKEN, or gh auth login --hostname github.com")
347
+ }
348
+ }
349
+ return token, nil
350
+ }
351
+
352
+ func responseError(resp *http.Response) error {
353
+ message := fmt.Sprintf("release request returned HTTP %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
354
+ switch resp.StatusCode {
355
+ case http.StatusUnauthorized, http.StatusForbidden:
356
+ message += "; check GH_TOKEN/GITHUB_TOKEN (environment tokens take precedence) or run gh auth login --hostname github.com"
357
+ case http.StatusTooManyRequests:
358
+ message += "; GitHub API rate limit reached; use GH_TOKEN/GITHUB_TOKEN or gh auth login --hostname github.com, or wait before retrying"
359
+ }
360
+ if resp.StatusCode == http.StatusForbidden && resp.Header.Get("X-RateLimit-Remaining") == "0" {
361
+ message += "; GitHub API rate limit reached"
362
+ }
363
+ if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusTooManyRequests {
364
+ if reset, err := strconv.ParseInt(resp.Header.Get("X-RateLimit-Reset"), 10, 64); err == nil && reset >= 0 && reset <= 253402300799 {
365
+ message += "; rate limit resets at " + time.Unix(reset, 0).UTC().Format(time.RFC3339)
366
+ }
367
+ retry := resp.Header.Get("Retry-After")
368
+ if seconds, err := strconv.ParseUint(retry, 10, 32); err == nil {
369
+ message += fmt.Sprintf("; retry after %d seconds", seconds)
370
+ } else if date, err := http.ParseTime(retry); err == nil {
371
+ message += "; retry after " + date.UTC().Format(time.RFC3339)
372
+ }
373
+ }
374
+ return errors.New(message)
375
+ }
376
+
290
377
  func validateURL(u *url.URL) error {
291
378
  if u == nil || u.Hostname() == "" {
292
379
  return errors.New("release URL has no host")
@@ -303,6 +390,10 @@ func validateURL(u *url.URL) error {
303
390
  }
304
391
 
305
392
  func checkRedirect(req *http.Request, via []*http.Request) error {
393
+ // net/http forwards sensitive headers to subdomains; GitHub tokens must not follow.
394
+ if !isGitHubAPI(req.URL) {
395
+ req.Header.Del("Authorization")
396
+ }
306
397
  if len(via) >= 10 {
307
398
  return errors.New("stopped after 10 redirects")
308
399
  }
package/engine/main.go CHANGED
@@ -26,6 +26,7 @@ Usage:
26
26
  devrites-engine install [flags] Install DevRites skills, agents, and host configuration
27
27
  devrites-engine update [flags] Update an existing DevRites install in place
28
28
  devrites-engine uninstall [flags] Remove a DevRites install, preserving runtime state
29
+ devrites-engine frontier [slug] Discover related ready work and native preparation without writes
29
30
  devrites-engine check candidate <slug> Validate and hash the closed project candidate
30
31
  devrites-engine check readiness <slug> Check required files, tasks.md graph, and Build-input binding
31
32
  devrites-engine check readiness --emit-binding <slug> Emit the stable Build-input binding for Vet
@@ -126,6 +127,7 @@ func init() {
126
127
  // parallel cannot import lib (lib imports it), so `parallel select --from-plan` gets the plan-derived ready set through this hook.
127
128
  parallel.PlanReady = lib.PlanReady
128
129
  parallel.WorkspaceReady = lib.WorkspaceReady
130
+ parallel.FrontierNotices = lib.FrontierNotices
129
131
  // `gates precheck` runs `check windows` through this hook for the same reason.
130
132
  acceptance.WindowsCheck = lib.RunCheckWindows
131
133
  // ...and `gates precheck --slice` learns which writer gates to skip.
@@ -169,6 +171,8 @@ func run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
169
171
  return cmdOrient(root, args[1:], stdout, stderr)
170
172
  case "next":
171
173
  return lib.RunNext(root, args[1:], stdout, stderr)
174
+ case "frontier":
175
+ return lib.RunFrontier(root, args[1:], stdout, stderr)
172
176
  case "handoff":
173
177
  return lib.RunHandoff(root, args[1:], stdout, stderr)
174
178
  case "recall":
@@ -37,17 +37,16 @@ handles before admitting more work; status queried via ACTIVE/source reports the
37
37
  actual retained controller even after ordered handoff. Another retained
38
38
  controller is not bypassable; no extra cursor pointer is needed.
39
39
 
40
- The existing `--parallel N` is one global active-writer cap across all sources:
40
+ The existing `--parallel N` is one global active-writer cap, not a target guarantee:
41
41
  explicit invocation value, else AFK `max_parallel`, else retained lease capacity
42
42
  on resume, else 10. Explicit 1 means one writer, not one writer per workspace.
43
- Review queues obey host capacity and existing reviewer caps separately; they never
44
- erase required specialist accounts.
45
-
46
- Discover only recorded forward sequence continuations through `sequence_parent`
47
- and the parent's recorded objectives. Honor `continue_sequence: false`, opening
48
- budget and human bounded requests. Charge each opening once; reuse prepared
49
- children and their cursor rather than re-specifying or charging them again.
50
- An unrelated directory or user-mentioned slug is not automatic enrollment.
43
+ Review queues obey host capacity and existing reviewer caps separately; neither
44
+ capacity nor a Reslice cap waiver waives required named-role fanout or proof gates.
45
+
46
+ Use the engine frontier below for recorded source-related sequence membership.
47
+ Honor `continue_sequence: false`, opening budget and human bounded requests.
48
+ Charge each opening once; reuse prepared children and their cursor.
49
+ Unrelated directories or user-mentioned slugs are not automatic enrollment.
51
50
  Respect workspace claims: a live lookahead owner retains its artifacts until
52
51
  release/terminal reconciliation. Root alone writes workspace artifacts; leaves
53
52
  never write `.devrites/**`.
@@ -116,35 +115,37 @@ returns scheduling to Autocomplete; its source-local phase/recovery return curso
116
115
  and pending native reviewer handles remain intact. This is not a second phase
117
116
  controller or a persistent frontier artifact.
118
117
 
119
- 1. Re-read controller lease, per-source state/questions/readiness and authorized
120
- sequence. Examine every recorded authorized source once per pass, not only
121
- the owner's next local slice or first blocked child. Prepare absent targets
122
- through authorized native Spec → Clarify → Temper → Define/Plan → Vet; repair
123
- and re-Vet current readiness of prepared targets now, not after owner Seal.
124
- Assess actual source gates, claims and explicit planning/Build restrictions
125
- before routing. Owner-local red/human/access gates withhold that owner or its
126
- actual consumers, not independent authorized planning; true whole-run safety,
127
- access and claim constraints still bind. Retain concrete actions or blockers
118
+ 1. Run `devrites-engine frontier <source>` before local recovery or selection.
119
+ This read-only report names the related pool, preparation/repair actions and
120
+ blockers; it neither authorizes work nor changes the source's local cursor.
121
+ For `frontier-prepare` and `frontier-workspace` prepare/repair actions, execute
122
+ authorized native Spec → Clarify → Temper → Define/Plan → affected Vet now,
123
+ not after owner Seal. Check source gates, claims and planning/Build restrictions.
124
+ For `frontier-review-sequence`, read the named approved roadmap/artifacts:
125
+ native authority normalization must record the exact continuation through its
126
+ owning decision/phase before rerunning frontier. Ambiguous legacy prose,
127
+ positive remaining budget and path disjointness never imply permission.
128
+ Retain unresolved authority blockers; continue independent authorized work.
129
+ Owner-local red/human/access gates withhold that owner and actual consumers;
130
+ whole-run safety/access/claim constraints still bind. Record actions/blockers
128
131
  in existing source artifacts under normal write authority.
129
- Yield a child at its pending reviewer/phase boundary back to this controller;
130
- never recursively wait for child completion while parent returns are pending.
131
- Reconcile available parent returns before another pass. A native wait is
132
- justified only after concrete authorized actions/blockers across the pool
133
- have been examined; reading later phase skills is not pending Plan/Vet work.
134
- If an explicit/AFK capacity change differs from retained capacity, reconcile it
135
- before selection using `parallel add --root <repo> --slug <controller> --cap N --json -`
136
- with stdin `[]`. This changes capacity only, not workers or a complete lease's
137
- status. A decrease below active count refuses: pause admissions and drain to
138
- the requested count, then retry; never cancel workers to meet a lower cap.
139
- A pending landing is recovery, not free capacity: retry the exact recorded
140
- integrate before admission; unrelated HEAD/ref movement remains blocked.
141
- 2. Query the mixed pool in stable sequence order, controller first (the ordered
142
- ACTIVE owner may now differ):
143
- `devrites-engine parallel select --root <repo> --slug <controller> --from-plan --workspaces <controller,authorized-source,...> --cap N`.
144
- `--workspaces` is an internal data-source parameter, not a user feature flag.
145
- Read all notices and route repair/waits to their source. Existing running slots
146
- and paths seed selection globally. Zero new picks with live writers means keep
147
- planning/reviewing or wait, not Prove for those unfinished sources.
132
+ Yield children at pending reviewer/phase boundaries; reconcile available
133
+ parent returns before another pass. Reporting actions or reading phase skills
134
+ is not executing preparation/Vet. Wait only after authorized actions across
135
+ the pool have run or reached concrete pending handles/blockers.
136
+ 2. Reconcile explicit/AFK capacity changes with
137
+ `parallel add --root <repo> --slug <controller> --cap N --json -`, stdin `[]`.
138
+ This changes capacity only. Below active count, pause admissions, drain and
139
+ retry; never cancel workers to meet a lower cap. Recover a pending recorded
140
+ landing before admission; unrelated HEAD/ref movement remains blocked.
141
+ Rerun frontier after preparation/repair or capacity changes. Execute returned
142
+ `frontier-select-command` with only authorized live `--workspaces`: this exact
143
+ candidate list includes the `--slug` controller only when eligible. This is
144
+ internal, not a new user flag. `frontier: none` retains source-local routing.
145
+ Source-local `parallel select --from-plan` notices neither select globally nor
146
+ expand picks. Route notices to their source; running slots/paths seed mixed
147
+ selection globally. Zero picks with live writers means continue planning/review
148
+ or wait, not Prove for unfinished sources.
148
149
  3. Preflight selected slices against their SOURCE workspace with Build's normal
149
150
  slice/readiness/precondition, exact-path and runtime-isolation gates. Early
150
151
  foreign Build always uses isolated workers. For cap>1 on a capable host,
@@ -160,9 +161,9 @@ controller or a persistent frontier artifact.
160
161
  landing: defer control mutations while frozen validation is pending. Outside
161
162
  that window, land each individually green sibling through Build review/proof
162
163
  and `parallel integrate --slice <key>`; refill immediately from the whole pool.
163
- A red sibling repairs in place and holds its slot/paths; it does not delay
164
- independent safe landings. Never satisfy a producer edge on writer return or
165
- green-but-unlanded work.
164
+ A red or `integrate-failed` sibling retains its slot/paths for repair; it does
165
+ not halt independent safe landings/refill. A pending landing intent still
166
+ requires exact recovery above. Only durable integration satisfies producer edges.
166
167
  After worker-bound review/proof and source bookkeeping finish, prune only
167
168
  verified clean integrated checkouts with `parallel prune --root <repo>
168
169
  --slug <controller>`. Retain transfer refs/lease identities; dirty or ignored
@@ -86,9 +86,9 @@ next sequence continuation through Vet in a second session, then stop.
86
86
 
87
87
  1. **Orient and parse.** Resolve source per [phase target binding](../devrites-lib/reference/parallel-workspaces.md#phase-target-binding); when a workspace exists,
88
88
  require its `state.md` and read its cursor; a fresh idea starts empty at step 2.
89
- Before phase-local cold recovery, execute the bounded
90
- [global frontier pass](../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier);
91
- retain this source's return cursor and pending reviewer handles.
89
+ After normalizing arguments below, run `devrites-engine frontier <source>`
90
+ before phase-local cold recovery; execute its actions under the shared
91
+ [global frontier pass](../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier).
92
92
  Before honoring `blocked` with terminal `next_action`,
93
93
  reconcile retained consumptive evidence and fingerprint attempts from
94
94
  `drift.md`/`evidence.md`; a retained fingerprint below its cap resumes offline
@@ -102,7 +102,7 @@ next sequence continuation through Vet in a second session, then stop.
102
102
  Normalize current arguments to idea, `ship_preflight: yes|no`,
103
103
  `max_slices: unlimited`, `parallel: N|default`, `profile: standard|full`,
104
104
  and `cross_model: yes|no`.
105
- **Completion:** normalized state is unambiguous and no sentinel or workspace file has been written.
105
+ **Completion:** arguments and source authority are unambiguous; local cursor and pending handles remain intact.
106
106
  2. **Specify and clarify.** Run `devrites-interview`, `/rite-spec`, and
107
107
  `/rite-clarify` as one window. `open` material coverage never arms
108
108
  AFK. **Completion:** `Decision coverage: CLEAR` is durable.
@@ -119,12 +119,12 @@ next sequence continuation through Vet in a second session, then stop.
119
119
  `/rite-build` batch loop → `/rite-prove` → `/rite-polish` →
120
120
  `/rite-review` → `/rite-seal`. Read and execute each skill; durable files, not
121
121
  chat, carry state. Apply the mutable post-vet budget before first Build.
122
- Execute the shared [global frontier pass](../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier)
123
- at phase entry, before recovery, on returns and before native waits, not only
124
- during Build or after Seal. Preserve source-local phase cursors; Autocomplete
125
- retains repository scheduling. Apply its protected validation window before
126
- landings/checkpoints and account admission. Pass resolved `--parallel N` to
127
- Build under its one global cap and existing admission/isolation gates.
122
+ Run `devrites-engine frontier <source>` at phase entry, before recovery,
123
+ on returns and before native waits; execute the shared
124
+ [global frontier pass](../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier),
125
+ not merely its report. Autocomplete retains repository scheduling and local
126
+ cursors. Apply its protected validation window and pass resolved `--parallel N`
127
+ to Build under the same global cap and admission/isolation gates.
128
128
  **Completion:** loop reaches Seal GO or persists a valid stop before any later phase.
129
129
  5. **Apply stops.** At every gate use
130
130
  [stop-conditions.md](reference/stop-conditions.md). A `blocked` label alone is not a stop condition:
@@ -72,12 +72,12 @@ Workspace files, not chat, carry state. Read/execute each phase skill:
72
72
  | 10 | `/rite-seal` | GO/NO-GO; no Git |
73
73
  | 11 | `/rite-ship` | only after GO; `--ship` never authorizes Git; stop at literal-GO/native approval |
74
74
 
75
- At every phase entry, before cold/local recovery, on reviewer/planning returns and
76
- before native waits, Autocomplete executes the bounded
77
- [global frontier pass](../../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier).
78
- Keep source-local return cursors and pending handles; apply its protected
79
- validation window before accepting accounts or landing work. This scheduling
80
- precedes owner Seal and does not change ordered handoff below.
75
+ At phase entry, before cold/local recovery, on landing/reviewer/planning returns
76
+ and before native waits, run `devrites-engine frontier <source>` and execute the
77
+ shared [global frontier pass](../../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier).
78
+ Its report requires authorized preparation/Vet and mixed selection, not a pause.
79
+ Preserve local cursors, pending handles and protected validation windows;
80
+ scheduling precedes owner Seal without changing ordered handoff below.
81
81
 
82
82
  Check [stop-conditions.md](stop-conditions.md) before advancing. Discard stale
83
83
  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
@@ -4,11 +4,11 @@
4
4
 
5
5
  Opt-in only. Default `/rite-build` stays [`one-slice-cycle.md`](one-slice-cycle.md).
6
6
 
7
- Autocomplete first executes shared
8
- [`parallel-workspaces.md`](../../devrites-lib/reference/parallel-workspaces.md):
9
- it owns global admission, authorized source discovery and controller routing.
10
- The local selection below applies to standalone Build; mixed selection uses
11
- that reference and then this same per-slice review/repair/integration lifecycle.
7
+ Autocomplete runs `devrites-engine frontier <source>` and executes shared
8
+ [`parallel-workspaces.md`](../../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier):
9
+ it owns preparation, global admission and controller routing. Standalone Build
10
+ stays source-local; frontier notices from `parallel select --from-plan` do not
11
+ expand its picks. Autocomplete consumes `frontier-select-command`, then this lifecycle.
12
12
 
13
13
  ## Entry
14
14
 
@@ -161,8 +161,8 @@ means controller; CLI lease `--slice` uses source-qualified foreign keys.
161
161
  (imperative summary, never a slice id). Staging branch, durable exact landing
162
162
  intent, then FF; retry the same operation after interrupted persistence.
163
163
  The engine reconciles only that recorded landing and refuses unrelated
164
- HEAD/ref movement. Conflict → `integrate-failed`; classify under repair rounds
165
- (siblings stay in place).
164
+ HEAD/ref movement. Conflict → `integrate-failed`; repair in place, retaining
165
+ slot/paths while independent safe landings/refill continue.
166
166
  Never integrate because a wright returned.
167
167
  7. Each landing: FF control (the branch that held `HEAD` when the batch started —
168
168
  often `main`/`master`, otherwise whatever the user was on) by one local commit,