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.
- package/CHANGELOG.md +26 -3
- package/README.md +21 -1
- package/docs/cli.md +83 -6
- package/docs/command-map.md +2 -2
- package/docs/engine/commands.md +8 -3
- package/engine/commands.go +1 -1
- package/engine/help.go +3 -1
- package/engine/internal/lib/continuation.go +33 -4
- package/engine/internal/lib/dispatch.go +46 -31
- package/engine/internal/lib/frontier.go +808 -0
- package/engine/internal/lib/handoff.go +2 -5
- package/engine/internal/lib/next.go +10 -0
- package/engine/internal/parallel/cli.go +16 -5
- package/engine/internal/parallel/ops.go +7 -5
- package/engine/internal/parallel/repository.go +13 -0
- package/engine/internal/release/release.go +96 -5
- package/engine/main.go +4 -0
- package/pack/.claude/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +10 -10
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +6 -6
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +7 -7
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +10 -10
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +6 -6
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +7 -7
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +10 -10
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +6 -6
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +7 -7
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
- package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +10 -10
- package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +6 -6
- package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +7 -7
- package/pack/generated/dsh/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
- package/pack/generated/dsh/skills/rite-autocomplete/SKILL.md +10 -10
- package/pack/generated/dsh/skills/rite-autocomplete/reference/loop.md +6 -6
- package/pack/generated/dsh/skills/rite-build/reference/parallel-batch.md +7 -7
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
- package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +10 -10
- package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +6 -6
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +7 -7
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-workspaces.md +41 -40
- package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +10 -10
- package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +6 -6
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +7 -7
- 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
|
-
|
|
93
|
-
|
|
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 :=
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
164
|
-
independent safe landings.
|
|
165
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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:**
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
Build under
|
|
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
|
|
76
|
-
before native waits,
|
|
77
|
-
[global frontier pass](../../devrites-lib/reference/parallel-workspaces.md#continuous-global-frontier).
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
precedes owner Seal
|
|
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
|
|
8
|
-
[`parallel-workspaces.md`](../../devrites-lib/reference/parallel-workspaces.md):
|
|
9
|
-
it owns global admission
|
|
10
|
-
|
|
11
|
-
|
|
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`;
|
|
165
|
-
|
|
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,
|