devrites 6.1.1 → 6.1.3
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/cli.md +4 -1
- package/docs/command-map.md +1 -1
- package/docs/engine/commands.md +4 -4
- package/docs/orchestration.md +4 -3
- package/docs/usage.md +1 -1
- package/engine/commands.go +1 -1
- package/engine/help.go +5 -1
- package/engine/internal/acceptance/cli.go +14 -5
- package/engine/internal/acceptance/run.go +20 -5
- package/engine/internal/lib/apply_bundle.go +474 -0
- 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/diffscope.go +2 -1
- 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/engine/main.go +4 -0
- package/pack/.claude/agents/devrites-plan-drafter.md +13 -3
- package/pack/.claude/agents/devrites-slice-wright.md +5 -1
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +14 -5
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +24 -9
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/.claude/skills/devrites-lib/reference/standards/core.md +3 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/gates.md +3 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +7 -0
- 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 +2 -2
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +6 -2
- package/pack/.claude/skills/rite-build/SKILL.md +7 -7
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +2 -2
- package/pack/.claude/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +28 -21
- 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 +9 -2
- package/pack/.claude/skills/rite-define/SKILL.md +11 -2
- package/pack/.claude/skills/rite-handoff/SKILL.md +1 -1
- package/pack/.claude/skills/rite-plan/SKILL.md +20 -9
- package/pack/.claude/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/.claude/skills/rite-polish/SKILL.md +6 -4
- package/pack/.claude/skills/rite-prove/SKILL.md +3 -2
- package/pack/.claude/skills/rite-review/SKILL.md +6 -1
- package/pack/.claude/skills/rite-seal/SKILL.md +2 -2
- package/pack/.claude/skills/rite-seal/reference/phase-contract.md +7 -1
- package/pack/.claude/skills/rite-vet/SKILL.md +9 -5
- 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 +13 -3
- package/pack/generated/claude/agents/devrites-slice-wright.md +5 -1
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +14 -5
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +24 -9
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +3 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/gates.md +3 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +7 -0
- 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 +2 -2
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +6 -2
- package/pack/generated/claude/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +2 -2
- package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +28 -21
- 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 +9 -2
- package/pack/generated/claude/skills/rite-define/SKILL.md +11 -2
- package/pack/generated/claude/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/claude/skills/rite-plan/SKILL.md +20 -9
- package/pack/generated/claude/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/claude/skills/rite-polish/SKILL.md +6 -4
- package/pack/generated/claude/skills/rite-prove/SKILL.md +3 -2
- package/pack/generated/claude/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/claude/skills/rite-seal/SKILL.md +2 -2
- package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +7 -1
- package/pack/generated/claude/skills/rite-vet/SKILL.md +9 -5
- 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 +13 -3
- package/pack/generated/codex/agents/devrites-slice-wright.toml +5 -1
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +14 -5
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +24 -9
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +3 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/gates.md +3 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +7 -0
- 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 +2 -2
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +6 -2
- package/pack/generated/codex/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +2 -2
- package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +28 -21
- 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 +9 -2
- package/pack/generated/codex/skills/rite-define/SKILL.md +11 -2
- package/pack/generated/codex/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/codex/skills/rite-plan/SKILL.md +20 -9
- package/pack/generated/codex/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/codex/skills/rite-polish/SKILL.md +6 -4
- package/pack/generated/codex/skills/rite-prove/SKILL.md +3 -2
- package/pack/generated/codex/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/codex/skills/rite-seal/SKILL.md +2 -2
- package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +7 -1
- package/pack/generated/codex/skills/rite-vet/SKILL.md +9 -5
- 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 +13 -3
- package/pack/generated/devin/agents/devrites-slice-wright.md +5 -1
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +14 -5
- package/pack/generated/devin/skills/devrites-lib/reference/standards/afk-hitl.md +24 -9
- package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/devin/skills/devrites-lib/reference/standards/core.md +3 -0
- package/pack/generated/devin/skills/devrites-lib/reference/standards/gates.md +3 -0
- package/pack/generated/devin/skills/devrites-lib/reference/standards/testing.md +7 -0
- 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 +2 -2
- package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/devin/skills/rite-autocomplete/reference/stop-conditions.md +6 -2
- package/pack/generated/devin/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/devin/skills/rite-build/reference/afk-discipline.md +2 -2
- package/pack/generated/devin/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +28 -21
- 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 +9 -2
- package/pack/generated/devin/skills/rite-define/SKILL.md +11 -2
- package/pack/generated/devin/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/devin/skills/rite-plan/SKILL.md +20 -9
- package/pack/generated/devin/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/devin/skills/rite-polish/SKILL.md +6 -4
- package/pack/generated/devin/skills/rite-prove/SKILL.md +3 -2
- package/pack/generated/devin/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/devin/skills/rite-seal/SKILL.md +2 -2
- package/pack/generated/devin/skills/rite-seal/reference/phase-contract.md +7 -1
- package/pack/generated/devin/skills/rite-vet/SKILL.md +9 -5
- 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 +13 -3
- package/pack/generated/omp/agents/devrites-slice-wright.md +5 -1
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +14 -5
- package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +24 -9
- package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/omp/skills/devrites-lib/reference/standards/core.md +3 -0
- package/pack/generated/omp/skills/devrites-lib/reference/standards/gates.md +3 -0
- package/pack/generated/omp/skills/devrites-lib/reference/standards/testing.md +7 -0
- 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 +2 -2
- package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +6 -2
- package/pack/generated/omp/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +2 -2
- package/pack/generated/omp/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +28 -21
- 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 +9 -2
- package/pack/generated/omp/skills/rite-define/SKILL.md +11 -2
- package/pack/generated/omp/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/omp/skills/rite-plan/SKILL.md +20 -9
- package/pack/generated/omp/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/omp/skills/rite-polish/SKILL.md +6 -4
- package/pack/generated/omp/skills/rite-prove/SKILL.md +3 -2
- package/pack/generated/omp/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/omp/skills/rite-seal/SKILL.md +2 -2
- package/pack/generated/omp/skills/rite-seal/reference/phase-contract.md +7 -1
- package/pack/generated/omp/skills/rite-vet/SKILL.md +9 -5
- 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 +13 -3
- package/pack/generated/pi/agents/devrites-slice-wright.md +5 -1
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +14 -5
- package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +24 -9
- package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +6 -2
- package/pack/generated/pi/skills/devrites-lib/reference/standards/core.md +3 -0
- package/pack/generated/pi/skills/devrites-lib/reference/standards/gates.md +3 -0
- package/pack/generated/pi/skills/devrites-lib/reference/standards/testing.md +7 -0
- 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 +2 -2
- package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +6 -2
- package/pack/generated/pi/skills/rite-build/SKILL.md +7 -7
- package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +2 -2
- package/pack/generated/pi/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +28 -21
- 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 +9 -2
- package/pack/generated/pi/skills/rite-define/SKILL.md +11 -2
- package/pack/generated/pi/skills/rite-handoff/SKILL.md +1 -1
- package/pack/generated/pi/skills/rite-plan/SKILL.md +20 -9
- package/pack/generated/pi/skills/rite-plan/reference/slicing.md +5 -0
- package/pack/generated/pi/skills/rite-polish/SKILL.md +6 -4
- package/pack/generated/pi/skills/rite-prove/SKILL.md +3 -2
- package/pack/generated/pi/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/pi/skills/rite-seal/SKILL.md +2 -2
- package/pack/generated/pi/skills/rite-seal/reference/phase-contract.md +7 -1
- package/pack/generated/pi/skills/rite-vet/SKILL.md +9 -5
- 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
|
}
|
package/engine/main.go
CHANGED
|
@@ -65,6 +65,8 @@ Usage:
|
|
|
65
65
|
devrites-engine gates <subcommand> <slug> Machine-checked gates.md ledger: scaffold/status/run/reverify/lint/attest/abandon
|
|
66
66
|
devrites-engine state resolve <qid> "<ans>" Resolve an open question and update state atomically
|
|
67
67
|
devrites-engine state merge-manifest <slug> [pred...] Fold the recorded predecessor chain's manifests into the release candidate manifest
|
|
68
|
+
devrites-engine state apply-bundle <slug> <bundle.json>... [--dry-run]
|
|
69
|
+
Validate and apply plan-drafter hunk bundles atomically
|
|
68
70
|
devrites-engine state close <slug> Archive a shipped feature and clear ACTIVE
|
|
69
71
|
devrites-engine migrate <slug> [--dry-run] [--answer id=choice] Normalize an older-schema workspace to the current schema
|
|
70
72
|
devrites-engine secret-scan [--staged] [--stdin] [slug] Scan exact staged blobs, stdin, or touched files; HIGH blocks
|
|
@@ -237,6 +239,8 @@ func cmdState(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
237
239
|
return lib.Resolve(root, args[1:], stdout, stderr)
|
|
238
240
|
case "merge-manifest":
|
|
239
241
|
return lib.RunMergeManifest(root, args[1:], stdout, stderr)
|
|
242
|
+
case "apply-bundle":
|
|
243
|
+
return lib.RunApplyBundle(root, args[1:], stdout, stderr)
|
|
240
244
|
case "close":
|
|
241
245
|
return lib.CloseOut(root, args[1:], stdout, stderr)
|
|
242
246
|
default:
|
|
@@ -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.
|
|
@@ -75,10 +76,13 @@ Return this result:
|
|
|
75
76
|
|
|
76
77
|
```yaml
|
|
77
78
|
mode: define | repair
|
|
79
|
+
base_hashes: {} # <path>: sha256 of the bytes you read, for every replace/patch target
|
|
78
80
|
candidate_files:
|
|
79
81
|
- path: <requested canonical target>
|
|
80
82
|
operation: create | replace | patch
|
|
81
|
-
content: <complete
|
|
83
|
+
content: <complete file; create/replace only>
|
|
84
|
+
hunks: [] # patch only: {id, old_text, new_text}; old_text byte-exact, unique in the pinned file
|
|
85
|
+
retired_ids: [] # tracked IDs (AC/REQ/HZN/SLICE/DEC/...) the delta removes on purpose
|
|
82
86
|
acceptance_map: []
|
|
83
87
|
dependency_order: []
|
|
84
88
|
technical_decisions: []
|
|
@@ -88,7 +92,13 @@ applicability_disposition: []
|
|
|
88
92
|
self_attack_exposures: []
|
|
89
93
|
```
|
|
90
94
|
|
|
91
|
-
The root
|
|
95
|
+
The root saves the bundle as JSON and applies it with `devrites-engine state
|
|
96
|
+
apply-bundle <slug> <bundle.json>`, which refuses a stale pin, a missing,
|
|
97
|
+
duplicate, or overlapping `old_text`, an unlisted vanished ID, a new task-graph
|
|
98
|
+
problem, new budget overshoot, or a new dated delta section, and writes nothing on refusal. Pin each
|
|
99
|
+
`base_hashes` entry with `shasum -a 256` (or `sha256sum`) on the exact file you
|
|
100
|
+
read, and prefer short anchored hunks to whole-file `replace`. Patch the owning
|
|
101
|
+
clause in place; never add a dated delta section beside superseded text.
|
|
92
102
|
|
|
93
103
|
## Tools / read-write mode
|
|
94
104
|
|
|
@@ -103,7 +103,11 @@ 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. Writer-safe means
|
|
108
|
+
local and non-consumptive — syntax/type/lint, unit and self-check suites, and a
|
|
109
|
+
narrow smoke of the changed path — never only a syntax check; contract text
|
|
110
|
+
cannot strip it. Report required
|
|
107
111
|
build/browser/E2E as `not-run` (root-owned, artifact-producing gates), and only
|
|
108
112
|
when the exact command, cwd, and prerequisites already appear in the unchanged
|
|
109
113
|
vetted `test-plan.md`; never synthesize or rewrite a root command. Root runs
|
|
@@ -4,7 +4,9 @@ Host dispatch; DevRites governs.
|
|
|
4
4
|
|
|
5
5
|
## Roster
|
|
6
6
|
|
|
7
|
-
Review: Spec/Code
|
|
7
|
+
Review: Spec/Code, triggered frontend/security/performance, and the seal roles that can
|
|
8
|
+
judge the frozen digest (test-analyst, doubt, devex); Seal: all applicable roles, citing
|
|
9
|
+
same-candidate Review accounts.
|
|
8
10
|
|
|
9
11
|
| Reviewer | Fires | Trigger |
|
|
10
12
|
|---|---|---|
|
|
@@ -74,8 +76,10 @@ If the candidate diff has no matching surfaces, record
|
|
|
74
76
|
and dedupe by evidence/root cause. Account for all seven (and, in `seal.md`, `devrites-proof-runner` and `devrites-doubt-reviewer`) in
|
|
75
77
|
`review.md`/`seal.md`, each [engineering lane](#engineering-lanes) separately:
|
|
76
78
|
`Outcome:` plus admitted account, or
|
|
77
|
-
`Not-applicable: <inspected-scope reason>`.
|
|
78
|
-
`review.md`
|
|
79
|
+
`Not-applicable: <inspected-scope reason>`. Seal cites, rather than re-dispatches, any
|
|
80
|
+
role `review.md` admitted on the identical candidate under
|
|
81
|
+
[evidence validity](candidate-integrity.md#evidence-validity); a changed digest or newly
|
|
82
|
+
triggered surface needs a fresh account. Persist no dispatch telemetry.
|
|
79
83
|
7. Missing profile, null/failure, or malformed output is a `gap`; never
|
|
80
84
|
infer approval or substitute inline review.
|
|
81
85
|
|
|
@@ -127,7 +131,8 @@ child's handle, path/resource ownership and capacity reservation until the
|
|
|
127
131
|
native host confirms a terminal state and returned work has been reconciled.
|
|
128
132
|
Query or wait on that handle; do not replace the child, release its slot, or
|
|
129
133
|
reuse its resources while it may still run. If terminal state cannot be
|
|
130
|
-
established, record a gap and retain ownership
|
|
134
|
+
established, record a gap and retain ownership; for a child launched by an earlier
|
|
135
|
+
session, the claim proof below settles it. A result classification alone
|
|
131
136
|
does not prove host termination.
|
|
132
137
|
|
|
133
138
|
On resume (compaction or a new session), run `devrites-engine dispatch <slug>
|
|
@@ -135,7 +140,11 @@ status`, `parallel status`, and `claim list --all` before any dispatch. A read-o
|
|
|
135
140
|
wave whose handles this session cannot query: `abandon` it, record each unreturned
|
|
136
141
|
role `gap`, and re-dispatch fresh on the current digest; its late returns are
|
|
137
142
|
`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.
|
|
143
|
+
claim is released or its holder is proven terminal. For an unqueryable writer,
|
|
144
|
+
`claim list --all` showing its claim `released` or `dead` (the engine proved the
|
|
145
|
+
holding session exited on this host) is that proof: `abandon` the wave, record the
|
|
146
|
+
role `gap`, claim the paths, and re-dispatch without waiting on the launching
|
|
147
|
+
session. `live` or `expired` stays held. A wright that never returned an
|
|
139
148
|
admitted result leaves unadmitted bytes: task-path changes since its pre-dispatch
|
|
140
149
|
`git diff --name-only` baseline are never cited as built nor discarded, and are
|
|
141
150
|
named in the re-dispatch contract; changes outside task paths stop for the human
|