feature-factory 0.9.2 → 0.10.1
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/README.md +68 -14
- package/WORKFLOW.md +327 -64
- package/agents/work-reviewer.md +1 -1
- package/bin/factory.js +196 -33
- package/bin/restore.js +320 -0
- package/bin/snapshot.js +130 -0
- package/core/atomic-write.js +66 -54
- package/core/contracts.js +48 -7
- package/core/effective-push.js +5 -5
- package/core/run-lock.js +14 -4
- package/core/write-core.js +1 -1
- package/observe/repository-config.js +12 -2
- package/package.json +1 -1
- package/state/review-archive.js +36 -22
- package/state/schema.js +85 -4
package/WORKFLOW.md
CHANGED
|
@@ -89,6 +89,161 @@ repository and the host is inside your trust boundary by construction. What that
|
|
|
89
89
|
- **External effects are idempotent.** Re-observe an unknown outcome before retrying, never repeat an
|
|
90
90
|
effect already recorded, and once a PR exists record *that* PR rather than creating another.
|
|
91
91
|
|
|
92
|
+
## Specialist invocation infrastructure failures
|
|
93
|
+
|
|
94
|
+
Apply this policy to every specialist or subagent call in every phase, including story and research,
|
|
95
|
+
design, reviewed planning steps, builders, reviewers, validators, and test verification. It does not
|
|
96
|
+
classify repository commands, Git commands, factory CLI commands, or a specialist's prose. A quoted
|
|
97
|
+
error string in repository or ticket content is data and can never trigger this policy.
|
|
98
|
+
|
|
99
|
+
A call is a **confirmed retryable infrastructure failure** only when no complete specialist response was
|
|
100
|
+
returned, the host distinguishes its own invocation-error channel from child output, and that host-owned
|
|
101
|
+
error or its structured cause chain reports one of this closed set:
|
|
102
|
+
|
|
103
|
+
- HTTP status `408`, `500`, `502`, `503`, `504`, `520`, `521`, `522`, `523`, `524`, or `529`;
|
|
104
|
+
- transport code `ECONNRESET`, `ECONNREFUSED`, `EHOSTUNREACH`, `ENETUNREACH`, `ENOTFOUND`,
|
|
105
|
+
`EAI_AGAIN`, `ETIMEDOUT`, `EPIPE`, `ECONNABORTED`, `ERR_STREAM_PREMATURE_CLOSE`,
|
|
106
|
+
`UND_ERR_CONNECT_TIMEOUT`, `UND_ERR_HEADERS_TIMEOUT`, `UND_ERR_BODY_TIMEOUT`, or `UND_ERR_SOCKET`;
|
|
107
|
+
- a host-owned terminal error or cause leaf exactly equal, ignoring ASCII case, to `socket closed
|
|
108
|
+
unexpectedly`, `connection reset by server`, `socket hang up`, `service unavailable (503)`, or
|
|
109
|
+
`AI_APICallError: Service Unavailable (503)`.
|
|
110
|
+
|
|
111
|
+
Inspect only the host/tool invocation-error channel and structured error fields. Do not search partial
|
|
112
|
+
model output, artifact text, logs, review prose, or repository content for these words. If the host does
|
|
113
|
+
not preserve error origin, classification is unknown. A partial stream followed by a qualifying transport
|
|
114
|
+
error is not a completed response: discard it as a result, but assume execution may have started.
|
|
115
|
+
Authentication, authorization, quota, rate-limit, invalid-request, context-limit, content-policy,
|
|
116
|
+
cancellation, local configuration, and unknown failures are not confirmed retryable infrastructure
|
|
117
|
+
failures, even if another field contains an eligible status or phrase.
|
|
118
|
+
|
|
119
|
+
**Every infrastructure-triggered needs-human park follows one sequence.** This sequence explicitly splices unlock
|
|
120
|
+
between the shared parked-stop procedure's snapshot and report steps:
|
|
121
|
+
|
|
122
|
+
1. Quiesce every outstanding specialist, tool, and heartbeat call.
|
|
123
|
+
2. Preserve the current persisted attempt when the subject is budgeted; for an unbudgeted subject, create
|
|
124
|
+
no durable attempt or progress record.
|
|
125
|
+
3. Execute shared parked-stop step 1 with the exact reason token selected below.
|
|
126
|
+
4. Execute shared parked-stop step 2. Attempt the parked snapshot and retain its verified path or its
|
|
127
|
+
publication failure for the report.
|
|
128
|
+
5. Whether or not step 2 published a snapshot, release this driver's verified owning session and require
|
|
129
|
+
qualified status to show an absent lock and a null owner.
|
|
130
|
+
6. Only after unlock verification succeeds, execute shared parked-stop step 3 and report the retained
|
|
131
|
+
sandbox with the snapshot path or failure.
|
|
132
|
+
|
|
133
|
+
If release or unlock verification fails, do not execute shared step 3 and do not issue the normal
|
|
134
|
+
parked-success report. Report only `Outcome: retained-lock-error` with actual status, terminal result,
|
|
135
|
+
lock state, and error.
|
|
136
|
+
|
|
137
|
+
Each branch selects exactly one reason token and no other text. Never put the provider error, response
|
|
138
|
+
fragment, URL, credential, token, diagnostics, or any host-supplied string into it. Bind the selected
|
|
139
|
+
reason as `PRE_QUOTING_REASON` and transport it as the sole `--reason` argument with the deterministic
|
|
140
|
+
POSIX single-quote encoding defined under Publishing identity enforcement; the encoded token is never
|
|
141
|
+
persisted.
|
|
142
|
+
|
|
143
|
+
| branch token | exact persisted reason template |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `NON_RETRYABLE_REASON` | `specialist invocation failed with a non-retryable error for <role> on <subject>; inspect the host invocation log, then prove execution never started or recover the same invocation before continuing` |
|
|
146
|
+
| `UNKNOWN_OUTCOME_REASON` | `specialist infrastructure outcome unknown for <role> on <subject>; prove execution never started or recover the same invocation before continuing` |
|
|
147
|
+
| `SECOND_FAILURE_REASON` | `specialist infrastructure failed twice consecutively for <role> on <subject>; after provider or network recovery, prove execution never started or recover the same invocation before continuing` |
|
|
148
|
+
|
|
149
|
+
`<role>` is the exact canonical dispatch target, never agent frontmatter or host/provider text. Select
|
|
150
|
+
`<subject>` from this closed map:
|
|
151
|
+
|
|
152
|
+
| role | canonical subject |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `story-reader`, `story-writer` | `stage:story` |
|
|
155
|
+
| `codebase-researcher` | `stage:research` |
|
|
156
|
+
| `design-interpreter` | `stage:design` |
|
|
157
|
+
| `spec-writer` | `step:spec-writer` |
|
|
158
|
+
| `work-decomposer` | `step:work-decomposer` |
|
|
159
|
+
| `backend-builder`, `frontend-builder` | `slice:<slice-id>` |
|
|
160
|
+
| `test-verifier` | `step:test-verifier` |
|
|
161
|
+
| `implementation-validator` | `stage:implementation-validator` |
|
|
162
|
+
| `work-reviewer` | the exact reviewed subject: `step:spec-writer`, `step:work-decomposer`, `slice:<slice-id>`, or `step:test-verifier` |
|
|
163
|
+
|
|
164
|
+
For a reason template, render `<slice-id>` from at most the first 80 ASCII characters of the schema-valid
|
|
165
|
+
persisted slice ID and append `~` when truncated. The in-memory invocation key still uses the exact full
|
|
166
|
+
role, subject, and persisted attempt when one exists. Constants come only from the table and slice IDs
|
|
167
|
+
come only from `run.json`; neither field comes from host or provider diagnostics.
|
|
168
|
+
|
|
169
|
+
If an excluded or non-transport failure returns no complete response, preserve the persisted attempt when
|
|
170
|
+
one exists, create no attempt for unbudgeted work, and take the common infrastructure-park sequence with
|
|
171
|
+
`NON_RETRYABLE_REASON`. Do not retry it or treat it as rejected work.
|
|
172
|
+
|
|
173
|
+
For each active invocation key — exact specialist role, exact workflow subject or slice, and the current
|
|
174
|
+
persisted attempt number when that subject is budgeted — hold an in-memory consecutive-infrastructure-
|
|
175
|
+
failure count. Start it at zero in every new driver invocation, including after resume; never write it to
|
|
176
|
+
`run.json` or any artifact. A complete specialist response or a non-infrastructure outcome for that same
|
|
177
|
+
key resets it to zero. Activity for another key neither combines with nor resets it.
|
|
178
|
+
|
|
179
|
+
On the first confirmed failure for a key, increment only that in-memory count. Keep the control plane
|
|
180
|
+
unchanged: do not issue any step or slice transition with `--attempts N+1`, do not observe or review
|
|
181
|
+
partial output, and do not record `accepted`, `rejected`, or `blocked`. Continue automatically only
|
|
182
|
+
through one of these two safe paths:
|
|
183
|
+
|
|
184
|
+
1. When trusted host metadata proves execution never started, re-dispatch the exact same role, subject,
|
|
185
|
+
inputs and, when budgeted, the persisted attempt number.
|
|
186
|
+
2. When execution started or may have started, recover and continue the same host dispatch or child
|
|
187
|
+
session by its existing host-owned identity. First inspect that same child and its expected artifact or
|
|
188
|
+
worktree; never create a second child for the logical attempt and never repeat successful siblings in a
|
|
189
|
+
parallel wave.
|
|
190
|
+
|
|
191
|
+
If neither path is available, preserve the persisted attempt when one exists, create none for unbudgeted
|
|
192
|
+
work, and take the common infrastructure-park sequence with `UNKNOWN_OUTCOME_REASON`. An unbudgeted
|
|
193
|
+
research or design call still permits at most one safe same-invocation recovery and creates no durable
|
|
194
|
+
progress record.
|
|
195
|
+
|
|
196
|
+
On the second consecutive confirmed failure for the same key during that safe re-dispatch or recovery,
|
|
197
|
+
do not invoke it again. Take the common infrastructure-park sequence with `SECOND_FAILURE_REASON`.
|
|
198
|
+
|
|
199
|
+
The failure count remains invocation-local: every new driver, including one entered after explicit resume,
|
|
200
|
+
starts it at zero. That reset is not proof that prior execution did not start. When the preserved historical
|
|
201
|
+
terminal reason matches any infrastructure reason template, it guards the first later dispatch of the
|
|
202
|
+
named canonical role and subject at the retained attempt, or the named unbudgeted stage. Before that
|
|
203
|
+
dispatch, trusted host metadata must prove the prior invocation never started, or the driver must recover
|
|
204
|
+
and inspect that same host dispatch or child identity and its expected artifact or worktree. Explicit
|
|
205
|
+
resume, `status.next`, provider recovery, the reset count, and an operator assertion alone establish
|
|
206
|
+
neither fact. If neither safe path is available, do not dispatch; re-enter the common infrastructure-park
|
|
207
|
+
sequence with `UNKNOWN_OUTCOME_REASON`. Never repeat successful siblings.
|
|
208
|
+
|
|
209
|
+
A budgeted attempt advances only after a complete specialist response reaches the ordinary workflow and
|
|
210
|
+
that response is rejected on its merits or violates the specialist's required output contract. A complete
|
|
211
|
+
unbudgeted result returns to its ordinary workflow without creating an attempt. Infrastructure recovery is
|
|
212
|
+
the same attempt, not another use of `max_retries`. For a slice, an output-contract violation may advance
|
|
213
|
+
only when canonical evidence and its matching REJECT review were recorded; if the malformed output prevents
|
|
214
|
+
either record, park top-level `needs-human` through the common procedure instead of fabricating authority for
|
|
215
|
+
N+1. This rule is instruction rather than CLI enforcement: the host owns specialist invocation errors, while
|
|
216
|
+
the CLI continues to enforce every durable attempt transition the driver actually records.
|
|
217
|
+
|
|
218
|
+
### Operator-authorized retry extension
|
|
219
|
+
|
|
220
|
+
A slice that reached its effective retry limit remains terminal until an operator explicitly grants exactly
|
|
221
|
+
one more attempt. Never edit `run.json`. Keep the top-level run in its existing parked state, claim and verify its
|
|
222
|
+
fresh session lock, record a concrete reason why N+1 is now bounded and materially different, and choose one
|
|
223
|
+
scope deliberately:
|
|
224
|
+
|
|
225
|
+
```sh
|
|
226
|
+
factory grant-retry "$R" "$SLICE_ID" --scope slice --reason "$EXTENSION_REASON" --session "$SESSION_ID" --repo "$RUN_REPO"
|
|
227
|
+
factory grant-retry "$R" "$SLICE_ID" --scope all --reason "$EXTENSION_REASON" --session "$SESSION_ID" --repo "$RUN_REPO"
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`slice` raises only that slice's additive allowance. `all` raises the run-wide default, including every
|
|
231
|
+
pending later wave, but still reopens only `SLICE_ID`; it refuses while another slice is blocked or an
|
|
232
|
+
exhausted post-merge repair exists. Both scopes require `blocked@N` exactly at the current effective limit;
|
|
233
|
+
a matching REJECT, evidence, immutable base and live clean branch head; the exact fresh lock owner; a
|
|
234
|
+
complete current park snapshot; and immutable attempt-N review and evidence archives. A legacy run missing
|
|
235
|
+
an archive gets a preparation-only refusal: publish the changed plane and invoke the grant again. They
|
|
236
|
+
append the durable authorization, preserve worktree, branch and `base_ref`, clear only the live attempt-bound
|
|
237
|
+
refs, and record `running@(N+1)` while top-level status and `terminal_result` remain parked.
|
|
238
|
+
|
|
239
|
+
The grant atomically moves the old canonical snapshot away so restore cannot recover pre-grant authority.
|
|
240
|
+
Do not dispatch or resume yet. Republish the updated live plane, then require qualified status
|
|
241
|
+
to report the refreshed `park_snapshot`, unchanged owner, the chosen new effective limit, and only the named
|
|
242
|
+
slice at `running@(N+1)`. Only then run the ordinary explicit
|
|
243
|
+
`factory resume "$R" --session "$SESSION_ID" --repo "$RUN_REPO"` and dispatch that attempt. Resume refreshes
|
|
244
|
+
the staged workflow before its final snapshot check. A grant never invokes a specialist, unlocks, resumes,
|
|
245
|
+
approves, merges, or publishes.
|
|
246
|
+
|
|
92
247
|
## The chain
|
|
93
248
|
|
|
94
249
|
```
|
|
@@ -255,6 +410,12 @@ sandbox: the completed handoff is the only thing that archives it, and that hand
|
|
|
255
410
|
`completed`. So anything that removed the sandbox destroyed the manifest, the approved gates, the ratified
|
|
256
411
|
plan and every review verdict, leaving the run neither resumable nor reconstructable.
|
|
257
412
|
|
|
413
|
+
`factory snapshot "$R" --repo "$O" --json` performs this publication and is the supported way to do it.
|
|
414
|
+
The steps below remain the definition of what it does; a driver may run the command instead of carrying
|
|
415
|
+
them out itself, and a supervisor that parked a run out-of-band **must** run it, because `factory
|
|
416
|
+
terminal` alone completes step 1 of the park and leaves no recovery evidence. It refuses a run that is
|
|
417
|
+
not parked, so it cannot record a live plane as a snapshot of a moment no resume can return to.
|
|
418
|
+
|
|
258
419
|
Publish the live plane `P` to `$O/.factory/.parked/$R`. Inspect `$O/.factory` and `$O/.factory/.parked` with
|
|
259
420
|
non-following metadata reads, creating each missing parent one directory at a time and requiring any present
|
|
260
421
|
one to be a real directory rather than a symbolic link. Never write through a symlinked parent, and never
|
|
@@ -272,12 +433,11 @@ and "clean up the prior copy" are contradictory instructions once that rename ha
|
|
|
272
433
|
outside `P`; do not copy slice worktrees or any other part of `S`.
|
|
273
434
|
3. **Verify.** Build source and destination inventories exactly as the completed archive does — every
|
|
274
435
|
entry's relative path, type and mode, a SHA-256 for each regular file, a link target for each symlink,
|
|
275
|
-
sorted lexically — and require exact equality,
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
the
|
|
280
|
-
reason. An unverified staging tree is never published.
|
|
436
|
+
sorted lexically — and require exact equality, excluding only plane-root `factory.lock` and
|
|
437
|
+
`run-json.lock`. The first is session liveness and can change on a timer; the second is held by the
|
|
438
|
+
snapshot command to serialize publication with state transitions. The exclusions are those exact root
|
|
439
|
+
paths and nothing else: either name below the plane root is run state and must match. Qualified status
|
|
440
|
+
applies the same exact exclusions at a transition boundary. An unverified staging tree is never published.
|
|
281
441
|
4. **Commit.** With no snapshot at the canonical path, rename `.staging-$R` onto it; that rename is the
|
|
282
442
|
commit point. With one present, first rename the canonical snapshot to `.prior-$R`, then rename
|
|
283
443
|
`.staging-$R` onto the canonical path; that second rename is the commit point. If the first rename
|
|
@@ -304,9 +464,16 @@ Three properties make this safe to do at a park rather than only at completion:
|
|
|
304
464
|
failed would leave `status: running` with nothing alive, which every health signal misreads — a worse
|
|
305
465
|
outcome than a missing snapshot.
|
|
306
466
|
|
|
307
|
-
A snapshot is
|
|
308
|
-
|
|
309
|
-
|
|
467
|
+
A snapshot is a restore input, not a live run: resume still operates only on a sandbox manifest. While `S`
|
|
468
|
+
exists, never restore over it or treat the snapshot as authority over the live plane. If `S` is lost, fetch
|
|
469
|
+
the feature branch still advertised by the operator effective push endpoint into a full remote-tracking ref whose suffix is the recorded branch, then run
|
|
470
|
+
`factory restore "$R" --repo "$O" --from "$RESTORE_REF" --json`. Restore creates only the exact derived
|
|
471
|
+
sandbox, remains parked and lockless, aligns and rechecks the operator's effective push target, omits the
|
|
472
|
+
old plane-root lock, proves every preserved merged-slice Git and evidence binding, resets unrecoverable nonmerged slices to `pending`, and reports `reset_slices` and `invalidated`. It omits prior-generation
|
|
473
|
+
canonical verifier records and always invalidates Gate 3 and test-verifier state. It records its source inventory and restored commit at `status.restore`; `park_snapshot` becomes `null` because the new
|
|
474
|
+
generation is intentionally not byte-identical to its source. Review those losses, bind `RUN_REPO` to the
|
|
475
|
+
returned sandbox, and only then enter the ordinary claim-and-resume order. Do not publish snapshots for
|
|
476
|
+
`blocked` or `partial`, which are not resumable.
|
|
310
477
|
|
|
311
478
|
At every interactive gate, `changes: <feedback>` records `changes`, follows
|
|
312
479
|
`changes-at-gate:<name>`, revises only the affected stage, and re-presents it pending. `stop` requires
|
|
@@ -376,10 +543,13 @@ The optional repository-owned file is `$O/.factory.json`:
|
|
|
376
543
|
}
|
|
377
544
|
```
|
|
378
545
|
|
|
379
|
-
The root must be a JSON object with the
|
|
380
|
-
plus only the optional own properties `pr_draft`, `verify_timeout_ms`, `bootstrap`, and
|
|
546
|
+
The root must be a JSON object with the two required own properties `resolve` and `verify`,
|
|
547
|
+
plus only the optional own properties `publish`, `pr_draft`, `verify_timeout_ms`, `bootstrap`, and
|
|
381
548
|
`bootstrap_timeout_ms`. `resolve`, `verify`, `publish`, and `bootstrap` are command strings; every present
|
|
382
|
-
command must be non-empty.
|
|
549
|
+
command must be non-empty. `publish` was required and invoked nowhere until this release, so every
|
|
550
|
+
consumer wrote a command that could not run. It is optional now and contributes only the file candidate
|
|
551
|
+
to the one Step 6 publishing selection. Inherited `FACTORY_PUBLISHING_COMMAND` or the default may win, so
|
|
552
|
+
presence alone never executes this entry. There is no `publishing_identity` key: the account a run publishes as is a
|
|
383
553
|
property of the environment it runs in, not of the repository, and a tracked file cannot hold two values
|
|
384
554
|
for one repository published from both a maintainer's checkout and an automated host. A file carrying that
|
|
385
555
|
key is malformed, because the optional set above is closed. `pr_draft` must be a JSON boolean
|
|
@@ -388,7 +558,7 @@ safe integers when present, and `bootstrap_timeout_ms` is valid only with a decl
|
|
|
388
558
|
`verify_timeout_ms` and `bootstrap_timeout_ms` each independently default to `900000` milliseconds;
|
|
389
559
|
neither timeout shares or consumes the other's budget.
|
|
390
560
|
|
|
391
|
-
Validation refuses the first matching defect in this order: unreadable or invalid JSON, a non-object root, or unknown keys; invalid `pr_draft`; invalid `bootstrap`; `bootstrap_timeout_ms` without `bootstrap`; invalid `bootstrap_timeout_ms`; invalid `verify_timeout_ms`;
|
|
561
|
+
Validation refuses the first matching defect in this order: unreadable or invalid JSON, a non-object root, or unknown keys; invalid `pr_draft`; invalid `bootstrap`; `bootstrap_timeout_ms` without `bootstrap`; invalid `bootstrap_timeout_ms`; invalid `verify_timeout_ms`; missing or invalid required entries; then invalid `publish`.
|
|
392
562
|
|
|
393
563
|
Do not use the obsolete summary “Validation refuses the first matching defect in this order: unreadable or invalid JSON, a non-object root, or unknown keys; invalid `bootstrap`; `bootstrap_timeout_ms` without `bootstrap`; invalid `bootstrap_timeout_ms`; invalid `verify_timeout_ms`; then missing or invalid required entries.” because it omits the earlier `pr_draft` check.
|
|
394
564
|
|
|
@@ -516,7 +686,7 @@ Do not create, write, merge, archive, or package `.factory.json`. It remains ope
|
|
|
516
686
|
committed, so every clone and sandbox carries it, and refused by the privileged-path policy, so a run
|
|
517
687
|
cannot widen its own configuration. It lived under the gitignored `.factory/` run directory until that proved unusable —
|
|
518
688
|
`.factory/` is gitignored, so the file could not be committed and never reached a sandbox clone, which
|
|
519
|
-
made the `verify` and
|
|
689
|
+
made the `verify` and `publish` entries unavailable and left this repository unable to resolve
|
|
520
690
|
a reference from a fresh checkout. For configured resolver execution, add no helper module,
|
|
521
691
|
command runner, parser service, plugin bridge, transport, protocol, or CLI command. Add no resolver
|
|
522
692
|
cache, payload handoff, manifest or session
|
|
@@ -534,11 +704,14 @@ separate capture policy, output channel, buffering, truncation, redaction, outpu
|
|
|
534
704
|
retry, or fallback after any configured resolver result or failure. The verify timeout and bounded retry
|
|
535
705
|
below apply only to repository `verify` shell attempts; the bootstrap timeout applies only to CLI-owned
|
|
536
706
|
init and explicit resume. Neither applies to `resolve`, slice observation, or Gate 3 commands. Do not
|
|
537
|
-
change platform placement, background-tool, title-association, host-session
|
|
707
|
+
change platform placement, background-tool, title-association, or host-session behavior. Publication changes only through the optional Step 6 command below.
|
|
538
708
|
`story-reader` remains lookup-free and capability-free beyond its existing generic read tools.
|
|
539
709
|
|
|
540
710
|
`resolve` and `verify` are consumed now, and the run's recorded `publishing_identity` is compared at the
|
|
541
|
-
guards below.
|
|
711
|
+
guards below. Step 6 resolves exactly one publishing selection from inherited
|
|
712
|
+
`FACTORY_PUBLISHING_COMMAND`, the optional `publish` entry, or the default, in the precedence defined
|
|
713
|
+
below. Only a selected nondefault command replaces the driver's `gh pr create`; the factory-owned exact
|
|
714
|
+
push and post-push identity guard remain unchanged.
|
|
542
715
|
|
|
543
716
|
Configured `bootstrap` is consumed only by CLI-owned fresh init and explicit resume; the workflow consumer validates it but never executes it itself.
|
|
544
717
|
|
|
@@ -548,7 +721,7 @@ Effective push-target capture and comparison are active through the package-owne
|
|
|
548
721
|
|---|---|---|---|---|
|
|
549
722
|
| `bootstrap` | Exact configured string as one shell command with `shell: true`, inherited environment and stdin, cwd exactly the selected sandbox, and child stdout and stderr both routed to CLI stderr. Each execution receives its own `bootstrap_timeout_ms`, independently `900000` when omitted. | Numeric exit status or unavailable `null`; output is visible on CLI stderr and never parsed | Clean zero succeeds; dirty or unobservable tracked state outranks unavailable or nonzero exit | Invoked by the CLI once during configured fresh init and again on every explicit configured resume; never invoked by resolver, merge verification or replay, direct repository verification, slice or Gate 3 observation, effective push, or publication. |
|
|
550
723
|
| `verify` | Ordinary shell step in the exact integration-worktree cwd with inherited environment; no structured stdin or factory-specific payload is defined. Each attempt receives the full configured `verify_timeout_ms`, silently `900000` when omitted. | Exit status is authoritative; stdout and stderr are inherited, informational, and unparsed | Zero means success; non-zero means repository verification failed; no numeric child status means unavailable | Invoked after each newly recorded merge through `observe --repository-verify`, with at most two executions in that merge invocation. The timeout and retry never apply to resolver, slice, or Gate 3 commands. |
|
|
551
|
-
| `publish` |
|
|
724
|
+
| `publish` | Optional file candidate for the one Step 6 publishing selection. A nonblank inherited `FACTORY_PUBLISHING_COMMAND` selects its exact string; the same variable set empty or to whitespace selects the default; when the variable is unset this entry is selected if present, otherwise the default. A selected nondefault command runs as one shell step in `RUN_REPO` cwd, with no stdin or positional arguments and inherited environment plus exact `PR_BASE`, `FEATURE_BRANCH`, `PR_DRAFT`, `PR_TITLE`, and absolute `PR_BODY_FILE`. | Exit status is authoritative; the last nonempty stdout line must be an absolute HTTPS URL and becomes `PR_URL` | Zero plus that URL is recordable; any other result is indeterminate and parks before `factory pr` | The resolved selection replaces only `gh pr create`, after the factory-owned exact push and post-push identity guard. `factory pr` is unchanged and still records the URL. |
|
|
552
725
|
| `publishing_identity` | No runtime input; read the value `status` reports for the run, recorded at init from `--publishing-identity` or the inherited `FACTORY_PUBLISHING_IDENTITY` | Exact case-sensitive string compared with the observed login | Absent at init refuses before any sandbox exists; mismatch or unobservable identity parks the run | Active at the three mandatory guards below; only a manifest written before 0.8.0 can report `null` and skip them. |
|
|
553
726
|
|
|
554
727
|
When both bootstrap keys are absent, init and resume are exact no-ops for bootstrap: no execution, manifest fields, output, or response-shape change.
|
|
@@ -656,7 +829,7 @@ intervene between the verified running/same-owner result and that guard, or betw
|
|
|
656
829
|
and reconciliation. A pre-0.8.0 manifest reporting `null` preserves the nine orders without adding an operation.
|
|
657
830
|
The refreshed workflow read belongs to order 7 verification, before this boundary.
|
|
658
831
|
|
|
659
|
-
For order 1 require the intended run ID, a valid manifest, recorded branch and mode, current parked status, and the original terminal result. Order 2 stays after selection and containment and before effective-push proof. Order 3 never absorbs containment, binding, or the post-selection exact-ref guard. During order 4 preserve every existing exact-ref recheck and the stated provenance sequence. No unrelated observation or effect occurs between order 5 and claim or justified steal. Order 6 requires `lock_session === SESSION_ID`, a fresh lock, unchanged parked status, and a terminal result deeply equal to the one first observed. Invoke `factory resume "$R" --session "$SESSION_ID" --repo "$RUN_REPO"` for order 7 — the same session order 6 just verified as the fresh owner — then require that owner unchanged. Resume refuses without it, and refuses a lock that is absent, stale, or held by anyone else. Order 8 may replay only the existing recorded-merge reconciliation path and must not move pre-lock proofs across the lock boundary. Order 9
|
|
832
|
+
For order 1 require the intended run ID, a valid manifest, recorded branch and mode, current parked status, and the original terminal result. Order 2 stays after selection and containment and before effective-push proof. Order 3 never absorbs containment, binding, or the post-selection exact-ref guard. During order 4 preserve every existing exact-ref recheck and the stated provenance sequence. No unrelated observation or effect occurs between order 5 and claim or justified steal. Order 6 requires `lock_session === SESSION_ID`, a fresh lock, unchanged parked status, and a terminal result deeply equal to the one first observed. Invoke `factory resume "$R" --session "$SESSION_ID" --repo "$RUN_REPO"` for order 7 — the same session order 6 just verified as the fresh owner — then require that owner unchanged. Resume refuses without it, and refuses a lock that is absent, stale, or held by anyone else. Order 8 may replay only the existing recorded-merge reconciliation path and must not move pre-lock proofs across the lock boundary. Order 9 uses only the newly qualified next action for workflow progress, but before its first matching specialist dispatch it must apply the preserved infrastructure-reason guard above; it never treats explicit resume or the count reset as no-start proof.
|
|
660
833
|
|
|
661
834
|
If resume refuses after claim or the run later reparks, quiesce builders, tools, specialist tasks, and
|
|
662
835
|
heartbeat loops. Qualify the intended retained run again before reporting the stop. If it is still parked
|
|
@@ -1276,11 +1449,16 @@ For a fresh pending slice, set the exact names, require both `refs/heads/$SLICE_
|
|
|
1276
1449
|
`SLICE_WORKTREE` path to be absent, and create the worktree from the current feature branch before
|
|
1277
1450
|
activation:
|
|
1278
1451
|
|
|
1452
|
+
Before creating a pending slice worktree, reload its exact manifest row. Bind `ACTIVATION_START` to
|
|
1453
|
+
`FEATURE_BRANCH` when `base_ref` is null. When a restored retry-extension row preserves non-null `base_ref`,
|
|
1454
|
+
require its latest audit to name the same base and start the replacement slice branch at that exact historical
|
|
1455
|
+
base. This recreates the original retry branch without importing later sibling changes into its owned diff.
|
|
1456
|
+
|
|
1279
1457
|
```sh
|
|
1280
1458
|
SLICE_BRANCH="factory/$R/$SLICE_ID"
|
|
1281
1459
|
SLICE_WORKTREE="$SLICE_ROOT/$SLICE_ID"
|
|
1282
1460
|
CHECKED_OUT_FEATURE_BRANCH="$(git -C "$INTEGRATION_WORKTREE" symbolic-ref --quiet --short HEAD)"
|
|
1283
|
-
git -C "$RUN_REPO" worktree add -b "$SLICE_BRANCH" "$SLICE_WORKTREE" "$
|
|
1461
|
+
git -C "$RUN_REPO" worktree add -b "$SLICE_BRANCH" "$SLICE_WORKTREE" "$ACTIVATION_START"
|
|
1284
1462
|
$ factory slice "$R" "$SLICE_ID" running --worktree "$SLICE_WORKTREE" --branch "$SLICE_BRANCH" --repo "$RUN_REPO"
|
|
1285
1463
|
```
|
|
1286
1464
|
|
|
@@ -1294,6 +1472,8 @@ Step 0. Require `run_id === R`, select exactly one `slices` row with `id === SLI
|
|
|
1294
1472
|
|
|
1295
1473
|
```text
|
|
1296
1474
|
RECORDED_SLICE = parsedRun.slices row whose id equals SLICE_ID
|
|
1475
|
+
MAX_RETRIES = parsedRun.max_retries
|
|
1476
|
+
SLICE_RETRY_LIMIT = MAX_RETRIES + (RECORDED_SLICE.extra_attempts when present, otherwise 0)
|
|
1297
1477
|
SLICE_WORKTREE = RECORDED_SLICE.worktree
|
|
1298
1478
|
SLICE_BRANCH = RECORDED_SLICE.branch
|
|
1299
1479
|
SLICE_BASE_REF = RECORDED_SLICE.base_ref
|
|
@@ -1307,13 +1487,15 @@ for it. A driver that assumes "this is the first try" observes as attempt 1 whil
|
|
|
1307
1487
|
merge then refuses that evidence — `evidence '…' is for attempt 1, slice is at attempt 2` — after the build
|
|
1308
1488
|
and the review have already been spent. It names the report and the `--attempt` argument below.
|
|
1309
1489
|
|
|
1310
|
-
Require
|
|
1311
|
-
a
|
|
1312
|
-
|
|
1313
|
-
`
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1490
|
+
Require every bound value to be non-null, `MAX_RETRIES`, `SLICE_RETRY_LIMIT`, and `SLICE_ATTEMPT` to be positive integers,
|
|
1491
|
+
`SLICE_BASE_REF` to be a 40-character commit SHA, `SLICE_BRANCH` to equal `factory/R/<slice-id>`, and the
|
|
1492
|
+
physical `SLICE_WORKTREE` to equal `SLICE_ROOT/<slice-id>`. Require `git -C "$RUN_REPO" worktree list
|
|
1493
|
+
--porcelain` to associate that physical path with that exact branch. Before dispatch or observation require
|
|
1494
|
+
status `running`. A `review` row is a completed decision checkpoint: on resume consume its recorded review
|
|
1495
|
+
through step 4, never re-observe it. A pending slice requires path and branch ref to remain absent. Its
|
|
1496
|
+
base is absent unless a restored retry-extension audit preserves that immutable base; any unrecorded path
|
|
1497
|
+
or branch ref is a collision. Refuse every mismatch instead of repairing, deleting, or
|
|
1498
|
+
reassociating it. A merged slice is never dispatched again.
|
|
1317
1499
|
|
|
1318
1500
|
For a non-empty `SLICE_TEST_PLAN`, select one complete entry and bind `SLICE_TEST_COMMAND` by copying
|
|
1319
1501
|
that persisted string verbatim. Never shorten, append to, normalize, or source it from the mutable
|
|
@@ -1367,31 +1549,32 @@ Per slice:
|
|
|
1367
1549
|
is no repair available at this step.** The slice is already activated, so `base_ref` is fixed; the suite
|
|
1368
1550
|
runs in `SLICE_WORKTREE`, so a commit on the integration branch is invisible to the re-observation; and
|
|
1369
1551
|
bringing that commit into the slice would put an out-of-lane test path in the observed diff, which the
|
|
1370
|
-
merge refuses.
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1552
|
+
merge refuses. This is a ratified-plan conflict, not a merit REJECT: do not mark the slice `blocked`
|
|
1553
|
+
without canonical evidence and a matching max-attempt review. Stop dispatching its dependents and park
|
|
1554
|
+
top-level `needs-human` through the common procedure with the diagnosis below. Retain merged siblings on
|
|
1555
|
+
the integration branch and the whole sandbox for operator replanning. The parked run is **surfaced, not
|
|
1556
|
+
published**: Gate 3 refuses the approval that authorizes publication unless every slice is `merged`, so
|
|
1557
|
+
an operator decides what to do with the work rather than a PR appearing for a plan that did not finish.
|
|
1375
1558
|
|
|
1376
1559
|
**Never narrow the ratified command to get past this.** `factory observe` compares the raw supplied
|
|
1377
1560
|
slice command with the persisted ratified entries before tokenization or execution and refuses a
|
|
1378
|
-
shortened, appended, or normalized command without writing evidence. A narrowed command is a false green wearing evidence's clothes;
|
|
1561
|
+
shortened, appended, or normalized command without writing evidence. A narrowed command is a false green wearing evidence's clothes; parking for replanning is the honest outcome when the verbatim command fails.
|
|
1379
1562
|
|
|
1380
1563
|
If the same incompatibility instead first appears in the **integrated** suite, this step is not involved
|
|
1381
1564
|
at all — Step 5's NO-GO repair owns it, on the branch where that suite actually runs.
|
|
1382
1565
|
|
|
1383
|
-
When you
|
|
1384
|
-
`--reason`: which slice owns the test, which assertion cannot hold, and what would
|
|
1385
|
-
reason naming only "tests failed" makes the operator repeat the whole investigation,
|
|
1386
|
-
difference between their fix being one commit and being an afternoon.
|
|
1566
|
+
When you park this plan conflict, record the **diagnosis** and not just the failure in the park
|
|
1567
|
+
terminal transition's `--reason`: which slice owns the test, which assertion cannot hold, and what would
|
|
1568
|
+
make it hold. A reason naming only "tests failed" makes the operator repeat the whole investigation,
|
|
1569
|
+
which is the difference between their fix being one commit and being an afternoon.
|
|
1387
1570
|
|
|
1388
1571
|
An out-of-lane **production** change is a different thing entirely and follows **Ownership disclosure**
|
|
1389
1572
|
below, where the reviewer decides whether the plan or the change is wrong.
|
|
1390
1573
|
4. **Review** — `work-reviewer` with subject `<slice-id>`, the observed evidence, the slice spec, and
|
|
1391
1574
|
the brief. Record both refs — the merge requires each:
|
|
1392
1575
|
```sh
|
|
1393
|
-
$ factory slice "$R" "$SLICE_ID" review --
|
|
1394
|
-
--review-ref "reviews/$SLICE_ID.json" --repo "$RUN_REPO"
|
|
1576
|
+
$ factory slice "$R" "$SLICE_ID" review --attempts "$SLICE_ATTEMPT" \
|
|
1577
|
+
--evidence-ref "evidence/$SLICE_ID.json" --review-ref "reviews/$SLICE_ID.json" --repo "$RUN_REPO"
|
|
1395
1578
|
```
|
|
1396
1579
|
- On REJECT, before spending an attempt, identify the cause of the remaining failures. If the fix would
|
|
1397
1580
|
violate an approved story or brief constraint, or repeated findings trace to the same unresolved
|
|
@@ -1401,8 +1584,26 @@ Per slice:
|
|
|
1401
1584
|
If no such target can be identified, park through the existing parked-stop procedure for replanning
|
|
1402
1585
|
or operator clarification; preserve the work and do not silently change approved acceptance criteria.
|
|
1403
1586
|
Unchanged finding counts alone are not a stall: progress can occur within a category that remains open.
|
|
1404
|
-
|
|
1405
|
-
`
|
|
1587
|
+
Only a complete merit REJECT spends an attempt; infrastructure recovery and resume preserve
|
|
1588
|
+
`SLICE_ATTEMPT` under the common rules above. Reload `RUN_MANIFEST`, require the recorded review to name
|
|
1589
|
+
this slice and `SLICE_ATTEMPT` with exact verdict `REJECT`, and require the row still to be `review`.
|
|
1590
|
+
If `SLICE_ATTEMPT >= SLICE_RETRY_LIMIT`, record the terminal slice state and stop dispatching its
|
|
1591
|
+
dependents without creating another attempt:
|
|
1592
|
+
```sh
|
|
1593
|
+
$ factory slice "$R" "$SLICE_ID" blocked --attempts "$SLICE_ATTEMPT" --repo "$RUN_REPO"
|
|
1594
|
+
```
|
|
1595
|
+
Then enter the common parked-stop procedure with exact reason `blocked-after-retries`; do not
|
|
1596
|
+
terminalize `partial`. The parked snapshot and retained lock are what make a later audited grant
|
|
1597
|
+
reachable without weakening the terminal slice transition.
|
|
1598
|
+
Otherwise bind `NEXT_SLICE_ATTEMPT = SLICE_ATTEMPT + 1` and, before redispatch, record:
|
|
1599
|
+
```sh
|
|
1600
|
+
$ factory slice "$R" "$SLICE_ID" running --attempts "$NEXT_SLICE_ATTEMPT" --repo "$RUN_REPO"
|
|
1601
|
+
```
|
|
1602
|
+
This retry-opening command takes no worktree, branch, evidence, or review flag. Reload the row and
|
|
1603
|
+
require status `running`, attempt `NEXT_SLICE_ATTEMPT`, unchanged worktree, branch, and exact
|
|
1604
|
+
`SLICE_BASE_REF`, and null evidence and review refs. The base is the immutable original branch point,
|
|
1605
|
+
not merely any ancestor and not the integration head after sibling merges. Then set `SLICE_ATTEMPT` to
|
|
1606
|
+
the recorded new value, route the bounded fixes back to that builder, and re-observe.
|
|
1406
1607
|
5. **Merge (you, serially)** — on APPROVE, merge the slice branch into the feature branch one at a
|
|
1407
1608
|
time. Builds are concurrent; merges are single-writer, which is what makes the parallelism safe.
|
|
1408
1609
|
```sh
|
|
@@ -1720,10 +1921,12 @@ content on those paths matches what was reviewed, so unreviewed content inside *
|
|
|
1720
1921
|
while movement around it is not. What guards the branch as a whole is the integration pass: the
|
|
1721
1922
|
validator judges the whole diff and Gate 3 will not approve unless the head it judged is still the head.
|
|
1722
1923
|
|
|
1723
|
-
Advance waves until all slices are `merged`, or a slice is `blocked`.
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
Use terminal needs-human only to park a running envelope; use explicit factory resume after the cause is fixed.
|
|
1924
|
+
Advance waves until all slices are `merged`, or a slice is `blocked`. A blocked slice stops the wave and
|
|
1925
|
+
enters the common parked-stop procedure as top-level `needs-human`; retry exhaustion never terminalizes the
|
|
1926
|
+
run as `partial`. Use explicit factory resume only after the cause is fixed or a qualified retry grant was
|
|
1927
|
+
recorded and the updated plane was published. Use terminal needs-human only to park a running envelope; use explicit factory resume after the cause is fixed.
|
|
1928
|
+
A `partial` run is **surfaced, not published** when some other checked terminal cause creates one; retry
|
|
1929
|
+
exhaustion uses the parked path above instead.
|
|
1727
1930
|
A top-level needs-human sandbox stays retained while parked and continues only after explicit factory resume.
|
|
1728
1931
|
A `blocked` or `partial` sandbox run retains `RUN_REPO`; stale nonterminal locks retain it
|
|
1729
1932
|
too. Nothing removes any of those sandboxes automatically. Legacy runs
|
|
@@ -1757,16 +1960,27 @@ HEAD, a branch name, or an unpersisted variable.
|
|
|
1757
1960
|
is not a slice and has no slice `test_plan`, so it continues to supply its integration command. There
|
|
1758
1961
|
is no waiver: the stage exists to run the tests, so the evidence must record an observed run that
|
|
1759
1962
|
exited zero, against the integration head as it stands. Then `work-reviewer` confirms each criterion
|
|
1760
|
-
maps to a real assertion.
|
|
1963
|
+
maps to a real assertion, judging the whole integrated diff rather than any one slice's.
|
|
1964
|
+
On a single-slice run this is the last review before publication, and its production findings have no
|
|
1965
|
+
in-band repair: post-merge repair is test-only and a merged slice is never dispatched again, so a
|
|
1966
|
+
production defect parks the run for an operator instead of returning to a builder. Review it as the
|
|
1967
|
+
final reading it is.
|
|
1761
1968
|
This Gate 3 observation is always fresh and independent in the ordinary path. It uses the existing
|
|
1762
1969
|
argv-tokenized `--test-cmd` path and overwrites canonical evidence at the current head. The sole
|
|
1763
1970
|
substitution is a qualifying explicit repair re-verification pass at current HEAD under Gate 3's
|
|
1764
1971
|
complete inventory rules below; failed ordinary evidence remains preserved rather than overwritten.
|
|
1765
1972
|
2. `implementation-validator` — the holistic pass across the whole diff, complementing per-slice
|
|
1766
1973
|
reviews. **Skip it when the run has exactly one slice**: its subject is the interaction *between*
|
|
1767
|
-
slices, and with one there is none, so it
|
|
1768
|
-
a
|
|
1769
|
-
|
|
1974
|
+
slices, and with one there is none, so it has no question of its own to answer. Gate 3 does not
|
|
1975
|
+
require a verdict for a single-slice run. Run it for every multi-slice run; the gate refuses
|
|
1976
|
+
without it.
|
|
1977
|
+
|
|
1978
|
+
**Skipping it does not mean the integrated diff goes unread**, and nothing here should be read as
|
|
1979
|
+
saying a second reading is worthless. Step 1's `work-reviewer` pass over `test-verifier` judges that
|
|
1980
|
+
diff whole, and on a single-slice run it is the only review after the slice's own: mimir's
|
|
1981
|
+
chainlink-1304 merged its one slice clean at zero findings, and that pass then found two production
|
|
1982
|
+
defects in it, both real and both confirmed. What the skip removes is a duplicate *verdict* on a
|
|
1983
|
+
subject that does not exist, not the reading.
|
|
1770
1984
|
|
|
1771
1985
|
When you do run it, it returns GO / GO-WITH-NITS / NO-GO **and writes `reviews/implementation-validator.json`
|
|
1772
1986
|
naming the commit it judged**, exactly like any other reviewer. Then:
|
|
@@ -1978,17 +2192,60 @@ gh api --method GET /user --jq .login
|
|
|
1978
2192
|
factory pr "$R" --url "$PR_URL" --repo "$RUN_REPO"
|
|
1979
2193
|
```
|
|
1980
2194
|
|
|
1981
|
-
The
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2195
|
+
The fully qualified `git push` above is factory-owned and unchanged for every resolved publishing
|
|
2196
|
+
selection. It is the only push in this procedure. The second identity observation always runs after that
|
|
2197
|
+
push is known successful and immediately before the selected pull-request operation, with no intervening
|
|
2198
|
+
operation.
|
|
2199
|
+
|
|
2200
|
+
**Resolve one publishing selection before running anything, and execute that selection rather than
|
|
2201
|
+
either source.** In order: inherited `FACTORY_PUBLISHING_COMMAND` holding at least one non-whitespace
|
|
2202
|
+
character selects that string; the same variable set empty or to whitespace selects the default, even
|
|
2203
|
+
when `$O/.factory.json` declares `publish`; an unset variable selects the configured `publish` when the
|
|
2204
|
+
file declares one; and with neither, the default. The resolution runs whether or not `$O/.factory.json`
|
|
2205
|
+
exists, so an environment-only override selects a command in a repository that declares none, and a
|
|
2206
|
+
declared `publish` is never executed while that variable holds a different value. Nothing downstream
|
|
2207
|
+
reads either source again.
|
|
2208
|
+
|
|
2209
|
+
The environment overrides the file because how a run publishes is a property of the environment as much
|
|
2210
|
+
as of the repository -- the same reason there is no `publishing_identity` key in that file, and one
|
|
2211
|
+
repository is published from both a maintainer's checkout and an automated host. Empty selecting the
|
|
2212
|
+
default is what keeps a repository from declaring its way into a run that cannot publish at all from a
|
|
2213
|
+
host with nothing to delegate to, and it makes empty and absent behave alike rather than needing two
|
|
2214
|
+
rules. The override removes no guard: every run with a non-null recorded `publishing_identity` runs all
|
|
2215
|
+
three identity guards whether or not `$O/.factory.json` exists. Only a legacy manifest reporting `null`
|
|
2216
|
+
skips them. Report which source the selection came from, since the sources are indistinguishable afterwards and
|
|
2217
|
+
an operator debugging a publication needs to know which one ran.
|
|
2218
|
+
|
|
2219
|
+
**When the resolution selects a command rather than the default, run that exact selected string instead
|
|
2220
|
+
of only `gh pr create` above**,
|
|
2221
|
+
as one shell command in `RUN_REPO` cwd with no stdin or positional arguments. Add exactly five values to
|
|
2222
|
+
the inherited environment: exact `PR_BASE`, exact `FEATURE_BRANCH`, `PR_DRAFT` as `true` or `false`, exact
|
|
2223
|
+
decorated `TITLE` as `PR_TITLE`, and an absolute `PR_BODY_FILE` naming the exact decorated body bytes.
|
|
2224
|
+
Read the last nonempty stdout line as `PR_URL` and require it to be an absolute HTTPS URL. The selected
|
|
2225
|
+
nondefault command owns PR creation, but these inputs preserve the recorded base, head, mode, title, and body intent;
|
|
2226
|
+
do not claim the factory verified that the command honored them. `factory pr` still records the returned
|
|
2227
|
+
URL. Exit zero **with** that absolute HTTPS URL is the only recordable result. A non-zero exit, or a zero
|
|
2228
|
+
exit whose last line is not a URL, is indeterminate: do not claim that no external effect occurred,
|
|
2229
|
+
do not run `factory pr`, and do not fall back to `gh pr create`. Bind `PRE_QUOTING_REASON` exactly to
|
|
2230
|
+
`selected publishing command outcome indeterminate; re-observe whether the pull request exists before retry`;
|
|
2231
|
+
persist no other reason text. Never append or interpolate stdout, stderr, exit status or status text, URLs,
|
|
2232
|
+
credentials, tokens, provider diagnostics, or any other command-supplied text. Follow the common quiesce,
|
|
2233
|
+
park, durable-reason transport, owning release, unlock-verification, retention, reporting, and later-driver
|
|
2234
|
+
procedure. Before any retry, re-observe whether the pull request exists and record an existing one rather
|
|
2235
|
+
than creating another.
|
|
2236
|
+
|
|
2237
|
+
When the recorded identity is non-null, both Step 6 identity guards run for every resolved selection and
|
|
2238
|
+
whether or not `.factory.json` exists; only a legacy recorded `null` skips them. A mismatch or unobservable
|
|
2239
|
+
result follows the common quiesce, park, durable-reason, owning release, unlock-verification, retention,
|
|
2240
|
+
reporting, and later-driver procedure above. There is no separate identity guard before `factory pr`;
|
|
2241
|
+
preserve that command and every existing publication mode, status, and gate exactly.
|
|
2242
|
+
|
|
2243
|
+
The selected PR-creation operation is the orchestrator's external effect; the package makes no forge call
|
|
2244
|
+
and `factory pr` does not verify the forge's base. For a legacy manifest where
|
|
2245
|
+
`pr_base` is absent or null, stop and require a human/operator to choose or confirm the exact target,
|
|
2246
|
+
then pass that value through `gh pr create --base` or the selected nondefault command's exact `PR_BASE`.
|
|
2247
|
+
Never
|
|
2248
|
+
infer it from HEAD, the feature branch, repository or forge defaults, and
|
|
1992
2249
|
never backfill the legacy manifest.
|
|
1993
2250
|
|
|
1994
2251
|
`pr_url` is immutable once recorded — a run has one PR, and overwriting the URL would hide a second
|
|
@@ -2159,6 +2416,12 @@ accepted NO-GO findings, recorded overrides, retained or residual sandboxes, or
|
|
|
2159
2416
|
|
|
2160
2417
|
## Resuming
|
|
2161
2418
|
|
|
2419
|
+
If the intended sandbox is absent but qualified operator inspection finds the canonical parked snapshot,
|
|
2420
|
+
restore it first using the exact procedure above. Restore is not resume: it leaves the new generation
|
|
2421
|
+
parked with no owner and may reset or invalidate state. Never substitute a local branch, a bare commit, a
|
|
2422
|
+
new run, or a hand-copied manifest for the full remote-tracking ref and CLI command. After restore, use its
|
|
2423
|
+
returned sandbox as `RUN_REPO`, inspect `status.restore`, and start the same ownership sequence below.
|
|
2424
|
+
|
|
2162
2425
|
On invocation, if the run directory exists and you hold or steal the lock, the preserved compatibility
|
|
2163
2426
|
claim reads “run `factory status <run-id> --json` and resume; never restart.” It names a non-runnable
|
|
2164
2427
|
command stem. Execute only `factory status "$R" --json --repo "$RUN_REPO"`, then continue from `next`:
|
|
@@ -2183,11 +2446,11 @@ Never re-do a side effect the manifest shows already done — ticket creation, p
|
|
|
2183
2446
|
Specialists are read-only toward them and builders write code only inside the worktree they receive.
|
|
2184
2447
|
- **Never hand-write `run.json`.** If a `factory` command refuses a transition, the refusal is the
|
|
2185
2448
|
answer; do not work around it by editing state.
|
|
2186
|
-
- **Bounded loops.**
|
|
2187
|
-
`
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2449
|
+
- **Bounded loops.** Each slice is bounded by `max_retries + extra_attempts`; each step uses
|
|
2450
|
+
`max_retries`. On slice exhaustion mark it `blocked` and park top-level needs-human; do not terminalize
|
|
2451
|
+
`partial` solely for retry exhaustion. Explicit resume may repark if the external cause remains unfixed.
|
|
2452
|
+
Qualified status reports the run's `max_retries` and each slice's effective `retry_limit`, so an
|
|
2453
|
+
extended budget is observed rather than assumed.
|
|
2191
2454
|
- **Publish a PR and stop.** Never merge, force-push, or close tickets. Humans merge. Draft or
|
|
2192
2455
|
ready-for-review is `pr_draft`'s decision, not this rule's.
|
|
2193
2456
|
- **Scope discipline and no fabrication.** Flag out-of-scope work at the next gate. Never invent paths,
|
package/agents/work-reviewer.md
CHANGED
|
@@ -121,7 +121,7 @@ When the subject is a **class-wide** requirement — one that **cannot be establ
|
|
|
121
121
|
docs-only slice reviewed against tests it was ratified not to have is rejected forever; that
|
|
122
122
|
exemption is a plan decision, not yours to re-open here. It waives **test execution only**: the
|
|
123
123
|
acceptance must still be implemented and the diff must still be observed.
|
|
124
|
-
- **Test step (`test-verifier`):** each AC maps to a real assertion that would fail if the behavior broke; no test weakened to pass; the observed command is the suite the plan named and was not narrowed to exclude failures, which is a separate finding from weakening a test; observed test run is green. **There is no WRITTEN-NOT-RUN waiver for this subject:** the stage exists to run the tests, so the evidence must record an observed run that exited zero. Reporting WRITTEN-NOT-RUN honestly is valid; approving on it is not.
|
|
124
|
+
- **Test step (`test-verifier`):** each AC maps to a real assertion that would fail if the behavior broke; no test weakened to pass; the observed command is the suite the plan named and was not narrowed to exclude failures, which is a separate finding from weakening a test; observed test run is green. **There is no WRITTEN-NOT-RUN waiver for this subject:** the stage exists to run the tests, so the evidence must record an observed run that exited zero. Reporting WRITTEN-NOT-RUN honestly is valid; approving on it is not. Because this is the last reading of the integrated diff before publication, make its findings exhaustive in one pass: a production defect recorded here parks the run for an operator rather than returning to a builder, since post-merge repair is test-only and a merged slice is never dispatched again, so there is no later round to carry a withheld finding into.
|
|
125
125
|
|
|
126
126
|
## Security proportionality
|
|
127
127
|
|