devrites 6.1.0 → 6.1.2
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 +12 -0
- package/README.md +1 -1
- package/docs/architecture.md +3 -1
- package/docs/cli.md +4 -1
- package/docs/engine/commands.md +4 -4
- package/docs/orchestration.md +4 -3
- package/docs/usage.md +1 -1
- package/engine/internal/lib/claims.go +12 -2
- package/engine/internal/lib/claims_holder.go +124 -0
- package/engine/internal/lib/cli_observe.go +14 -0
- package/engine/internal/lib/taskgraph.go +124 -0
- package/engine/internal/parallel/cli.go +111 -9
- package/engine/internal/parallel/lease.go +58 -4
- package/engine/internal/parallel/ops.go +244 -75
- package/engine/internal/parallel/select.go +31 -6
- package/pack/.claude/agents/devrites-plan-drafter.md +2 -1
- package/pack/.claude/agents/devrites-slice-wright.md +2 -1
- package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +5 -4
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +7 -2
- package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +12 -15
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +28 -8
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/.claude/skills/devrites-lib/reference/standards/core.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/one-shot-actions.md +7 -6
- package/pack/.claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +10 -6
- package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +1 -1
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +13 -11
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +8 -6
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +28 -21
- package/pack/.claude/skills/rite-build/SKILL.md +7 -7
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +8 -5
- package/pack/.claude/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +1 -1
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +33 -26
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +10 -1
- package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +3 -2
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +2 -2
- package/pack/.claude/skills/rite-define/SKILL.md +1 -1
- package/pack/.claude/skills/rite-handoff/SKILL.md +1 -1
- package/pack/.claude/skills/rite-plan/SKILL.md +1 -1
- package/pack/.claude/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/.claude/skills/rite-prove/SKILL.md +4 -2
- package/pack/.claude/skills/rite-prove/reference/failure-triage.md +2 -1
- package/pack/.claude/skills/rite-spec/reference/state-workspace.md +2 -2
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +8 -0
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +1 -1
- package/pack/generated/claude/agents/devrites-plan-drafter.md +2 -1
- package/pack/generated/claude/agents/devrites-slice-wright.md +2 -1
- package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +5 -4
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +7 -2
- package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +12 -15
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +28 -8
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/one-shot-actions.md +7 -6
- package/pack/generated/claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +10 -6
- package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +1 -1
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +13 -11
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +8 -6
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +28 -21
- package/pack/generated/claude/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +8 -5
- package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +33 -26
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +10 -1
- package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +3 -2
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +2 -2
- package/pack/generated/claude/skills/rite-define/SKILL.md +1 -1
- package/pack/generated/claude/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/claude/skills/rite-plan/SKILL.md +1 -1
- package/pack/generated/claude/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/claude/skills/rite-prove/SKILL.md +4 -2
- package/pack/generated/claude/skills/rite-prove/reference/failure-triage.md +2 -1
- package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +2 -2
- package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +8 -0
- package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +1 -1
- package/pack/generated/codex/agents/devrites-plan-drafter.toml +2 -1
- package/pack/generated/codex/agents/devrites-slice-wright.toml +2 -1
- package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +5 -4
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +7 -2
- package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +12 -15
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +28 -8
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/one-shot-actions.md +7 -6
- package/pack/generated/codex/skills/devrites-lib/reference/standards/workflow-artifacts.md +10 -6
- package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +1 -1
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +13 -11
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +8 -6
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +28 -21
- package/pack/generated/codex/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +8 -5
- package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +33 -26
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +10 -1
- package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +3 -2
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +2 -2
- package/pack/generated/codex/skills/rite-define/SKILL.md +1 -1
- package/pack/generated/codex/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/codex/skills/rite-plan/SKILL.md +1 -1
- package/pack/generated/codex/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/codex/skills/rite-prove/SKILL.md +4 -2
- package/pack/generated/codex/skills/rite-prove/reference/failure-triage.md +2 -1
- package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +2 -2
- package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +8 -0
- package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +1 -1
- package/pack/generated/devin/agents/devrites-plan-drafter.md +2 -1
- package/pack/generated/devin/agents/devrites-slice-wright.md +2 -1
- package/pack/generated/devin/skills/devrites-debug-recovery/SKILL.md +5 -4
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +7 -2
- package/pack/generated/devin/skills/devrites-lib/reference/reply-contract.md +12 -15
- package/pack/generated/devin/skills/devrites-lib/reference/standards/afk-hitl.md +28 -8
- package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/devin/skills/devrites-lib/reference/standards/core.md +1 -1
- package/pack/generated/devin/skills/devrites-lib/reference/standards/one-shot-actions.md +7 -6
- package/pack/generated/devin/skills/devrites-lib/reference/standards/workflow-artifacts.md +10 -6
- package/pack/generated/devin/skills/devrites-lib/reference/workspace-artifact-schema.md +1 -1
- package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +13 -11
- package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +8 -6
- package/pack/generated/devin/skills/rite-autocomplete/reference/stop-conditions.md +28 -21
- package/pack/generated/devin/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/devin/skills/rite-build/reference/afk-discipline.md +8 -5
- package/pack/generated/devin/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/one-slice-cycle.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +33 -26
- package/pack/generated/devin/skills/rite-build/reference/phase-contract.md +10 -1
- package/pack/generated/devin/skills/rite-build/reference/spec-drift-guard.md +3 -2
- package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +2 -2
- package/pack/generated/devin/skills/rite-define/SKILL.md +1 -1
- package/pack/generated/devin/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/devin/skills/rite-plan/SKILL.md +1 -1
- package/pack/generated/devin/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/devin/skills/rite-prove/SKILL.md +4 -2
- package/pack/generated/devin/skills/rite-prove/reference/failure-triage.md +2 -1
- package/pack/generated/devin/skills/rite-spec/reference/state-workspace.md +2 -2
- package/pack/generated/devin/skills/rite-vet/reference/artifacts.md +8 -0
- package/pack/generated/devin/skills/rite-vet/reference/review-axes.md +1 -1
- package/pack/generated/omp/agents/devrites-plan-drafter.md +2 -1
- package/pack/generated/omp/agents/devrites-slice-wright.md +2 -1
- package/pack/generated/omp/skills/devrites-debug-recovery/SKILL.md +5 -4
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +7 -2
- package/pack/generated/omp/skills/devrites-lib/reference/reply-contract.md +12 -15
- package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +28 -8
- package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/omp/skills/devrites-lib/reference/standards/core.md +1 -1
- package/pack/generated/omp/skills/devrites-lib/reference/standards/one-shot-actions.md +7 -6
- package/pack/generated/omp/skills/devrites-lib/reference/standards/workflow-artifacts.md +10 -6
- package/pack/generated/omp/skills/devrites-lib/reference/workspace-artifact-schema.md +1 -1
- package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +13 -11
- package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +8 -6
- package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +28 -21
- package/pack/generated/omp/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +8 -5
- package/pack/generated/omp/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/one-slice-cycle.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +33 -26
- package/pack/generated/omp/skills/rite-build/reference/phase-contract.md +10 -1
- package/pack/generated/omp/skills/rite-build/reference/spec-drift-guard.md +3 -2
- package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +2 -2
- package/pack/generated/omp/skills/rite-define/SKILL.md +1 -1
- package/pack/generated/omp/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/omp/skills/rite-plan/SKILL.md +1 -1
- package/pack/generated/omp/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/omp/skills/rite-prove/SKILL.md +4 -2
- package/pack/generated/omp/skills/rite-prove/reference/failure-triage.md +2 -1
- package/pack/generated/omp/skills/rite-spec/reference/state-workspace.md +2 -2
- package/pack/generated/omp/skills/rite-vet/reference/artifacts.md +8 -0
- package/pack/generated/omp/skills/rite-vet/reference/review-axes.md +1 -1
- package/pack/generated/pi/agents/devrites-plan-drafter.md +2 -1
- package/pack/generated/pi/agents/devrites-slice-wright.md +2 -1
- package/pack/generated/pi/skills/devrites-debug-recovery/SKILL.md +5 -4
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +7 -2
- package/pack/generated/pi/skills/devrites-lib/reference/reply-contract.md +12 -15
- package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +28 -8
- package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/pi/skills/devrites-lib/reference/standards/core.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/one-shot-actions.md +7 -6
- package/pack/generated/pi/skills/devrites-lib/reference/standards/workflow-artifacts.md +10 -6
- package/pack/generated/pi/skills/devrites-lib/reference/workspace-artifact-schema.md +1 -1
- package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +13 -11
- package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +8 -6
- package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +28 -21
- package/pack/generated/pi/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +8 -5
- package/pack/generated/pi/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/one-slice-cycle.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +33 -26
- package/pack/generated/pi/skills/rite-build/reference/phase-contract.md +10 -1
- package/pack/generated/pi/skills/rite-build/reference/spec-drift-guard.md +3 -2
- package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +2 -2
- package/pack/generated/pi/skills/rite-define/SKILL.md +1 -1
- package/pack/generated/pi/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/pi/skills/rite-plan/SKILL.md +1 -1
- package/pack/generated/pi/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/pi/skills/rite-prove/SKILL.md +4 -2
- package/pack/generated/pi/skills/rite-prove/reference/failure-triage.md +2 -1
- package/pack/generated/pi/skills/rite-spec/reference/state-workspace.md +2 -2
- package/pack/generated/pi/skills/rite-vet/reference/artifacts.md +8 -0
- package/pack/generated/pi/skills/rite-vet/reference/review-axes.md +1 -1
- package/package.json +1 -1
|
@@ -90,70 +90,182 @@ func createLocked(opts CreateOpts) (*Lease, error) {
|
|
|
90
90
|
session = fmt.Sprintf("pid:%d", os.Getpid())
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
slices, cleanupPartial, err := createWorktrees(opts.Root, opts.Slug, opts.BatchID, base, opts.Slices)
|
|
94
|
+
if err != nil {
|
|
95
|
+
return nil, err
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
lease := &Lease{
|
|
99
|
+
BatchID: opts.BatchID,
|
|
100
|
+
CreatedAt: NowUTC(),
|
|
101
|
+
BaseSHA: base,
|
|
102
|
+
N: len(slices),
|
|
103
|
+
Status: StatusRunning,
|
|
104
|
+
ControlPIDOrSession: session,
|
|
105
|
+
Slices: slices,
|
|
106
|
+
}
|
|
107
|
+
if err := WriteLease(leasePath, lease); err != nil {
|
|
108
|
+
cleanupPartial()
|
|
109
|
+
return nil, err
|
|
110
|
+
}
|
|
111
|
+
ensureScratchExcluded(opts.Root)
|
|
112
|
+
return lease, nil
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// createWorktrees makes one pending worker worktree and branch per slice from
|
|
116
|
+
// base, all or nothing: a failure removes what this call already made. The
|
|
117
|
+
// returned rollback undoes a success when the caller's lease write fails.
|
|
118
|
+
func createWorktrees(root, slug, batchID, base string, specs []SlicePaths) ([]LeaseSlice, func(), error) {
|
|
119
|
+
made := make([]LeaseSlice, 0, len(specs))
|
|
120
|
+
rollback := func() {
|
|
121
|
+
// Worktrees first: git refuses to delete a branch still checked out.
|
|
122
|
+
for _, sl := range made {
|
|
123
|
+
if err := removeWorktree(root, sl.WorktreePath); err != nil {
|
|
124
|
+
warnf("worktree cleanup %s: %v", sl.WorktreePath, err)
|
|
99
125
|
}
|
|
100
126
|
}
|
|
101
|
-
for _,
|
|
102
|
-
if err := deleteBranch(
|
|
103
|
-
warnf("branch cleanup %s: %v",
|
|
127
|
+
for _, sl := range made {
|
|
128
|
+
if err := deleteBranch(root, sl.Branch); err != nil {
|
|
129
|
+
warnf("branch cleanup %s: %v", sl.Branch, err)
|
|
104
130
|
}
|
|
105
131
|
}
|
|
106
132
|
}
|
|
107
|
-
|
|
133
|
+
for _, sp := range specs {
|
|
134
|
+
sl, err := createWorktree(root, slug, batchID, base, sp)
|
|
135
|
+
if err != nil {
|
|
136
|
+
rollback()
|
|
137
|
+
return nil, nil, err
|
|
138
|
+
}
|
|
139
|
+
made = append(made, sl)
|
|
140
|
+
}
|
|
141
|
+
return made, rollback, nil
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
func createWorktree(root, slug, batchID, base string, sp SlicePaths) (LeaseSlice, error) {
|
|
145
|
+
if err := validateSliceID(sp.ID); err != nil {
|
|
146
|
+
return LeaseSlice{}, err
|
|
147
|
+
}
|
|
148
|
+
paths, err := validateSlicePaths(sp.Paths, fmt.Sprintf("slice %q", sp.ID), root)
|
|
149
|
+
if err != nil {
|
|
150
|
+
return LeaseSlice{}, err
|
|
151
|
+
}
|
|
152
|
+
branch := WorkerBranch(slug, batchID, sp.ID)
|
|
153
|
+
wt := WorkerWorktreePath(root, batchID, sp.ID)
|
|
154
|
+
if _, err := os.Stat(wt); err == nil {
|
|
155
|
+
return LeaseSlice{}, fmt.Errorf("worktree path already exists: %s", wt)
|
|
156
|
+
}
|
|
157
|
+
if err := os.MkdirAll(filepath.Dir(wt), 0o755); err != nil {
|
|
158
|
+
return LeaseSlice{}, err
|
|
159
|
+
}
|
|
160
|
+
if err := worktreeAdd(root, wt, branch, base); err != nil {
|
|
161
|
+
return LeaseSlice{}, fmt.Errorf("worktree add %s: %w", sp.ID, err)
|
|
162
|
+
}
|
|
163
|
+
return LeaseSlice{
|
|
164
|
+
ID: sp.ID,
|
|
165
|
+
Paths: paths,
|
|
166
|
+
WorktreePath: wt,
|
|
167
|
+
Branch: branch,
|
|
168
|
+
WrightStatus: WrightPending,
|
|
169
|
+
}, nil
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// AddOpts configures refilling a running lease with new slices.
|
|
173
|
+
type AddOpts struct {
|
|
174
|
+
Root string
|
|
175
|
+
Slug string
|
|
176
|
+
Slices []SlicePaths
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Add refills freed slots of a running lease: each new slice gets a worktree
|
|
180
|
+
// from the current control tip, under the same path-disjoint and cap rules as
|
|
181
|
+
// the slices still active. Landed (integrated) slices no longer claim paths.
|
|
182
|
+
func Add(opts AddOpts) (lease *Lease, err error) {
|
|
183
|
+
err = withLeaseLock(opts.Root, opts.Slug, func() error {
|
|
184
|
+
lease, err = addLocked(opts)
|
|
185
|
+
return err
|
|
186
|
+
})
|
|
187
|
+
return lease, err
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
func addLocked(opts AddOpts) (*Lease, error) {
|
|
191
|
+
if abs, err := filepath.Abs(opts.Root); err == nil {
|
|
192
|
+
opts.Root = abs
|
|
193
|
+
}
|
|
194
|
+
if len(opts.Slices) == 0 {
|
|
195
|
+
return nil, fmt.Errorf("add requires at least one slice")
|
|
196
|
+
}
|
|
197
|
+
leasePath, err := LeasePath(opts.Root, opts.Slug)
|
|
198
|
+
if err != nil {
|
|
199
|
+
return nil, err
|
|
200
|
+
}
|
|
201
|
+
lease, err := ReadLease(leasePath)
|
|
202
|
+
if err != nil {
|
|
203
|
+
return nil, err
|
|
204
|
+
}
|
|
205
|
+
if lease.Status != StatusRunning {
|
|
206
|
+
return nil, fmt.Errorf("add requires status=running (have %s)", lease.Status)
|
|
207
|
+
}
|
|
208
|
+
head, err := revParse(opts.Root, expectedControl(lease))
|
|
209
|
+
if err != nil {
|
|
210
|
+
return nil, err
|
|
211
|
+
}
|
|
212
|
+
if err := ensureControlAtBase(opts.Root, head); err != nil {
|
|
213
|
+
return nil, err
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
seen := make(map[string]bool, len(lease.Slices)+len(opts.Slices))
|
|
217
|
+
for _, sl := range lease.Slices {
|
|
218
|
+
seen[sl.ID] = true
|
|
219
|
+
}
|
|
220
|
+
added := make([]SlicePaths, 0, len(opts.Slices))
|
|
221
|
+
var addedPaths []string
|
|
108
222
|
for _, sp := range opts.Slices {
|
|
109
223
|
if err := validateSliceID(sp.ID); err != nil {
|
|
110
|
-
cleanupPartial()
|
|
111
224
|
return nil, err
|
|
112
225
|
}
|
|
226
|
+
if seen[sp.ID] {
|
|
227
|
+
return nil, fmt.Errorf("slice id %q already in lease", sp.ID)
|
|
228
|
+
}
|
|
229
|
+
seen[sp.ID] = true
|
|
113
230
|
paths, err := validateSlicePaths(sp.Paths, fmt.Sprintf("slice %q", sp.ID), opts.Root)
|
|
114
231
|
if err != nil {
|
|
115
|
-
cleanupPartial()
|
|
116
232
|
return nil, err
|
|
117
233
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
234
|
+
added = append(added, SlicePaths{ID: sp.ID, Paths: paths})
|
|
235
|
+
addedPaths = append(addedPaths, paths...)
|
|
236
|
+
}
|
|
237
|
+
all := append(activeSlices(lease), added...)
|
|
238
|
+
if len(all) > MaxParallelSlices {
|
|
239
|
+
return nil, fmt.Errorf("add would make %d slices active; parallel allows at most %d", len(all), MaxParallelSlices)
|
|
240
|
+
}
|
|
241
|
+
if len(all) >= 2 {
|
|
242
|
+
if _, err := CheckPathDisjoint(all, opts.Root); err != nil {
|
|
126
243
|
return nil, err
|
|
127
244
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
WorktreePath: wt,
|
|
138
|
-
Branch: branch,
|
|
139
|
-
WrightStatus: WrightPending,
|
|
140
|
-
})
|
|
245
|
+
}
|
|
246
|
+
// The new worktree starts from control HEAD, so uncommitted user work on
|
|
247
|
+
// its paths would be neither in the slice nor safe from its landing.
|
|
248
|
+
dirty, err := porcelainDirtyPaths(opts.Root, addedPaths)
|
|
249
|
+
if err != nil {
|
|
250
|
+
return nil, err
|
|
251
|
+
}
|
|
252
|
+
if dirty {
|
|
253
|
+
return nil, fmt.Errorf("control has uncommitted changes on new slice paths; stop for the human to resolve them (never stash or commit them), then rerun add")
|
|
141
254
|
}
|
|
142
255
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
ControlPIDOrSession: session,
|
|
150
|
-
Slices: slices,
|
|
256
|
+
made, rollback, err := createWorktrees(opts.Root, opts.Slug, lease.BatchID, head, added)
|
|
257
|
+
if err != nil {
|
|
258
|
+
return nil, err
|
|
259
|
+
}
|
|
260
|
+
for i := range made {
|
|
261
|
+
made[i].BaseSHA = head
|
|
151
262
|
}
|
|
263
|
+
lease.Slices = append(lease.Slices, made...)
|
|
264
|
+
lease.N = len(lease.Slices)
|
|
152
265
|
if err := WriteLease(leasePath, lease); err != nil {
|
|
153
|
-
|
|
266
|
+
rollback()
|
|
154
267
|
return nil, err
|
|
155
268
|
}
|
|
156
|
-
ensureScratchExcluded(opts.Root)
|
|
157
269
|
return lease, nil
|
|
158
270
|
}
|
|
159
271
|
|
|
@@ -185,6 +297,10 @@ func recordGreenLocked(repoRoot, slug, sliceID, commit string) (*Lease, error) {
|
|
|
185
297
|
found := false
|
|
186
298
|
for i := range lease.Slices {
|
|
187
299
|
if lease.Slices[i].ID == sliceID {
|
|
300
|
+
if lease.Slices[i].WrightStatus == WrightIntegrated {
|
|
301
|
+
// Re-greening a landed slice would land its work a second time.
|
|
302
|
+
return nil, fmt.Errorf("slice %s already integrated", sliceID)
|
|
303
|
+
}
|
|
188
304
|
lease.Slices[i].WrightStatus = WrightGreen
|
|
189
305
|
lease.Slices[i].TransferCommit = sha
|
|
190
306
|
found = true
|
|
@@ -222,11 +338,11 @@ func abortLocked(repoRoot, slug string) (*Lease, error) {
|
|
|
222
338
|
if lease.Status == StatusComplete {
|
|
223
339
|
return nil, fmt.Errorf("abort refused: lease already complete — the batch landed in control; nothing to discard")
|
|
224
340
|
}
|
|
225
|
-
|
|
341
|
+
expected, err := revParse(repoRoot, expectedControl(lease))
|
|
226
342
|
if err != nil {
|
|
227
343
|
return nil, err
|
|
228
344
|
}
|
|
229
|
-
if err := ensureControlAtBase(repoRoot,
|
|
345
|
+
if err := ensureControlAtBase(repoRoot, expected); err != nil {
|
|
230
346
|
warnf("control not at base: %v", err)
|
|
231
347
|
}
|
|
232
348
|
lease.Status = StatusAborted
|
|
@@ -252,12 +368,17 @@ func ensureControlAtBase(repoRoot, base string) error {
|
|
|
252
368
|
|
|
253
369
|
// IntegrateOpts configures staging integrate.
|
|
254
370
|
type IntegrateOpts struct {
|
|
255
|
-
Root
|
|
256
|
-
Slug
|
|
371
|
+
Root string
|
|
372
|
+
Slug string
|
|
373
|
+
// Slice, when set, lands only that slice (requires ApplyToControl);
|
|
374
|
+
// otherwise every slice not yet integrated lands together.
|
|
375
|
+
Slice string
|
|
257
376
|
ApplyToControl bool
|
|
258
377
|
}
|
|
259
378
|
|
|
260
|
-
// Integrate all-or-nothing applies
|
|
379
|
+
// Integrate all-or-nothing applies transfer commits onto a staging branch
|
|
380
|
+
// cut from the expected control tip — every not-yet-integrated slice, or just
|
|
381
|
+
// opts.Slice — and with ApplyToControl fast-forwards control to it.
|
|
261
382
|
func Integrate(opts IntegrateOpts) (tip string, lease *Lease, err error) {
|
|
262
383
|
err = withLeaseLock(opts.Root, opts.Slug, func() error {
|
|
263
384
|
tip, lease, err = integrateLocked(opts)
|
|
@@ -267,6 +388,9 @@ func Integrate(opts IntegrateOpts) (tip string, lease *Lease, err error) {
|
|
|
267
388
|
}
|
|
268
389
|
|
|
269
390
|
func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
|
|
391
|
+
if opts.Slice != "" && !opts.ApplyToControl {
|
|
392
|
+
return "", nil, fmt.Errorf("integrate --slice requires --apply-to-control")
|
|
393
|
+
}
|
|
270
394
|
leasePath, err := LeasePath(opts.Root, opts.Slug)
|
|
271
395
|
if err != nil {
|
|
272
396
|
return "", nil, err
|
|
@@ -278,17 +402,36 @@ func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
|
|
|
278
402
|
if lease.Status != StatusRunning && lease.Status != StatusIntegrateFailed {
|
|
279
403
|
return "", nil, fmt.Errorf("integrate requires status=running|integrate-failed (have %s)", lease.Status)
|
|
280
404
|
}
|
|
281
|
-
|
|
405
|
+
// Control may only have moved by this lease's own landings; anything else
|
|
406
|
+
// is external work that integrate must neither rewind nor build on.
|
|
407
|
+
expected, err := revParse(opts.Root, expectedControl(lease))
|
|
282
408
|
if err != nil {
|
|
283
409
|
return "", nil, err
|
|
284
410
|
}
|
|
285
|
-
if err := ensureControlAtBase(opts.Root,
|
|
411
|
+
if err := ensureControlAtBase(opts.Root, expected); err != nil {
|
|
286
412
|
return "", nil, err
|
|
287
413
|
}
|
|
288
414
|
|
|
289
|
-
|
|
290
|
-
for
|
|
291
|
-
|
|
415
|
+
var targets []int
|
|
416
|
+
for i, sl := range lease.Slices {
|
|
417
|
+
switch {
|
|
418
|
+
case opts.Slice == "" && sl.WrightStatus != WrightIntegrated,
|
|
419
|
+
opts.Slice != "" && sl.ID == opts.Slice:
|
|
420
|
+
targets = append(targets, i)
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
switch {
|
|
424
|
+
case opts.Slice != "" && len(targets) == 0:
|
|
425
|
+
return "", nil, fmt.Errorf("slice not in lease: %s", opts.Slice)
|
|
426
|
+
case opts.Slice != "" && lease.Slices[targets[0]].WrightStatus == WrightIntegrated:
|
|
427
|
+
return "", nil, fmt.Errorf("slice %s already integrated", opts.Slice)
|
|
428
|
+
case len(targets) == 0:
|
|
429
|
+
return "", nil, fmt.Errorf("no slices left to integrate")
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
union := make([]string, 0, len(targets))
|
|
433
|
+
for _, i := range targets {
|
|
434
|
+
union = append(union, lease.Slices[i].Paths...)
|
|
292
435
|
}
|
|
293
436
|
if opts.ApplyToControl {
|
|
294
437
|
// FF into control must not silently absorb or clobber the user's
|
|
@@ -303,7 +446,8 @@ func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
|
|
|
303
446
|
}
|
|
304
447
|
}
|
|
305
448
|
|
|
306
|
-
for _,
|
|
449
|
+
for _, i := range targets {
|
|
450
|
+
sl := lease.Slices[i]
|
|
307
451
|
if sl.WrightStatus != WrightGreen {
|
|
308
452
|
return "", nil, fmt.Errorf("slice %s is not green", sl.ID)
|
|
309
453
|
}
|
|
@@ -326,7 +470,7 @@ func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
|
|
|
326
470
|
if err := os.MkdirAll(filepath.Dir(stageWT), 0o755); err != nil {
|
|
327
471
|
return "", nil, err
|
|
328
472
|
}
|
|
329
|
-
if err := worktreeAdd(opts.Root, stageWT, ibranch,
|
|
473
|
+
if err := worktreeAdd(opts.Root, stageWT, ibranch, expected); err != nil {
|
|
330
474
|
return "", nil, fmt.Errorf("staging worktree: %w", err)
|
|
331
475
|
}
|
|
332
476
|
|
|
@@ -350,13 +494,20 @@ func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
|
|
|
350
494
|
leasePath, StatusRunning, retryErr)
|
|
351
495
|
}
|
|
352
496
|
}
|
|
353
|
-
if err := ensureControlAtBase(opts.Root,
|
|
497
|
+
if err := ensureControlAtBase(opts.Root, expected); err != nil {
|
|
354
498
|
warnf("control diverged from base during integrate: %v", err)
|
|
355
499
|
}
|
|
356
500
|
return "", lease, reason
|
|
357
501
|
}
|
|
358
502
|
|
|
359
|
-
for _,
|
|
503
|
+
for _, i := range targets {
|
|
504
|
+
sl := lease.Slices[i]
|
|
505
|
+
// Each slice is proven against the commit its worktree was cut from,
|
|
506
|
+
// which for a refill is a later control tip than the batch base.
|
|
507
|
+
base, err := revParse(opts.Root, sliceBase(lease, sl))
|
|
508
|
+
if err != nil {
|
|
509
|
+
return fail(fmt.Errorf("slice %s base: %w", sl.ID, err))
|
|
510
|
+
}
|
|
360
511
|
tc, err := revParse(opts.Root, sl.TransferCommit)
|
|
361
512
|
if err != nil {
|
|
362
513
|
return fail(fmt.Errorf("slice %s transfer: %w", sl.ID, err))
|
|
@@ -417,11 +568,20 @@ func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
|
|
|
417
568
|
if err := mergeFFOnly(opts.Root, tip); err != nil {
|
|
418
569
|
return fail(fmt.Errorf("control fast-forward to integrate tip failed: %w", err))
|
|
419
570
|
}
|
|
420
|
-
|
|
571
|
+
for _, i := range targets {
|
|
572
|
+
lease.Slices[i].WrightStatus = WrightIntegrated
|
|
573
|
+
}
|
|
574
|
+
lease.ControlTip = tip
|
|
575
|
+
// A per-slice landing also clears an earlier integrate-failed: the
|
|
576
|
+
// lease is healthy again while other slices are still in flight.
|
|
577
|
+
lease.Status = StatusRunning
|
|
578
|
+
if len(activeSlices(lease)) == 0 {
|
|
579
|
+
lease.Status = StatusComplete
|
|
580
|
+
}
|
|
421
581
|
if err := WriteLease(leasePath, lease); err != nil {
|
|
422
582
|
return "", nil, err
|
|
423
583
|
}
|
|
424
|
-
} else if err := ensureControlAtBase(opts.Root,
|
|
584
|
+
} else if err := ensureControlAtBase(opts.Root, expected); err != nil {
|
|
425
585
|
return fail(err)
|
|
426
586
|
}
|
|
427
587
|
// Without --apply-to-control the lease stays running: the integrate
|
|
@@ -529,11 +689,12 @@ func salvageSlice(sl LeaseSlice, batchID string) (string, error) {
|
|
|
529
689
|
fmt.Sprintf("devrites: salvage WIP (%s)", batchID), sl.Paths)
|
|
530
690
|
}
|
|
531
691
|
|
|
532
|
-
// Cleanup removes worker worktrees and clears the lease. A complete batch
|
|
533
|
-
// deletes the merged slice branches; a forced
|
|
534
|
-
// salvages each sibling — uncommitted
|
|
535
|
-
// the slice branch — and keeps every
|
|
536
|
-
// durable rejected-work
|
|
692
|
+
// Cleanup removes worker worktrees and clears the lease. A complete batch, and
|
|
693
|
+
// any slice already integrated, deletes the merged slice branches; a forced
|
|
694
|
+
// non-complete cleanup first salvages each other sibling — uncommitted
|
|
695
|
+
// allowlisted changes become a commit on the slice branch — and keeps every
|
|
696
|
+
// branch whose tip moved past that slice's base as the durable rejected-work
|
|
697
|
+
// ref.
|
|
537
698
|
func Cleanup(repoRoot, slug string, force bool) (salvaged []Salvage, err error) {
|
|
538
699
|
err = withLeaseLock(repoRoot, slug, func() error {
|
|
539
700
|
salvaged, err = cleanupLocked(repoRoot, slug, force)
|
|
@@ -558,14 +719,6 @@ func cleanupLocked(repoRoot, slug string, force bool) ([]Salvage, error) {
|
|
|
558
719
|
return nil, fmt.Errorf("cleanup refuses status=%s without --force (non-complete cleanup salvages work onto slice branches)", lease.Status)
|
|
559
720
|
}
|
|
560
721
|
complete := lease.Status == StatusComplete
|
|
561
|
-
base := ""
|
|
562
|
-
if !complete {
|
|
563
|
-
if b, err := revParse(repoRoot, lease.BaseSHA); err != nil {
|
|
564
|
-
warnf("base sha %s: %v", lease.BaseSHA, err)
|
|
565
|
-
} else {
|
|
566
|
-
base = b
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
722
|
// Ownership comes from what git registered, read before anything is
|
|
570
723
|
// removed, never from the lease's own claim.
|
|
571
724
|
registered, err := registeredWorktrees(repoRoot)
|
|
@@ -576,6 +729,9 @@ func cleanupLocked(repoRoot, slug string, force bool) ([]Salvage, error) {
|
|
|
576
729
|
var failed, stranded, unconfirmed []string
|
|
577
730
|
keptWorktrees := map[string]bool{}
|
|
578
731
|
for _, sl := range lease.Slices {
|
|
732
|
+
// A slice integrate already landed is done like a complete batch: its
|
|
733
|
+
// work is on control, so there is nothing to salvage or keep.
|
|
734
|
+
landed := complete || sl.WrightStatus == WrightIntegrated
|
|
579
735
|
salvageFailed := false
|
|
580
736
|
// Only the deterministic scratch layout is ours to touch. A forged or
|
|
581
737
|
// stale lease worktree_path is never salvaged into (git ops would run
|
|
@@ -595,7 +751,7 @@ func cleanupLocked(repoRoot, slug string, force bool) ([]Salvage, error) {
|
|
|
595
751
|
sl.ID, sl.WorktreePath, ScratchRoot(repoRoot))
|
|
596
752
|
}
|
|
597
753
|
}
|
|
598
|
-
if !
|
|
754
|
+
if !landed && owned {
|
|
599
755
|
if _, err := salvage(sl, lease.BatchID); err != nil {
|
|
600
756
|
warnf("salvage %s: %v", sl.ID, err)
|
|
601
757
|
salvageFailed = true
|
|
@@ -631,7 +787,13 @@ func cleanupLocked(repoRoot, slug string, force bool) ([]Salvage, error) {
|
|
|
631
787
|
if sl.Branch != "" {
|
|
632
788
|
keep := salvageFailed
|
|
633
789
|
tip := ""
|
|
634
|
-
if !
|
|
790
|
+
if !landed {
|
|
791
|
+
base := ""
|
|
792
|
+
if b, err := revParse(repoRoot, sliceBase(lease, sl)); err != nil {
|
|
793
|
+
warnf("base sha %s: %v", sliceBase(lease, sl), err)
|
|
794
|
+
} else {
|
|
795
|
+
base = b
|
|
796
|
+
}
|
|
635
797
|
if t, err := revParse(repoRoot, sl.Branch); err != nil {
|
|
636
798
|
warnf("branch tip %s: %v", sl.Branch, err)
|
|
637
799
|
keep = true // fail toward preservation
|
|
@@ -740,13 +902,20 @@ func StatusReport(repoRoot, slug string) (string, error) {
|
|
|
740
902
|
fmt.Fprintf(&b, "lease: %s\n", leasePath)
|
|
741
903
|
fmt.Fprintf(&b, "status: %s\n", lease.Status)
|
|
742
904
|
fmt.Fprintf(&b, "base_sha: %s\n", lease.BaseSHA)
|
|
905
|
+
if lease.ControlTip != "" {
|
|
906
|
+
fmt.Fprintf(&b, "control_tip: %s\n", lease.ControlTip)
|
|
907
|
+
}
|
|
743
908
|
for _, sl := range lease.Slices {
|
|
744
909
|
tc := sl.TransferCommit
|
|
745
910
|
if tc == "" {
|
|
746
911
|
tc = "-"
|
|
747
912
|
}
|
|
748
|
-
fmt.Fprintf(&b, "slice %s: wright=%s wt=%s branch=%s transfer=%s
|
|
913
|
+
fmt.Fprintf(&b, "slice %s: wright=%s wt=%s branch=%s transfer=%s",
|
|
749
914
|
sl.ID, sl.WrightStatus, sl.WorktreePath, sl.Branch, tc)
|
|
915
|
+
if sl.BaseSHA != "" {
|
|
916
|
+
fmt.Fprintf(&b, " base=%s", sl.BaseSHA)
|
|
917
|
+
}
|
|
918
|
+
b.WriteString("\n")
|
|
750
919
|
}
|
|
751
920
|
if head, err := headSHA(repoRoot); err == nil {
|
|
752
921
|
fmt.Fprintf(&b, "control_HEAD: %s\n", head)
|
|
@@ -13,6 +13,13 @@ var checkPathDisjoint = CheckPathDisjoint
|
|
|
13
13
|
// whose size is ≤ cap. ready must already be dependency-satisfied and in plan
|
|
14
14
|
// order. cap is 1–MaxParallelSlices; 1 is a legal one-slice round, not a latch.
|
|
15
15
|
func SelectGreedy(cap int, ready []SlicePaths, root string) ([]SlicePaths, error) {
|
|
16
|
+
return SelectGreedySeeded(cap, nil, ready, root)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// SelectGreedySeeded is SelectGreedy with seed slices already running: they
|
|
20
|
+
// occupy cap slots and claim their paths, a ready candidate with a seed's id
|
|
21
|
+
// is skipped, and only the new picks are returned.
|
|
22
|
+
func SelectGreedySeeded(cap int, seed, ready []SlicePaths, root string) ([]SlicePaths, error) {
|
|
16
23
|
if cap < 1 || cap > MaxParallelSlices {
|
|
17
24
|
return nil, fmt.Errorf("cap must be 1-%d (got %d)", MaxParallelSlices, cap)
|
|
18
25
|
}
|
|
@@ -39,11 +46,18 @@ func SelectGreedy(cap int, ready []SlicePaths, root string) ([]SlicePaths, error
|
|
|
39
46
|
normalized = append(normalized, SlicePaths{ID: item.ID, Paths: paths})
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
running := make(map[string]bool, len(seed))
|
|
50
|
+
for _, s := range seed {
|
|
51
|
+
running[s.ID] = true
|
|
52
|
+
}
|
|
53
|
+
selected := append([]SlicePaths{}, seed...)
|
|
43
54
|
for _, cand := range normalized {
|
|
44
55
|
if len(selected) >= cap {
|
|
45
56
|
break
|
|
46
57
|
}
|
|
58
|
+
if running[cand.ID] {
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
47
61
|
if len(selected) == 0 {
|
|
48
62
|
selected = append(selected, cand)
|
|
49
63
|
continue
|
|
@@ -57,7 +71,7 @@ func SelectGreedy(cap int, ready []SlicePaths, root string) ([]SlicePaths, error
|
|
|
57
71
|
}
|
|
58
72
|
selected = trial
|
|
59
73
|
}
|
|
60
|
-
return selected, nil
|
|
74
|
+
return selected[len(seed):], nil
|
|
61
75
|
}
|
|
62
76
|
|
|
63
77
|
func selectMode(n int) string {
|
|
@@ -71,10 +85,21 @@ func selectMode(n int) string {
|
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
// formatSelect prints the select line. running < 0 means no lease seeded the
|
|
89
|
+
// selection; otherwise new picks join that many running slices, so the mode
|
|
90
|
+
// counts them too and the line reports running=<k>.
|
|
91
|
+
func formatSelect(picks []SlicePaths, running int) string {
|
|
92
|
+
ids := make([]string, len(picks))
|
|
93
|
+
for i, item := range picks {
|
|
77
94
|
ids[i] = item.ID
|
|
78
95
|
}
|
|
79
|
-
|
|
96
|
+
mode := selectMode(len(picks))
|
|
97
|
+
if running > 0 && len(picks) > 0 {
|
|
98
|
+
mode = selectMode(len(picks) + running)
|
|
99
|
+
}
|
|
100
|
+
line := fmt.Sprintf("select: n_eff=%d mode=%s slices=%s", len(picks), mode, strings.Join(ids, ","))
|
|
101
|
+
if running >= 0 {
|
|
102
|
+
line += fmt.Sprintf(" running=%d", running)
|
|
103
|
+
}
|
|
104
|
+
return line
|
|
80
105
|
}
|
|
@@ -21,7 +21,8 @@ seams supplied by the orchestrator.
|
|
|
21
21
|
planning artifacts while preserving settled intent.
|
|
22
22
|
|
|
23
23
|
Reuse existing seams and dependencies before proposing new ones. Build vertical
|
|
24
|
-
slices in dependency order
|
|
24
|
+
slices in dependency order; put a hub file's shared edit in one seam slice
|
|
25
|
+
([slicing.md](../skills/rite-plan/reference/slicing.md#slice-independence)). Map every REQ and AC to a slice and executable proof,
|
|
25
26
|
name rollback and failure paths; keep acceptance-changing proposals separate. Durable
|
|
26
27
|
proof commands must be portable repository commands, never host-local wrappers,
|
|
27
28
|
user-specific absolute paths, or temporary proof trees.
|
|
@@ -103,7 +103,8 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
|
|
|
103
103
|
that source in the result. Never invent an API.
|
|
104
104
|
- **No process residue.** Do not put `SLICE-###`, slice numbers, or "this slice"
|
|
105
105
|
in product source, tests, comments, or filenames.
|
|
106
|
-
4. **VERIFY (fail-on-red).** Run writer-safe tests/types/lint
|
|
106
|
+
4. **VERIFY (fail-on-red).** Run writer-safe tests/types/lint: `Tests/proof` plus
|
|
107
|
+
tests of changed code; `## Feature gates` rows are root-owned. Report required
|
|
107
108
|
build/browser/E2E as `not-run` (root-owned, artifact-producing gates), and only
|
|
108
109
|
when the exact command, cwd, and prerequisites already appear in the unchanged
|
|
109
110
|
vetted `test-plan.md`; never synthesize or rewrite a root command. Root runs
|
|
@@ -87,13 +87,14 @@ has no clear next move. Optional slug selects the active workspace; defaults to
|
|
|
87
87
|
with that fingerprint. Reclassify only on new causal evidence. On cold resume,
|
|
88
88
|
a retained fingerprint with fewer than three such attempts remains runnable
|
|
89
89
|
even if the previous action wrote a terminal cursor.
|
|
90
|
-
- **A
|
|
90
|
+
- **A strategy is spent after three no-progress attempts per exact causal fingerprint.**
|
|
91
91
|
Count an attempt only when its recheck preserves the same decisive failure.
|
|
92
92
|
Record attempt number, exact failure, hypothesis, probe, and failed idea after
|
|
93
93
|
each; closure is progress and a different Critical/Important invariant is a new
|
|
94
94
|
fingerprint. There is no JSONL ledger,
|
|
95
95
|
counter command, or reset-on-green operation. Product/acceptance ambiguity, irreversible risk, or
|
|
96
|
-
human-only access becomes a human gate; otherwise return
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
human-only access becomes a human gate; otherwise return the spent strategy and
|
|
97
|
+
reproduction to the caller, which escalates or parks per the
|
|
98
|
+
[retry contract](../devrites-lib/reference/standards/afk-hitl.md#retry-cap-no-progress-loops-and-self-resolve);
|
|
99
|
+
never request attempt four of the same strategy. While budget remains, coupled failure requiring behavior change routes
|
|
99
100
|
`/rite-plan repair` inline; behavior-neutral rerouting uses `unblock` inline.
|
|
@@ -127,7 +127,8 @@ child's handle, path/resource ownership and capacity reservation until the
|
|
|
127
127
|
native host confirms a terminal state and returned work has been reconciled.
|
|
128
128
|
Query or wait on that handle; do not replace the child, release its slot, or
|
|
129
129
|
reuse its resources while it may still run. If terminal state cannot be
|
|
130
|
-
established, record a gap and retain ownership
|
|
130
|
+
established, record a gap and retain ownership; for a child launched by an earlier
|
|
131
|
+
session, the claim proof below settles it. A result classification alone
|
|
131
132
|
does not prove host termination.
|
|
132
133
|
|
|
133
134
|
On resume (compaction or a new session), run `devrites-engine dispatch <slug>
|
|
@@ -135,7 +136,11 @@ status`, `parallel status`, and `claim list --all` before any dispatch. A read-o
|
|
|
135
136
|
wave whose handles this session cannot query: `abandon` it, record each unreturned
|
|
136
137
|
role `gap`, and re-dispatch fresh on the current digest; its late returns are
|
|
137
138
|
`stale`. A writer keeps its claim and lease — no relaunch on those paths until the
|
|
138
|
-
claim is released or its holder is proven terminal.
|
|
139
|
+
claim is released or its holder is proven terminal. For an unqueryable writer,
|
|
140
|
+
`claim list --all` showing its claim `released` or `dead` (the engine proved the
|
|
141
|
+
holding session exited on this host) is that proof: `abandon` the wave, record the
|
|
142
|
+
role `gap`, claim the paths, and re-dispatch without waiting on the launching
|
|
143
|
+
session. `live` or `expired` stays held. A wright that never returned an
|
|
139
144
|
admitted result leaves unadmitted bytes: task-path changes since its pre-dispatch
|
|
140
145
|
`git diff --name-only` baseline are never cited as built nor discarded, and are
|
|
141
146
|
named in the re-dispatch contract; changes outside task paths stop for the human
|
|
@@ -10,7 +10,7 @@ When an active rite is the controlling caller, Intermediate `NEEDS_REPLAN` (the
|
|
|
10
10
|
token for the workspace's `NEEDS REPLAN` readiness value), a nested phase `STOP`, and a
|
|
11
11
|
routine Plan/Vet `Next step` are not eligible completion states. Autocomplete may
|
|
12
12
|
use the shapes below only after its requested rest point or a shared genuine
|
|
13
|
-
human/safety/access/
|
|
13
|
+
human/safety/access/parked-recovery stop is reached.
|
|
14
14
|
|
|
15
15
|
```text
|
|
16
16
|
Done: <result in one sentence>
|
|
@@ -104,23 +104,20 @@ Record: .devrites/archive/<slug>/ship.md
|
|
|
104
104
|
Claims such as proved, reviewed, sealed, shipped, or complete must point to real
|
|
105
105
|
output or an artifact. A NOT-RUN verification keeps its marker in `Evidence:` or `Open:`
|
|
106
106
|
per [`core.md`](standards/core.md#gate-contract) § Gate contract.
|
|
107
|
-
Use exactly one recommended next action
|
|
108
|
-
terminal agent-owned technical exhaustion, which has no runnable action.
|
|
107
|
+
Use exactly one recommended next action.
|
|
109
108
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
Agent-owned recovery never ends with `Next: none`. Per the
|
|
110
|
+
[retry contract](standards/afk-hitl.md#retry-cap-no-progress-loops-and-self-resolve),
|
|
111
|
+
a spent strategy escalates inline and a fully escalated fingerprint parks while
|
|
112
|
+
independent work continues; with **no safe in-scope diagnostic-amplification seam**,
|
|
113
|
+
escalate or park instead of stopping. A spent consumptive-action authorization plus a
|
|
114
|
+
retained new fingerprint keeps recovering. Once nothing runnable remains, use:
|
|
115
115
|
|
|
116
116
|
```text
|
|
117
|
-
Stopped: Technical recovery
|
|
117
|
+
Stopped: Technical recovery parked
|
|
118
118
|
Blocking: <causal fingerprint and invariant>
|
|
119
|
-
Attempts: <
|
|
120
|
-
|
|
121
|
-
Next:
|
|
119
|
+
Attempts: <each strategy, its class, and decisive reproduction>
|
|
120
|
+
Completed: <independent work finished after parking>
|
|
121
|
+
Next: <invoking command> — reopens parked fingerprints with fresh escalation
|
|
122
122
|
Record: <artifact path>
|
|
123
123
|
```
|
|
124
|
-
|
|
125
|
-
The terminal cursor marker is `Next: none` exactly as shown; a stored `Next step: none`
|
|
126
|
-
names the same exhausted state.
|