@dpeek/codeless 0.1.1 → 0.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/spec/workflow.md CHANGED
@@ -1,16 +1,19 @@
1
1
  # Codeless workflow
2
2
 
3
- Codeless is an attended workflow for delivering independent capability changes
4
- through a project's integration branch. This contract owns the implemented
5
- workflow; the [project guide](../README.md) explains installation, operation,
6
- and reusable execution mechanics. Missing workflow behavior belongs in
7
- [the workflow todo](../todo/workflow.md).
3
+ Codeless is an attended workflow for delivering independent capability changes through a
4
+ project's integration branch. This contract owns the implemented workflow; the [project
5
+ guide](../README.md) explains installation, operation, and reusable execution mechanics.
6
+ Missing workflow behavior belongs in [the workflow todo](../todo/workflow.md).
8
7
 
9
- ## Stream and change lifecycle
8
+ ## Ownership and lifecycle
10
9
 
11
- A stream has one lowercase kebab-case slug, one `stream/<slug>` branch and
12
- worktree, one planner, one implementer pane, and at most one approved change in
13
- progress. Work inside a stream is sequential:
10
+ Codeless owns reusable execution mechanics and shared local state. Projects own
11
+ directions, prompt contents, model choices, and approval/review policy. Pi and Herdr own
12
+ their runtime APIs and process behavior.
13
+
14
+ A stream has one lowercase kebab-case slug, one `stream/<slug>` branch and worktree, one
15
+ planner, one implementer pane, and at most one approved change in progress. Work inside
16
+ a stream is sequential:
14
17
 
15
18
  1. create or reopen the stream from the configured integration branch;
16
19
  2. propose one small change and wait for operator approval;
@@ -21,22 +24,42 @@ progress. Work inside a stream is sequential:
21
24
  slot; and
22
25
  6. replace the planner session before proposing another change.
23
26
 
24
- Automated landing updates only the configured integration branch's dedicated
25
- clean checkout. Other branches and checkouts are neither stream sources nor
26
- landing targets. Codeless does not push.
27
+ Automated landing updates only the configured integration branch's dedicated clean
28
+ checkout. Other branches and checkouts are neither stream sources nor landing targets.
29
+ Codeless does not push.
30
+
31
+ Each project supplies `.codeless/config.json`, a direction at `<directions>/<slug>.md`, and
32
+ `change`, `implement`, `review`, and `commit` prompt templates. Creation refuses a
33
+ missing direction or prompt and an existing stream. Opening requires the existing
34
+ branch, worktree, journal, proposal file, direction, and prompts. Both install
35
+ dependencies before starting a new planner. Reopening a running managed planner
36
+ only focuses its workspace and preserves its conversation.
37
+
38
+ ## Command and tool boundaries
39
+
40
+ The executable currently exposes both operator commands and subprocess entrypoints used
41
+ by planner tools:
42
+
43
+ | Surface | Responsibility |
44
+ | ------------------------------------------------- | ----------------------------------------------------------------------------- |
45
+ | `init`, `create`, `open`, `metrics` | Operator setup, session management, and observation |
46
+ | `approve`, `dispatch`, `rework`, `finish`, `next` | Backing commands for the corresponding planner tools |
47
+ | `land` | Commit integration, invoked by the project prompt or operator during recovery |
48
+ | `/change`, `/implement`, `/review`, `/commit` | Project-owned prompt templates |
49
+ | `/streams-activate`, `/streams-next` | Package-owned planner activation and session replacement |
50
+ | `/codeless-rework`, `/codeless-finish` | Package-owned commands delivered to the existing implementer |
27
51
 
28
- Each project supplies `.codeless/config.json`, a direction at
29
- `todo/<slug>.md`, and `change`, `implement`, `review`, and `commit` prompt
30
- templates. Creation refuses a missing direction or prompt and an existing
31
- stream. Opening requires the existing branch, worktree, journal, proposal file,
32
- direction, and prompts. Both install dependencies before starting the planner.
52
+ The Pi commands bridge session/process operations. In particular, `next_stream_change`
53
+ queues a command so session replacement occurs after the current turn settles. Surface
54
+ reductions and additional tools belong in the workflow todo; all backing CLI commands
55
+ remain callable today.
33
56
 
34
57
  ## Shared local state
35
58
 
36
- Codeless keeps workflow state outside tracked project documents in a shared
37
- workspace selected by the local Git `codeless.workspaceRoot` setting or, by
38
- default, at `.codeless/state/` in the primary checkout. The default directory is
39
- Git-ignored, and every linked worktree resolves the same primary-checkout state:
59
+ Codeless keeps workflow state outside tracked project documents in a shared workspace
60
+ selected by the local Git `codeless.workspaceRoot` setting or, by default, at
61
+ `.codeless/state/` in the primary checkout. The default directory is Git-ignored, and
62
+ every linked worktree resolves the same primary-checkout state:
40
63
 
41
64
  ```text
42
65
  <primary-checkout>/.codeless/state/
@@ -49,169 +72,212 @@ Git-ignored, and every linked worktree resolves the same primary-checkout state:
49
72
  metrics/<slug>/NNN.json
50
73
  ```
51
74
 
52
- `codeless init` is the explicit, idempotent bootstrap for a configured project.
53
- It requires the configured integration branch to exist and creates the shared
54
- state directories plus that branch's worktree only at
75
+ `codeless init` is the explicit, idempotent bootstrap for a configured project. It
76
+ requires the configured integration branch and creates missing copies of the four
77
+ package-owned generic prompt starters in the invoking checkout's configured in-project
78
+ prompt directory. It validates every template and destination before mutation, reports
79
+ each absolute path as created or preserved, and never replaces an existing prompt file.
80
+ A non-directory ancestor or non-file prompt collision stops without prompt, state, ignore,
81
+ or worktree mutation. Missing prompts are not generated when init is invoked from the
82
+ dedicated integration checkout; the operator must initialize from an editable checkout.
83
+
84
+ Init creates the shared state directories plus that branch's worktree only at
55
85
  `<workspace>/worktree/<integration-branch>`. It reuses only the exact registered
56
- canonical checkout. An occupied target, a branch registered elsewhere, invalid
57
- checkout, or ambiguous Git registration stops unchanged; init never switches
58
- branches, moves worktrees, or repairs conflicts. With the default workspace,
59
- it accepts a repository ignore rule only when it ignores the state path without
60
- covering configuration or configured prompts, otherwise appending the narrow
61
- `/.codeless/state/` rule. An absolute workspace never changes repository
62
- ignores. No other command bootstraps this layout.
63
-
64
- `planner.md` owns decisions, approvals, review outcomes, landing history, and
65
- the context needed by a fresh planner. `change.md` is the editable current
66
- proposal. `changes/NNN.md` is the immutable-by-policy approved input to one
67
- implementation loop. These documents are local workflow state, not product
68
- contracts, and are never copied into `spec/`.
86
+ canonical checkout. An occupied target, a branch registered elsewhere, invalid checkout,
87
+ or ambiguous Git registration stops unchanged; init never switches branches, moves
88
+ worktrees, or repairs conflicts. With the default workspace, it accepts a repository
89
+ ignore rule only when it ignores the state path without covering configuration or
90
+ configured prompts, otherwise appending the narrow `/.codeless/state/` rule. An absolute
91
+ workspace never changes repository ignores. No other command bootstraps this layout.
92
+
93
+ `planner.md` owns decisions, approvals, review outcomes, landing history, and the
94
+ context needed by a fresh planner. `change.md` is the editable current proposal.
95
+ `changes/NNN.md` is the immutable-by-policy approved input to one implementation loop.
96
+ These documents are local workflow state, not product contracts, and are never copied
97
+ into `spec/`.
69
98
 
70
99
  After operator `go`, the argument-free planner-only `approve_stream_change` tool
71
- promotes the current proposal before any dispatch. It derives the active planner
72
- session and passes it to the backing CLI, which requires its `<slug>-planner`
73
- identity to match the clean `stream/<slug>` worktree and branch exactly at the
74
- current integration branch. The proposal needs one usable H1 title plus the `Why`, `Change`,
75
- `Acceptance`, and `Decisions` headings; titles must be representable by the
76
- canonical record. The CLI writes `changes/NNN.md` exclusively, where `NNN` is
77
- the successor of the greatest existing three-digit number (and stops after
78
- `999`), then appends a canonical journal approval containing the file, title,
79
- and proposal hash. Repeated calls reconcile that exact file and entry, completing
80
- one missing step without another number; conflicting or ambiguous partial state
81
- stops unchanged. Rejection and ordinary feedback allocate nothing. Dispatch
82
- remains a separate explicit tool call using the returned absolute path.
100
+ promotes the current proposal before any dispatch. It derives the active planner session
101
+ and passes it to the backing CLI, which requires its `<slug>-planner` identity to match
102
+ the clean `stream/<slug>` worktree and branch. The stream commit established for
103
+ planning is the proposal's base; approval does not compare it with the moving
104
+ integration branch. Landing alone acquires the integration lock and rebases that
105
+ stream change when integration has advanced. The proposal needs one usable H1 title plus
106
+ the `Why`, `Change`, `Acceptance`, and
107
+ `Decisions` headings in that order; titles must be representable by the canonical
108
+ record. The CLI writes `changes/NNN.md` exclusively, where `NNN` is the successor of the
109
+ greatest existing three-digit number (and stops after `999`), then appends a canonical
110
+ journal approval containing the file, title, and proposal hash. Repeated calls reconcile
111
+ that exact file and entry, completing one missing step without another number;
112
+ conflicting or ambiguous partial state stops unchanged. Rejection and ordinary feedback
113
+ allocate nothing. Dispatch remains a separate explicit tool call using the returned
114
+ absolute path.
83
115
 
84
116
  ## Role sessions and configuration
85
117
 
86
118
  Project configuration selects an exact Pi provider, model, and thinking level
87
119
  independently for planner and implementer roles.
88
120
 
89
- Before starting either role, Codeless uses Pi's machine-readable APIs to require
90
- the configured model, authentication, supported thinking level, and effective
91
- selection. It fails before agent work rather than accepting a fallback model or
92
- clamped thinking level. The validated selection is displayed and passed to the
93
- role process.
94
-
95
- Configuration changes take effect only at a new role-session boundary. An
96
- active review or remediation keeps its implementer setting. A successful
97
- post-landing handoff rereads and validates planner configuration from the
98
- fast-forwarded stream worktree before the replacement session receives its
99
- first project prompt.
100
-
101
- Every planner launch—creation, reopening, direct `planner` restart, and
102
- post-landing replacement—uses the package-owned extension as its activation
103
- boundary. Before its first project prompt, activation requires the exact
104
- `<slug>-planner` Pi session name, establishes and verifies Herdr reports
105
- `<slug-with-hyphens-replaced>_planner`, and verifies
106
- `approve_stream_change`, `dispatch_stream_implementer`, and
107
- `next_stream_change` are active. Missing or incompatible activation, identity
108
- mismatch, or an incomplete tool set stops visibly before `/change`. A direct
109
- restart may begin with Herdr's `pi` fallback identity; activation renames and
110
- rereads only that fallback. Any other identity mismatch stops. Implementers use
111
- the corresponding `_impl` and `-impl` forms. The package loads its planner
112
- extension explicitly; global Pi extension installation is not required.
121
+ Before starting either role, Codeless uses Pi's machine-readable APIs to require the
122
+ configured model, authentication, supported thinking level, and effective selection. It
123
+ fails before agent work rather than accepting a fallback model or clamped thinking
124
+ level. The validated selection is displayed and passed to the role process.
125
+
126
+ Configuration changes take effect only at a new role-session boundary. An active review
127
+ or remediation keeps its implementer setting. A successful post-landing handoff rereads
128
+ and validates planner configuration from the fast-forwarded stream worktree before the
129
+ replacement session receives its first project prompt.
130
+
131
+ Every new planner process uses `herdr agent start`, which owns its managed name
132
+ and waits for interactive readiness before Codeless sends activation. `open`
133
+ reuses the stream workspace and its root planner pane, adding a right-hand shell
134
+ only when absent. It accepts only a lone planner pane or a planner with one
135
+ right-hand pane. Starting a planner requires both existing panes to be shells in
136
+ the stream worktree; occupied, mismatched, or ambiguous layouts stop unchanged.
137
+ The operator must invoke opening from outside those target panes. Reopening an
138
+ existing managed planner focuses it without installation or another prompt.
139
+
140
+ The package-owned extension activates creation, reopening, and post-landing
141
+ replacement. Before the first project prompt it requires the exact
142
+ `<slug>-planner` Pi name, `<slug-with-hyphens-replaced>_planner` Herdr name,
143
+ managed interactive readiness, matching foreground worktree, and the current
144
+ native Pi session ID/file reported by Herdr's official Pi lifecycle integration.
145
+ It verifies `approve_stream_change`, `dispatch_stream_implementer`,
146
+ `rework_stream_implementer`, `finish_stream_implementer`, and `next_stream_change`
147
+ are active. During replacement, an otherwise-valid previous native session
148
+ reference receives a brief bounded synchronization wait; a wrong name, process,
149
+ lifecycle source, or worktree fails immediately. Any binding that remains missing
150
+ or incompatible stops visibly before `/change`. Activation never repairs names.
151
+ The direct `planner` command is removed;
152
+ recovery exits Pi deliberately and reopens from another Herdr shell.
153
+
154
+ Pi session replacement keeps the managed process and Herdr name while changing
155
+ its native conversation reference. Codeless revalidates that new binding before
156
+ prompting the replacement. Implementers use the corresponding `_impl` and
157
+ `-impl` names. Codeless loads its own extension explicitly; Herdr's official Pi
158
+ integration supplies lifecycle and native-session reporting. This boundary was
159
+ verified against Herdr 0.8.2 and Pi 0.85.1.
113
160
 
114
161
  ## Dispatch and review
115
162
 
116
163
  The planner-only `dispatch_stream_implementer` tool accepts an absolute approved
117
- `changes/NNN.md` path. Dispatch verifies the stream branch, clean worktree,
118
- planner pane, prompts, and implementer selection. It creates or reuses the
119
- right-hand Herdr pane only when that pane is an available shell or the expected
120
- idle implementer, starts a fresh ephemeral Pi implementer in the stream
121
- worktree, submits `/implement`, and waits for at most one hour.
164
+ `changes/NNN.md` path. Dispatch verifies the stream branch, clean worktree, planner
165
+ pane, prompts, and implementer selection. It creates or reuses the right-hand Herdr pane
166
+ only when that pane is an available shell or the expected idle implementer, starts a
167
+ fresh ephemeral Pi implementer in the stream worktree, submits `/implement`, and waits
168
+ for at most one hour.
122
169
 
123
170
  Successful dispatch loads the package-owned reporting extension while retaining
124
- `--no-session` and passes its report configuration through that extension's
125
- explicit Pi string flag, then returns one normalized attempt to the planner tool
126
- before it queues the expanded `/review` prompt. Attempts have a stable ID and
127
- capture only stream/change/role, start and settlement timestamps, Pi's actual
128
- settled provider/model/thinking selection, terminal outcome and final text,
129
- full-session Pi input/output/cache usage (including tool results, compaction,
130
- and branch summaries), available Pi model cost estimate with USD currency and
131
- source, and tool/error counts. Cost is omitted when Pi did not supply valid
132
- cost totals. They do not retain prompts, source, credentials, thinking, or a
133
- transcript. The extension writes its narrow report atomically once, then
134
- remains disarmed for remediation; Codeless atomically deduplicates it inside the
135
- per-change metric record, rejecting a
136
- conflicting duplicate ID. Missing, malformed, or unwritable collection warns
137
- and yields an explicitly incomplete attempt when possible without failing or
138
- repeating a settled implementation.
139
-
140
- The planner inspects the full diff and relevant code, checks the approved
141
- acceptance criteria, and runs focused checks when the implementation output is
142
- insufficient. Remediation reuses the same implementer context. Once approved,
143
- the planner records the review result, exits the implementer so its pane returns
144
- to a shell, and follows the commit-and-land prompt without another approval
145
- round.
146
-
147
- Dispatch and remediation do not retry automatically. Remediation and implementer
148
- shutdown are still performed through prompt-owned Herdr commands.
171
+ `--no-session` and passes its report configuration through that extension's explicit Pi
172
+ string flag, then returns one normalized attempt to the planner tool before it queues
173
+ the expanded `/review` prompt. Attempts have a stable ID and capture only
174
+ stream/change/role, start and settlement timestamps, Pi's actual settled
175
+ provider/model/thinking selection, terminal outcome and final text, Pi
176
+ input/output/cache usage over the collected attempt (including tool results, compaction,
177
+ and branch summaries), available Pi model cost estimate with USD currency and source,
178
+ and tool/error counts. Cost is omitted when Pi did not supply valid cost totals.
179
+ Collection stores final assistant text and aggregate measurements, not a transcript or
180
+ separate prompt, source, credential, or thinking fields. Final assistant text is not
181
+ redacted. The extension writes its report atomically once, then remains disarmed for
182
+ remediation; Codeless atomically deduplicates it inside the per-change metric record,
183
+ rejecting a conflicting duplicate ID. Missing, malformed, or unwritable collection warns
184
+ and yields an explicitly incomplete attempt when possible without failing or repeating a
185
+ settled implementation.
186
+
187
+ The planner inspects the full diff and relevant code, checks the approved acceptance
188
+ criteria, and runs focused checks when the implementation output is insufficient. The
189
+ planner-only `rework_stream_implementer` tool accepts that approved path and concise
190
+ feedback, verifies the expected idle implementer, its right-hand pane and worktree, then
191
+ invokes one package-owned Pi command. That command verifies the immutable startup
192
+ stream/change scope, arms package reporting in the existing conversation, and submits
193
+ one bounded feedback turn. It records usage from that remediation turn, excluding
194
+ earlier conversation entries, and returns one `rework` attempt before queueing review
195
+ again; missing or malformed reports warn and yield an incomplete attempt after
196
+ settlement. Prompt rejection, timeout, blocked state, identity/worktree/change mismatch,
197
+ or ambiguous pane stops without a completed attempt or queued review. The separate
198
+ planner-only `finish_stream_implementer` tool first verifies that immutable
199
+ stream/change scope in the same idle implementer, then gracefully exits it and waits for
200
+ its pane to become the stream-worktree shell. Its failure stops before commit or landing
201
+ instructions continue.
202
+
203
+ Dispatch, remediation, and shutdown do not retry automatically or replace an implementer
204
+ session or its selected model.
149
205
 
150
206
  ## Commit and landing
151
207
 
152
- A reviewed change produces exactly one commit outside the merge base with the
153
- configured integration branch. `codeless land <slug>` requires clean stream and integration worktrees,
154
- then atomically acquires the shared `.land-lock` with its owner and captured
208
+ A reviewed change produces exactly one commit outside the merge base with the configured
209
+ integration branch. `codeless land <slug>` requires clean stream and integration
210
+ worktrees, then atomically acquires the shared `.land-lock` with its owner and captured
155
211
  integration commit.
156
212
 
157
- If the integration branch advanced, landing rebases the single stream commit. It then rereads and
158
- runs the configured project check in the stream worktree, requires checks to
159
- leave the worktree clean, and fast-forwards the dedicated `main` checkout. Only
213
+ If the integration branch advanced, landing rebases the single stream commit. It then
214
+ rereads and runs the configured project check in the stream worktree, requires checks to
215
+ leave the worktree clean, and fast-forwards the dedicated integration checkout. Only
160
216
  successful completion releases the lock.
161
217
 
162
- A lock owned by another stream stops landing without polling. A rebase conflict,
163
- failed check, or other error after acquisition retains this stream's lock for
164
- deliberate recovery. Rerunning landing for the same owner is allowed only while
165
- the recorded integration commit still matches. Codeless never removes a stale or
166
- ambiguous lock automatically.
218
+ A lock owned by another stream stops landing without polling. A rebase conflict, failed
219
+ check, or other error after acquisition retains this stream's lock for deliberate
220
+ recovery. Rerunning landing for the same owner is allowed only while the recorded
221
+ integration commit still matches. Codeless never removes a stale or ambiguous lock
222
+ automatically.
167
223
 
168
224
  ## Fresh planner handoff
169
225
 
170
226
  After landing, the planner records the full landed commit hash and calls the
171
- planner-only `next_stream_change` tool exactly once. The handoff requires the
172
- latest numbered change, its full hash in the journal and stream history, no
173
- unlanded stream commit, a clean worktree, and no unresolved lock owned by this
174
- stream or by an unknown owner.
175
-
176
- Codeless captures the current integration branch, fast-forwards the stream worktree, validates
177
- the updated direction, prompts, and planner selection, and returns the next
178
- session name and `/change` prompt. The extension replaces the Pi session in the
179
- same pane, preserves its name, activates the validated selection and planner
180
- identity, and only then sends the project prompt. Conversation history is not copied; the journal and
227
+ planner-only `next_stream_change` tool exactly once. The handoff requires the latest
228
+ numbered change, the full landed commit hash in the journal and stream history, no unlanded stream
229
+ commit, a clean worktree, and no unresolved lock owned by this stream or by an unknown
230
+ owner.
231
+
232
+ Codeless captures the current integration branch, fast-forwards the stream worktree,
233
+ validates the updated direction, prompts, and planner selection, and returns the next
234
+ session name and `/change` prompt. The extension replaces the Pi session in the same
235
+ pane, preserves its name, activates the validated selection and planner identity, and
236
+ only then sends the project prompt. Conversation history is not copied; the journal and
181
237
  project files carry durable context.
182
238
 
183
239
  A cancelled or failed replacement stops for operator attention. Landing remains
184
- complete, and any successful preparation fast-forward remains applied. There is
185
- no background retry. The replacement planner still needs a new operator `go`
186
- before another implementation.
240
+ complete, and any successful preparation fast-forward remains applied. There is no
241
+ background retry. The replacement planner still needs a new operator `go` before another
242
+ implementation.
187
243
 
188
244
  ## Local workflow metrics
189
245
 
190
- The first dispatch for a stream and numbered change creates one atomic local
191
- metric record. Every accepted dispatch creates a new attempt ID; re-ingesting an
192
- attempt ID is atomic and idempotent, while the original dispatch timestamp stays
193
- unchanged. Successful landing adds its timestamp and commit, or creates a landed
194
- record with unavailable elapsed time when dispatch collection was unavailable.
195
- Collection warnings do not change dispatch or landing outcomes.
196
-
197
- `codeless metrics` reports every recorded stream and a project total with:
198
-
199
- - landed and dispatched-but-unlanded change counts;
200
- - measured versus unavailable elapsed coverage; and
201
- - total and average dispatch-to-land wall-clock time.
202
-
203
- The measurements are prospective, local observations. They are not journal
204
- state, an approval source, or a recovery mechanism. Attempt usage and cost are
205
- stored for later aggregation; `codeless metrics`, review rework, and failure
206
- breakdowns do not yet report them.
246
+ The first dispatch for a stream and numbered change creates one atomic local metric
247
+ record. Every accepted dispatch creates a new attempt ID; re-ingesting an attempt ID is
248
+ atomic and idempotent, while the original dispatch timestamp stays unchanged. Successful
249
+ landing adds its timestamp and commit, or creates a landed record with unavailable
250
+ elapsed time when dispatch collection was unavailable. Collection warnings do not change
251
+ dispatch or landing outcomes.
252
+
253
+ `codeless metrics` reports every recorded stream and a project total in two tables. The
254
+ elapsed table reports landed and dispatched-but-unlanded change counts, measured versus
255
+ unavailable elapsed coverage, and total and average dispatch-to-land wall-clock time.
256
+ The attempt table aggregates only validated canonical attempt records and reports
257
+ distinct changes with rework, initial and rework turns, incomplete collection, exact
258
+ stored terminal-outcome labels, and summed tool errors.
259
+
260
+ Usage coverage is measured versus unavailable attempts; input, output, cache-read, and
261
+ cache-write totals include only attempts with recorded usage. Cost coverage follows the
262
+ same rule, and totals are grouped by recorded currency without conversion. Missing usage
263
+ or cost is unavailable, never zero. These measurements do not establish implementation
264
+ quality or review success. They are prospective local observations, not journal state,
265
+ an approval source, or a recovery mechanism.
207
266
 
208
267
  ## Limits
209
268
 
210
- Codeless is attended and intentionally has no supervisor, project registry,
211
- queue, automatic landing retry, stale-lock recovery, or unattended approval.
212
- Planner startup reads every numbered change, and conflict recovery currently
213
- causes the configured landing check to run twice. Because the default state is
214
- ignored, `git clean -fdx` can delete it.
269
+ Codeless is attended and intentionally has no supervisor, project registry, queue,
270
+ automatic landing retry, stale-lock recovery, or unattended approval. Planner startup
271
+ reads every numbered change, and conflict recovery currently causes the configured
272
+ landing check to run twice. Because the default state is ignored, `git clean -fdx` can
273
+ delete it.
274
+
275
+ The single-active-change rule and the requirement to dispatch only approved input still
276
+ partly depend on planner instructions. Approval reconciles records and hashes but does
277
+ not require the previous approved change to be complete before allocating a different
278
+ proposal. Dispatch requires an existing numbered file, but does not validate its
279
+ approval record/hash or require it to be the latest change. These are implementation
280
+ gaps, not additional permissions.
215
281
 
216
282
  ## References
217
283
 
package/src/attempt.ts CHANGED
@@ -3,6 +3,7 @@ export type Attempt = {
3
3
  stream: string;
4
4
  change: string;
5
5
  role: "implementer";
6
+ kind: "initial" | "rework";
6
7
  startedAt: string;
7
8
  endedAt: string;
8
9
  selection?: { provider: string; model: string; thinking: string };
@@ -23,6 +24,7 @@ export function validAttempt(value: unknown, stream: string, change: string): va
23
24
  "stream",
24
25
  "change",
25
26
  "role",
27
+ "kind",
26
28
  "startedAt",
27
29
  "endedAt",
28
30
  "selection",
@@ -34,7 +36,7 @@ export function validAttempt(value: unknown, stream: string, change: string): va
34
36
  "errorCount",
35
37
  "incomplete",
36
38
  ]);
37
- const strings = ["id", "stream", "change", "role", "startedAt", "endedAt", "outcome"];
39
+ const strings = ["id", "stream", "change", "role", "kind", "startedAt", "endedAt", "outcome"];
38
40
  if (
39
41
  !Object.keys(attempt).every((key) => allowed.has(key)) ||
40
42
  !strings.every((key) => typeof attempt[key] === "string" && attempt[key].length > 0) ||
@@ -43,6 +45,7 @@ export function validAttempt(value: unknown, stream: string, change: string): va
43
45
  attempt["stream"] !== stream ||
44
46
  attempt["change"] !== change ||
45
47
  attempt["role"] !== "implementer" ||
48
+ !["initial", "rework"].includes(attempt["kind"] as string) ||
46
49
  !Number.isSafeInteger(attempt["toolCalls"]) ||
47
50
  (attempt["toolCalls"] as number) < 0 ||
48
51
  !Number.isSafeInteger(attempt["errorCount"]) ||