muse-crew 0.13.1 → 0.13.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/docs/decisions/AGENTS.md +94 -0
- package/docs/decisions/publish-path.md +1280 -0
- package/docs/decisions/qa-reproduce.md +486 -0
- package/docs/decisions/workflow-core.md +234 -0
- package/docs/guide.md +3 -3
- package/docs/visual-verdict.md +6 -7
- package/lib/compose-evidence-caption.js +36 -5
- package/lib/crew-api.js +8 -1
- package/lib/crew-release.sh +51 -9
- package/package.json +2 -2
- package/seed/crons.json +0 -2
- package/workflows/AGENTS.md +3 -1
- package/workflows/bugfix.js +271 -583
- package/workflows/chore.js +261 -516
- package/workflows/crew-init.js +6 -6
- package/workflows/standard.js +264 -541
|
@@ -0,0 +1,1280 @@
|
|
|
1
|
+
# Decision history: publish path
|
|
2
|
+
|
|
3
|
+
Relocated from workflow source comments during H5 (2026-09-18). The workflows keep only the relied-upon invariant inline; the full decision history lives here.
|
|
4
|
+
|
|
5
|
+
<a id="fire-and-forget-trigger"></a>
|
|
6
|
+
## Fire-and-forget trigger + workflow-owned observation
|
|
7
|
+
|
|
8
|
+
Invariant: the trigger child returns immediately; the workflow owns observation and verdict — never trust the child's self-report.
|
|
9
|
+
|
|
10
|
+
Applies to: standard, bugfix, chore.
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
// Fire-and-forget trigger + workflow-owned observation (2026-09-16,
|
|
14
|
+
// clean-room task e2a8d9f8): the trigger's JSON closeout contract
|
|
15
|
+
// traveled over the stochastic text channel, and the runtime's
|
|
16
|
+
// JSON-candidate heuristic misfired on it ("workflow agent output
|
|
17
|
+
// was not JSON: no JSON object or array found in final response"),
|
|
18
|
+
// parking a task whose edit may have gone through. The contract's
|
|
19
|
+
// content was already observation-only (the applied report never
|
|
20
|
+
// gated; the pre_hashes were diagnostic-only), so the contract is
|
|
21
|
+
// removed: the trigger carries NO schema and its return value is
|
|
22
|
+
// never consumed, which takes the extraction heuristic out of this
|
|
23
|
+
// call entirely. The workflow attributes the edit itself through
|
|
24
|
+
// the tiny schema'd reads below — no prose is parsed for the
|
|
25
|
+
// trigger outcome.
|
|
26
|
+
// (Probe, 2026-09-16: the workflow scope exposes only agent() —
|
|
27
|
+
// tool_search, artifact_edit and artifact_status are undefined
|
|
28
|
+
// there — so the workflow cannot call the artifact tools directly;
|
|
29
|
+
// observation still goes through minimal child calls with tiny
|
|
30
|
+
// schemas, never a broad JSON contract.)
|
|
31
|
+
//
|
|
32
|
+
// Pre-trigger toolcheck (tiny, schema'd): the artifact namespace is
|
|
33
|
+
// deferred for workflow children — the child self-loads it and emits
|
|
34
|
+
// one exact signal line, read mechanically (never English prose).
|
|
35
|
+
// Only a parsed ARTIFACT_TOOLS: missing signal is explicit negative
|
|
36
|
+
// evidence: it gets one bounded retry with a fresh key, then parks
|
|
37
|
+
// rejected — without the tools the edit provably did NOT go through,
|
|
38
|
+
// so this is the one safe retry on the publish path. A throw (or an
|
|
39
|
+
// unparseable signal) is INCONCLUSIVE transport noise, never
|
|
40
|
+
// evidence of missing tools (2026-09-16, critic finding 3): it is
|
|
41
|
+
// recorded, it retries once in case the flake clears, but it can
|
|
42
|
+
// never take the rejected path.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
<a id="publish-attempt-ledger"></a>
|
|
46
|
+
## Durable publish-attempt ledger
|
|
47
|
+
|
|
48
|
+
Invariant: every trigger outcome is recorded in the durable ledger while the trigger is in flight; the receipt poll reads the ledger, not the child's claims.
|
|
49
|
+
|
|
50
|
+
Applies to: standard, bugfix, chore.
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
// Durable publish-attempt ledger (2026-09-12): every artifact publish
|
|
54
|
+
// attempt is recorded append-only at $CREW_HOME/.publish-ledger/<slug>.jsonl
|
|
55
|
+
// on persistent disk (NOT /tmp). The ledger is the correlation record for
|
|
56
|
+
// publish attempts whose outcome is UNKNOWN. When the rebuild trigger's
|
|
57
|
+
// child returns prose instead of JSON (structured-output failure), the edit
|
|
58
|
+
// may already have been accepted as pending_init — and artifact_status
|
|
59
|
+
// cannot see pending_init (diagnostic canary 2026-09-12: an edit accepted
|
|
60
|
+
// as pending_init was immediately followed by an all-false status check,
|
|
61
|
+
// and the old retry issued a DUPLICATE edit). "No build visible" is NOT
|
|
62
|
+
// evidence the edit did not go through, so the workflow never blind-retries
|
|
63
|
+
// on an unknown outcome: it records the attempt and parks fail-closed. A
|
|
64
|
+
// human or a later run correlates the accepted edit via the ledger (commit
|
|
65
|
+
// hash + attempt key + the artifact build's agent_id when one was observed)
|
|
66
|
+
// instead of guessing from a blind status poll.
|
|
67
|
+
// Best-effort observability: a failed write is logged loudly but never
|
|
68
|
+
// throws — the caller's park/proceed decision never depends on the ledger.
|
|
69
|
+
// Byte-identical across standard/bugfix/chore — pinned by
|
|
70
|
+
// tests/publish-ledger.test.js.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
<a id="pre-trigger-baseline"></a>
|
|
74
|
+
## Pre-trigger build-state baseline
|
|
75
|
+
|
|
76
|
+
Invariant: snapshot the build state before the trigger; a build whose agent_id differs from the baseline is attributed to our edit.
|
|
77
|
+
|
|
78
|
+
Applies to: standard, bugfix, chore.
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
// Pre-trigger build-state baseline (tiny, schema'd): one read of
|
|
82
|
+
// artifact_status. The post-trigger observation diffs against this
|
|
83
|
+
// baseline — a build whose agent_id was absent from (or differs
|
|
84
|
+
// from) the baseline is attributed to our edit; a build already in
|
|
85
|
+
// flight at baseline predates the trigger and is never attributed
|
|
86
|
+
// to it. If the baseline read itself fails, receipt attribution is
|
|
87
|
+
// skipped and the durable audit-dir evidence below is the only
|
|
88
|
+
// positive signal.
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
<a id="deterministic-artifact-publish"></a>
|
|
92
|
+
## Deterministic artifact publish
|
|
93
|
+
|
|
94
|
+
Invariant: the work agent never publishes; the parent runs the deterministic publish script and stamps the result.
|
|
95
|
+
|
|
96
|
+
Applies to: standard.
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
// Deterministic artifact publish (canary b5efd1b1, 2026-09-10): the work
|
|
100
|
+
// agent claimed "Rebuilt and deployed" while no build ran and no
|
|
101
|
+
// provenance was stamped — prose-trusted side effects, the same failure
|
|
102
|
+
// class as the npm double-skip (bb739316). The npm path already runs one
|
|
103
|
+
// deterministic script; the artifact path now has the same shape. Lock
|
|
104
|
+
// refresh, rebuild trigger, build-completion poll, and post-deploy are
|
|
105
|
+
// narrow schema'd bookkeeping calls owned by the workflow — the work
|
|
106
|
+
// agent reports on the mechanical outcome and cannot skip what it never
|
|
107
|
+
// owned. Any step failing parks with an honest, step-specific reason
|
|
108
|
+
// (fail-closed). There is deliberately NO workflow-side provenance
|
|
109
|
+
// stamp: the builder's applied-report is circular (canary run 8,
|
|
110
|
+
// 2026-09-11), so the stamp moved to the parent — after the build
|
|
111
|
+
// lands, the workflow records the session completed and parks with
|
|
112
|
+
// "publish: verification-requested". The parent owns verification
|
|
113
|
+
// (docs/publish-verification.md); the primary sensor is the
|
|
114
|
+
// deterministic lib/readback-disk.js (the agent-callable read-back
|
|
115
|
+
// tool is unavailable — artifact_inspect was removed by the platform
|
|
116
|
+
// 2026-09-14 — so the LLM-inspector path is manual-fallback only).
|
|
117
|
+
// QA's provenance check enforces the stamp mechanically.
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Applies to: bugfix.
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
// Deterministic artifact publish (canary b5efd1b1, 2026-09-10): the work
|
|
124
|
+
// agent claimed "Rebuilt and deployed" while no build ran and no
|
|
125
|
+
// provenance was stamped — prose-trusted side effects, the same failure
|
|
126
|
+
// class as the npm double-skip (bb739316). The npm path already runs one
|
|
127
|
+
// deterministic script; the artifact path now has the same shape. Lock
|
|
128
|
+
// refresh, rebuild trigger, build-completion poll, and post-deploy are
|
|
129
|
+
// narrow schema'd bookkeeping calls owned by the workflow — the work
|
|
130
|
+
// agent reports on the mechanical outcome and cannot skip what it never
|
|
131
|
+
// owned. Any step failing parks with an honest, step-specific reason
|
|
132
|
+
// (fail-closed). There is deliberately NO workflow-side provenance
|
|
133
|
+
// stamp: the builder's applied-report is circular (canary run 8,
|
|
134
|
+
// 2026-09-11), so the stamp moved to the parent — after the build
|
|
135
|
+
// lands, the workflow records the session completed and parks with
|
|
136
|
+
// "publish: verification-requested". The parent owns verification
|
|
137
|
+
// (docs/publish-verification.md); the primary sensor is the
|
|
138
|
+
// deterministic lib/readback-disk.js (the agent-callable read-back
|
|
139
|
+
// tool is unavailable — artifact_inspect was removed by the platform
|
|
140
|
+
// 2026-09-14 — so the LLM-inspector path is manual-fallback only).
|
|
141
|
+
// QA's provenance check enforces the stamp mechanically.
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Applies to: chore.
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
// Deterministic artifact publish (canary b5efd1b1, 2026-09-10): the work
|
|
148
|
+
// agent claimed "Rebuilt and deployed" while no build ran and no
|
|
149
|
+
// provenance was stamped — prose-trusted side effects, the same failure
|
|
150
|
+
// class as the npm double-skip (bb739316). The npm path already runs one
|
|
151
|
+
// deterministic script; the artifact path now has the same shape. Lock
|
|
152
|
+
// refresh, rebuild trigger, build-completion poll, and post-deploy are
|
|
153
|
+
// narrow schema'd bookkeeping calls owned by the workflow — the work
|
|
154
|
+
// agent reports on the mechanical outcome and cannot skip what it never
|
|
155
|
+
// owned. Any step failing parks with an honest, step-specific reason
|
|
156
|
+
// (fail-closed). There is deliberately NO workflow-side provenance
|
|
157
|
+
// stamp: the builder's applied-report is circular (canary run 8,
|
|
158
|
+
// 2026-09-11), so the stamp moved to the parent — after the build
|
|
159
|
+
// lands, the workflow records the session completed and parks with
|
|
160
|
+
// "publish: verification-requested". The parent owns verification
|
|
161
|
+
// (docs/publish-verification.md); the primary sensor is the
|
|
162
|
+
// deterministic lib/readback-disk.js (the agent-callable read-back
|
|
163
|
+
// tool is unavailable — artifact_inspect was removed by the platform
|
|
164
|
+
// 2026-09-14 — so the LLM-inspector path is manual-fallback only).
|
|
165
|
+
// Chore has no QA: the parent's verification is the final gate.
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
<a id="manual-inspector-fallback"></a>
|
|
170
|
+
## Manual inspector fallback
|
|
171
|
+
|
|
172
|
+
Invariant: the LLM-inspector path is manual-fallback only; the mechanical path is primary.
|
|
173
|
+
|
|
174
|
+
Applies to: standard.
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
// Deterministic artifact publish (canary b5efd1b1, 2026-09-10): the work
|
|
178
|
+
// agent claimed "Rebuilt and deployed" while no build ran and no
|
|
179
|
+
// provenance was stamped — prose-trusted side effects, the same failure
|
|
180
|
+
// class as the npm double-skip (bb739316). The npm path already runs one
|
|
181
|
+
// deterministic script; the artifact path now has the same shape. Lock
|
|
182
|
+
// refresh, rebuild trigger, build-completion poll, and post-deploy are
|
|
183
|
+
// narrow schema'd bookkeeping calls owned by the workflow — the work
|
|
184
|
+
// agent reports on the mechanical outcome and cannot skip what it never
|
|
185
|
+
// owned. Any step failing parks with an honest, step-specific reason
|
|
186
|
+
// (fail-closed). There is deliberately NO workflow-side provenance
|
|
187
|
+
// stamp: the builder's applied-report is circular (canary run 8,
|
|
188
|
+
// 2026-09-11), so the stamp moved to the parent — after the build
|
|
189
|
+
// lands, the workflow records the session completed and parks with
|
|
190
|
+
// "publish: verification-requested". The parent owns verification
|
|
191
|
+
// (docs/publish-verification.md); the primary sensor is the
|
|
192
|
+
// deterministic lib/readback-disk.js (the agent-callable read-back
|
|
193
|
+
// tool is unavailable — artifact_inspect was removed by the platform
|
|
194
|
+
// 2026-09-14 — so the LLM-inspector path is manual-fallback only).
|
|
195
|
+
// QA's provenance check enforces the stamp mechanically.
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Applies to: bugfix.
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
// Deterministic artifact publish (canary b5efd1b1, 2026-09-10): the work
|
|
202
|
+
// agent claimed "Rebuilt and deployed" while no build ran and no
|
|
203
|
+
// provenance was stamped — prose-trusted side effects, the same failure
|
|
204
|
+
// class as the npm double-skip (bb739316). The npm path already runs one
|
|
205
|
+
// deterministic script; the artifact path now has the same shape. Lock
|
|
206
|
+
// refresh, rebuild trigger, build-completion poll, and post-deploy are
|
|
207
|
+
// narrow schema'd bookkeeping calls owned by the workflow — the work
|
|
208
|
+
// agent reports on the mechanical outcome and cannot skip what it never
|
|
209
|
+
// owned. Any step failing parks with an honest, step-specific reason
|
|
210
|
+
// (fail-closed). There is deliberately NO workflow-side provenance
|
|
211
|
+
// stamp: the builder's applied-report is circular (canary run 8,
|
|
212
|
+
// 2026-09-11), so the stamp moved to the parent — after the build
|
|
213
|
+
// lands, the workflow records the session completed and parks with
|
|
214
|
+
// "publish: verification-requested". The parent owns verification
|
|
215
|
+
// (docs/publish-verification.md); the primary sensor is the
|
|
216
|
+
// deterministic lib/readback-disk.js (the agent-callable read-back
|
|
217
|
+
// tool is unavailable — artifact_inspect was removed by the platform
|
|
218
|
+
// 2026-09-14 — so the LLM-inspector path is manual-fallback only).
|
|
219
|
+
// QA's provenance check enforces the stamp mechanically.
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Applies to: chore.
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
// Deterministic artifact publish (canary b5efd1b1, 2026-09-10): the work
|
|
226
|
+
// agent claimed "Rebuilt and deployed" while no build ran and no
|
|
227
|
+
// provenance was stamped — prose-trusted side effects, the same failure
|
|
228
|
+
// class as the npm double-skip (bb739316). The npm path already runs one
|
|
229
|
+
// deterministic script; the artifact path now has the same shape. Lock
|
|
230
|
+
// refresh, rebuild trigger, build-completion poll, and post-deploy are
|
|
231
|
+
// narrow schema'd bookkeeping calls owned by the workflow — the work
|
|
232
|
+
// agent reports on the mechanical outcome and cannot skip what it never
|
|
233
|
+
// owned. Any step failing parks with an honest, step-specific reason
|
|
234
|
+
// (fail-closed). There is deliberately NO workflow-side provenance
|
|
235
|
+
// stamp: the builder's applied-report is circular (canary run 8,
|
|
236
|
+
// 2026-09-11), so the stamp moved to the parent — after the build
|
|
237
|
+
// lands, the workflow records the session completed and parks with
|
|
238
|
+
// "publish: verification-requested". The parent owns verification
|
|
239
|
+
// (docs/publish-verification.md); the primary sensor is the
|
|
240
|
+
// deterministic lib/readback-disk.js (the agent-callable read-back
|
|
241
|
+
// tool is unavailable — artifact_inspect was removed by the platform
|
|
242
|
+
// 2026-09-14 — so the LLM-inspector path is manual-fallback only).
|
|
243
|
+
// Chore has no QA: the parent's verification is the final gate.
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
<a id="step-1c-no-stamp"></a>
|
|
248
|
+
## STEP 1c: no provenance stamp
|
|
249
|
+
|
|
250
|
+
Invariant: no provenance stamp in STEP 1c; the stamp cannot certify content.
|
|
251
|
+
|
|
252
|
+
Applies to: standard.
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
// STEP 1c (mechanical): NO provenance stamp here. Canary run 8
|
|
256
|
+
// (2026-09-11) proved the stamp cannot certify content: the
|
|
257
|
+
// builder's applied-report was derived from the carried diff, so
|
|
258
|
+
// the old report check was circular — a fabricated report
|
|
259
|
+
// passed by construction, and every phase went green on a hollow
|
|
260
|
+
// build. The stamp moves to the parent (docs/publish-verification.md);
|
|
261
|
+
// the deterministic lib/readback-disk.js is the primary sensor
|
|
262
|
+
// (the agent-callable read-back tool is unavailable —
|
|
263
|
+
// artifact_inspect was removed by the platform 2026-09-14 — so
|
|
264
|
+
// the LLM-inspector path is manual-fallback only), and the task
|
|
265
|
+
// parks for parent verification.
|
|
266
|
+
// QA's provenance check enforces the stamp mechanically.
|
|
267
|
+
// An unverified publish fails loudly in QA instead of passing
|
|
268
|
+
// silently here.
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Applies to: bugfix.
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
// STEP 1c (mechanical): NO provenance stamp here. Canary run 8
|
|
275
|
+
// (2026-09-11) proved the stamp cannot certify content: the
|
|
276
|
+
// builder's applied-report was derived from the carried diff, so
|
|
277
|
+
// the old report check was circular — a fabricated report
|
|
278
|
+
// passed by construction, and every phase went green on a hollow
|
|
279
|
+
// build. The stamp moves to the parent (docs/publish-verification.md);
|
|
280
|
+
// the deterministic lib/readback-disk.js is the primary sensor
|
|
281
|
+
// (the agent-callable read-back tool is unavailable —
|
|
282
|
+
// artifact_inspect was removed by the platform 2026-09-14 — so
|
|
283
|
+
// the LLM-inspector path is manual-fallback only), and the task
|
|
284
|
+
// parks for parent verification.
|
|
285
|
+
// QA's provenance check enforces the stamp mechanically.
|
|
286
|
+
// An unverified publish fails loudly in QA instead of passing
|
|
287
|
+
// silently here.
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Applies to: chore.
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
// STEP 1c (mechanical): NO provenance stamp here. Canary run 8
|
|
294
|
+
// (2026-09-11) proved the stamp cannot certify content: the
|
|
295
|
+
// builder's applied-report was derived from the carried diff, so
|
|
296
|
+
// the old report check was circular — a fabricated report
|
|
297
|
+
// passed by construction, and every phase went green on a hollow
|
|
298
|
+
// build. The stamp moves to the parent (docs/publish-verification.md);
|
|
299
|
+
// the deterministic lib/readback-disk.js is the primary sensor
|
|
300
|
+
// (the agent-callable read-back tool is unavailable —
|
|
301
|
+
// artifact_inspect was removed by the platform 2026-09-14 — so
|
|
302
|
+
// the LLM-inspector path is manual-fallback only), and the task
|
|
303
|
+
// parks for parent verification.
|
|
304
|
+
// Chore has no QA: the parent's verification is the final gate.
|
|
305
|
+
// An unverified publish fails loudly in QA instead of passing
|
|
306
|
+
// silently here.
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
<a id="artifact-inspect-removed"></a>
|
|
311
|
+
## artifact_inspect removed by platform
|
|
312
|
+
|
|
313
|
+
Invariant: artifact_inspect was removed by the platform 2026-09-14; the parent verifies content directly.
|
|
314
|
+
|
|
315
|
+
Applies to: standard.
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
// STEP 1c (mechanical): NO provenance stamp here. Canary run 8
|
|
319
|
+
// (2026-09-11) proved the stamp cannot certify content: the
|
|
320
|
+
// builder's applied-report was derived from the carried diff, so
|
|
321
|
+
// the old report check was circular — a fabricated report
|
|
322
|
+
// passed by construction, and every phase went green on a hollow
|
|
323
|
+
// build. The stamp moves to the parent (docs/publish-verification.md);
|
|
324
|
+
// the deterministic lib/readback-disk.js is the primary sensor
|
|
325
|
+
// (the agent-callable read-back tool is unavailable —
|
|
326
|
+
// artifact_inspect was removed by the platform 2026-09-14 — so
|
|
327
|
+
// the LLM-inspector path is manual-fallback only), and the task
|
|
328
|
+
// parks for parent verification.
|
|
329
|
+
// QA's provenance check enforces the stamp mechanically.
|
|
330
|
+
// An unverified publish fails loudly in QA instead of passing
|
|
331
|
+
// silently here.
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Applies to: bugfix.
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
// STEP 1c (mechanical): NO provenance stamp here. Canary run 8
|
|
338
|
+
// (2026-09-11) proved the stamp cannot certify content: the
|
|
339
|
+
// builder's applied-report was derived from the carried diff, so
|
|
340
|
+
// the old report check was circular — a fabricated report
|
|
341
|
+
// passed by construction, and every phase went green on a hollow
|
|
342
|
+
// build. The stamp moves to the parent (docs/publish-verification.md);
|
|
343
|
+
// the deterministic lib/readback-disk.js is the primary sensor
|
|
344
|
+
// (the agent-callable read-back tool is unavailable —
|
|
345
|
+
// artifact_inspect was removed by the platform 2026-09-14 — so
|
|
346
|
+
// the LLM-inspector path is manual-fallback only), and the task
|
|
347
|
+
// parks for parent verification.
|
|
348
|
+
// QA's provenance check enforces the stamp mechanically.
|
|
349
|
+
// An unverified publish fails loudly in QA instead of passing
|
|
350
|
+
// silently here.
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Applies to: chore.
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
// STEP 1c (mechanical): NO provenance stamp here. Canary run 8
|
|
357
|
+
// (2026-09-11) proved the stamp cannot certify content: the
|
|
358
|
+
// builder's applied-report was derived from the carried diff, so
|
|
359
|
+
// the old report check was circular — a fabricated report
|
|
360
|
+
// passed by construction, and every phase went green on a hollow
|
|
361
|
+
// build. The stamp moves to the parent (docs/publish-verification.md);
|
|
362
|
+
// the deterministic lib/readback-disk.js is the primary sensor
|
|
363
|
+
// (the agent-callable read-back tool is unavailable —
|
|
364
|
+
// artifact_inspect was removed by the platform 2026-09-14 — so
|
|
365
|
+
// the LLM-inspector path is manual-fallback only), and the task
|
|
366
|
+
// parks for parent verification.
|
|
367
|
+
// Chore has no QA: the parent's verification is the final gate.
|
|
368
|
+
// An unverified publish fails loudly in QA instead of passing
|
|
369
|
+
// silently here.
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
<a id="audit-dir-fallback"></a>
|
|
374
|
+
## STEP 1b durable audit-dir fallback
|
|
375
|
+
|
|
376
|
+
Invariant: the audit-dir fallback uses the poll's own observations, not just its final verdict.
|
|
377
|
+
|
|
378
|
+
Applies to: standard, bugfix, chore.
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
// STEP 1b durable audit-dir fallback (2026-09-15, task aadeccc3):
|
|
382
|
+
// the poll above only observes IN-FLIGHT builds. A build that
|
|
383
|
+
// finished between the receipt capture and the poll's first check
|
|
384
|
+
// leaves no in-flight trace — but the platform's audit harness
|
|
385
|
+
// leaves a durable one (~/workspace/ts-spaces/<slug>/audits/
|
|
386
|
+
// <timestamp>-<id>/ per completed build). Diff the audit-dir
|
|
387
|
+
// listing against the pre-trigger snapshot: a timestamped dir
|
|
388
|
+
// that appeared during the attempt window is evidence a build
|
|
389
|
+
// completed. Attribution is by window, not by build identity:
|
|
390
|
+
// the poll's saw_stranger signal only catches stranger builds in
|
|
391
|
+
// flight AT a check — a stranger that finished entirely inside
|
|
392
|
+
// the window is indistinguishable, so any observed stranger
|
|
393
|
+
// blocks attribution and the outcome stays unknown. This never
|
|
394
|
+
// re-issues the edit and never stamps provenance — ok=true only
|
|
395
|
+
// routes to the parent's independent content read-back, which
|
|
396
|
+
// remains the real verification.
|
|
397
|
+
//
|
|
398
|
+
// The poll end-state is read from the poll's own observations,
|
|
399
|
+
// not from build_done alone: a build in flight at the last check
|
|
400
|
+
// means the budget was shorter than the latency (or the build is
|
|
401
|
+
// stuck) — NOT that no build ever started; nothing observed at
|
|
402
|
+
// any check is the never-started signal.
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
<a id="applied-report-gone"></a>
|
|
406
|
+
## The builder's applied report is gone
|
|
407
|
+
|
|
408
|
+
Invariant: applied_report stays "missing-report" on issued triggers; null on pre-trigger/unattributed parks.
|
|
409
|
+
|
|
410
|
+
Applies to: standard, bugfix, chore.
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
// The builder's applied report is gone (2026-09-16): it rode on the
|
|
414
|
+
// trigger's JSON closeout contract, which is removed below. The
|
|
415
|
+
// parent's independent read-back (docs/publish-verification.md) is
|
|
416
|
+
// the verification — this field stays "missing-report" on ledger
|
|
417
|
+
// lines for issued triggers; pre-trigger parks (toolcheck
|
|
418
|
+
// rejected/inconclusive) and unattributed-unknown parks write null
|
|
419
|
+
// (no trigger was observed, so there is nothing to report).
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
<a id="h2-immediate-no-receipt"></a>
|
|
423
|
+
## H2 verdict-first: immediate no-receipt fallback
|
|
424
|
+
|
|
425
|
+
Invariant: with durable audit evidence but no receipt agent_id, read the build report now and decide the verdict once via decidePublishVerdict — never poll blind on a null receipt.
|
|
426
|
+
|
|
427
|
+
Applies to: standard, bugfix, chore.
|
|
428
|
+
|
|
429
|
+
```
|
|
430
|
+
// (2026-09-18, H2 verdict-first) Durable audit evidence exists,
|
|
431
|
+
// but there is no receipt agent_id to chain the completion poll
|
|
432
|
+
// to — polling with a null receipt can only observe strangers
|
|
433
|
+
// (any running build differs from "null") or nothing, burning
|
|
434
|
+
// 10.5 minutes to park unknown. Read the build report now instead
|
|
435
|
+
// of polling, then decide the verdict ONCE via
|
|
436
|
+
// decidePublishVerdict: exactly one new dir with ok=true lands
|
|
437
|
+
// (attribution by window, not identity — never poll blind);
|
|
438
|
+
// ok=false is UNKNOWN with the failure evidence preserved in the
|
|
439
|
+
// ledger detail (the evidence is explicit, the attribution is
|
|
440
|
+
// not); unreadable / zero / ambiguous dirs are UNKNOWN.
|
|
441
|
+
// Verdict-first: landed bypasses the poll below, unknown falls
|
|
442
|
+
// through to post-deploy. STEP 2 runs on every path.
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
<a id="h2-verdict-dispatch"></a>
|
|
446
|
+
## H2 verdict-first: verdict dispatch
|
|
447
|
+
|
|
448
|
+
Invariant: the verdict is decided exactly once; landed bypasses the receipt poll; an explicit publishFailure is preserved verbatim through post-deploy; otherwise the poll requires a real receipt (null-safe assertion records UNKNOWN and continues to STEP 2).
|
|
449
|
+
|
|
450
|
+
Applies to: standard, bugfix, chore.
|
|
451
|
+
|
|
452
|
+
```
|
|
453
|
+
// Durable publish-attempt ledger: record the trigger outcome while the
|
|
454
|
+
// attempt key and commit are in scope. Every attempt lands here with
|
|
455
|
+
// its outcome — submitted, rejected, or unknown (unknown is recorded
|
|
456
|
+
// at the park site above). A later run or human matches commit hash +
|
|
457
|
+
// attempt key against the builder's eventual completion.
|
|
458
|
+
// (2026-09-16) The trigger is fire-and-forget: the observation above
|
|
459
|
+
// already recorded the ledger's submitted line on both positive paths
|
|
460
|
+
// and parked on unknown — there is no applied report to observe and
|
|
461
|
+
// no rejection signal to record.
|
|
462
|
+
// (2026-09-18, H2 verdict-first) The verdict was decided exactly
|
|
463
|
+
// once above; dispatch on it. landed bypasses the receipt poll
|
|
464
|
+
// (the audit evidence already proved completion); an explicit
|
|
465
|
+
// publishFailure is preserved verbatim through post-deploy.
|
|
466
|
+
// Otherwise the verdict is open — but the poll below is only
|
|
467
|
+
// legitimate against a real receipt: the null-safe assertion records
|
|
468
|
+
// UNKNOWN and continues to STEP 2 instead of polling blind.
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Root cause, fixed 2026-09-18: the immediate-landed path set
|
|
472
|
+
`skipReceiptPoll=true`, so the poll gate
|
|
473
|
+
`if (rebuildTrigger.edit_started && !skipReceiptPoll)` evaluated false —
|
|
474
|
+
and the code fell into an `else` the author had marked "Unreachable",
|
|
475
|
+
parking with 'not attributed' even though `publishBuildLanded` was already
|
|
476
|
+
true. The verdict the evidence had already set was overwritten by a branch
|
|
477
|
+
that claimed it couldn't happen. The fix decides the verdict exactly once
|
|
478
|
+
(above) and dispatches on it; the old "Unreachable" else is now a loud
|
|
479
|
+
defensive assertion that records UNKNOWN and continues to STEP 2 — it never
|
|
480
|
+
parks early and never polls blind.
|
|
481
|
+
|
|
482
|
+
<a id="diff-transport"></a>
|
|
483
|
+
## Publish diff transport (file-backed)
|
|
484
|
+
|
|
485
|
+
Invariant: the diff travels git -> file -> deterministic script summary; the LLM never carries diff bytes; the compute script is pinned.
|
|
486
|
+
|
|
487
|
+
Applies to: standard, bugfix, chore.
|
|
488
|
+
|
|
489
|
+
```
|
|
490
|
+
// Publish diff transport (room #14, 2026-09-17): the diff used to be
|
|
491
|
+
// ferried as a JSON string field in the agent's response — the agent
|
|
492
|
+
// produced a valid 700-line diff on disk but the JSON ferry dropped
|
|
493
|
+
// it, and the parse saw zero files ("Publish diff parsed to zero
|
|
494
|
+
// files"). The diff now travels git -> file -> deterministic script
|
|
495
|
+
// summary; the LLM never carries diff bytes. The agent is pure hands:
|
|
496
|
+
// it runs exactly one command (the PINNED compute-publish-diff.js —
|
|
497
|
+
// a mid-run release swap cannot change it under the workflow) and
|
|
498
|
+
// returns the small JSON summary verbatim. All fail-closed parks
|
|
499
|
+
// below are unchanged in meaning.
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
<a id="chunk-containment"></a>
|
|
503
|
+
## Poll-chunk exception containment
|
|
504
|
+
|
|
505
|
+
Invariant: a hung or failed chunk is inconclusive, never terminal — record it and continue; fail-closed still applies after chunk 3 and the fallback are exhausted.
|
|
506
|
+
|
|
507
|
+
Applies to: standard, bugfix, chore.
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
// A hung or failed chunk is inconclusive, never terminal:
|
|
511
|
+
// record it and continue to the next chunk. (2026-09-16,
|
|
512
|
+
// clean-room task 1febe8eb: the platform's 270s agent
|
|
513
|
+
// timeout killed chunk 2, which threw out of this loop —
|
|
514
|
+
// skipping chunk 3 AND the STEP 1b audit-dir fallback and
|
|
515
|
+
// parking on the exception path.) Fail-closed still applies
|
|
516
|
+
// after chunk 3 and the fallback are exhausted.
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
<a id="poll-accumulators"></a>
|
|
520
|
+
## STEP 1b poll-signal accumulators
|
|
521
|
+
|
|
522
|
+
Invariant: the poll's observations (saw-our-build, saw-stranger, last check) are OR-ed across all three chunks so a signal seen in any chunk survives the chunk boundary.
|
|
523
|
+
|
|
524
|
+
Applies to: standard, bugfix, chore.
|
|
525
|
+
|
|
526
|
+
```
|
|
527
|
+
// STEP 1b poll-signal accumulators (2026-09-15, task aadeccc3):
|
|
528
|
+
// the durable audit-dir fallback below needs the poll's own
|
|
529
|
+
// observations, not just its final verdict — whether our build was
|
|
530
|
+
// ever seen, whether a stranger's build was ever in flight, and
|
|
531
|
+
// what the last check observed. OR-ed across all three chunks so
|
|
532
|
+
// a signal seen in any chunk survives the chunk boundary.
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
<a id="no-evidence-unknown"></a>
|
|
536
|
+
## No-evidence unknown (H2 verdict-first)
|
|
537
|
+
|
|
538
|
+
Invariant: no attributable build and no durable evidence proves nothing — verdict UNKNOWN via decidePublishVerdict, recorded in the ledger, fall through to post-deploy; never poll blind on a null receipt.
|
|
539
|
+
|
|
540
|
+
Applies to: standard, bugfix, chore.
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
// No attributable build and no durable evidence — but that proves
|
|
544
|
+
// nothing (a fast-completing build can finish between polls, or
|
|
545
|
+
// the checks themselves failed). Verdict UNKNOWN via the same
|
|
546
|
+
// mapping: record it in the ledger and fall through to
|
|
547
|
+
// post-deploy. No retry: re-issuing the edit here duplicated it
|
|
548
|
+
// on 2026-09-12. Never poll blind on a null receipt.
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
<a id="explicit-refusal"></a>
|
|
552
|
+
## Explicit artifact refusal
|
|
553
|
+
|
|
554
|
+
Invariant: ARTIFACT_EDIT_REFUSED is conclusive negative evidence -> park rejected, skip polling; a missing/unparseable signal is NOT a refusal (stays unknown fail-closed).
|
|
555
|
+
|
|
556
|
+
Applies to: standard, bugfix, chore.
|
|
557
|
+
|
|
558
|
+
```
|
|
559
|
+
// Explicit refusal (room #16 blocker 10): the child ends its turn
|
|
560
|
+
// with ARTIFACT_EDIT_REFUSED when artifact_edit explicitly refused.
|
|
561
|
+
// Conclusive negative evidence — the edit provably did NOT go
|
|
562
|
+
// through — so this parks rejected and skips observation polling.
|
|
563
|
+
// A missing/unparseable signal is NOT a refusal: it stays unknown
|
|
564
|
+
// and fail-closed below.
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
<a id="refusal-signal"></a>
|
|
568
|
+
## Explicit artifact refusal (signal format)
|
|
569
|
+
|
|
570
|
+
Invariant: a refusal is conclusive negative evidence; the signal must be the ENTIRE trimmed turn output — a child quoting instructions back in prose degrades to unknown fail-closed.
|
|
571
|
+
|
|
572
|
+
Applies to: standard, bugfix, chore.
|
|
573
|
+
|
|
574
|
+
```
|
|
575
|
+
// Explicit artifact refusal (room #16 blocker 10, 2026-09-18): the rebuild
|
|
576
|
+
// trigger child ends its turn with `ARTIFACT_EDIT_REFUSED: <text>` when
|
|
577
|
+
// artifact_edit explicitly refuses the edit (e.g. the artifact does not
|
|
578
|
+
// exist). A refusal is conclusive negative evidence — the edit provably did
|
|
579
|
+
// NOT go through — distinct from an unconsumed trigger return (unknown).
|
|
580
|
+
// Pure — pinned byte-identical across standard/bugfix/chore.
|
|
581
|
+
// The signal must be the ENTIRE trimmed turn output (not a line within prose):
|
|
582
|
+
// the trigger child is instructed to end its turn with exactly this line and
|
|
583
|
+
// nothing else. A confused child quoting the instructions back in prose must
|
|
584
|
+
// NOT produce a conclusive negative — that degrades to unknown (fail-closed).
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
<a id="provenance-refresh"></a>
|
|
588
|
+
## Provenance refresh for self-publishes
|
|
589
|
+
|
|
590
|
+
Invariant: after a verified landed publish, refresh the provenance record's crew_release to the now-live release identity, preserving the existing source_commit.
|
|
591
|
+
|
|
592
|
+
Applies to: standard, bugfix, chore.
|
|
593
|
+
|
|
594
|
+
```
|
|
595
|
+
// Provenance refresh for self-publishes (task 7946d2a4): the npm path
|
|
596
|
+
// installs and activates a new immutable release (crew-release.sh deploy
|
|
597
|
+
// swaps the `current` symlink inside publish-npm.sh) but never stamped
|
|
598
|
+
// the dashboard's provenance record — every crew release left
|
|
599
|
+
// crew_release pointing at a pruned release. After a verified landed
|
|
600
|
+
// publish, refresh the record's crew_release to the now-live release
|
|
601
|
+
// identity, preserving the existing source_commit (the dashboard
|
|
602
|
+
// artifact's build source — a crew-repo commit here would fail the
|
|
603
|
+
// dashboard QA source check).
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
<a id="parent-owned-verification"></a>
|
|
607
|
+
## Publish content verification — parent-owned
|
|
608
|
+
|
|
609
|
+
Invariant: the parent owns content verification (docs/publish-verification.md); a skipped or failed publish has nothing to verify.
|
|
610
|
+
|
|
611
|
+
Applies to: standard.
|
|
612
|
+
|
|
613
|
+
```
|
|
614
|
+
// Publish content verification — parent-owned (docs/publish-verification.md).
|
|
615
|
+
// The old block read back the workflow's OWN provenance stamp and compared
|
|
616
|
+
// it to HEAD: that verifies the stamp, not the content. Canary run 8
|
|
617
|
+
// (2026-09-11) passed it with a hollow build — the stamp was honest, the
|
|
618
|
+
// artifact was stale, all eight phases green. The stamp now moves to the
|
|
619
|
+
// parent (docs/publish-verification.md); the independent read-back step
|
|
620
|
+
// is currently unavailable (no agent-callable read-back tool exists —
|
|
621
|
+
// artifact_inspect was removed by the platform 2026-09-14), so the parent
|
|
622
|
+
// cannot confirm content and the task parks for verification. QA's
|
|
623
|
+
// provenance check enforces the stamp — an unverified publish fails loudly
|
|
624
|
+
// there instead of passing silently here.
|
|
625
|
+
// Skip-aware (park 2026-09-11): an empty-diff Integrate takes no merge
|
|
626
|
+
// lock, and the deterministic publish path skips rebuild/stamp entirely —
|
|
627
|
+
// there is no new content to verify, so verification is vacuous.
|
|
628
|
+
// publishSkippedNoLock is workflow-computed state from the explicit
|
|
629
|
+
// lock-status read in STEP 0, not agent prose.
|
|
630
|
+
// The parent (tick worker) triggers the ONE read-back inspection it can
|
|
631
|
+
// actually receive (async results go to the root agent, never into a
|
|
632
|
+
// workflow run — a workflow-side trigger would be an orphan). The workflow
|
|
633
|
+
// only parks; the parent's scan builds the request deterministically via
|
|
634
|
+
// lib/build-readback-request.js and ferries the inspection.
|
|
635
|
+
// publishBuildLanded and publishSkippedNoLock are workflow-computed state;
|
|
636
|
+
// a skipped or failed publish has nothing to verify.
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
Applies to: bugfix.
|
|
640
|
+
|
|
641
|
+
```
|
|
642
|
+
// Publish content verification — parent-owned (docs/publish-verification.md).
|
|
643
|
+
// The old block read back the workflow's OWN provenance stamp and compared
|
|
644
|
+
// it to HEAD: that verifies the stamp, not the content. Canary run 8
|
|
645
|
+
// (2026-09-11) passed it with a hollow build — the stamp was honest, the
|
|
646
|
+
// artifact was stale, all eight phases green. The stamp now moves to the
|
|
647
|
+
// parent (docs/publish-verification.md); the independent read-back step
|
|
648
|
+
// is currently unavailable (no agent-callable read-back tool exists —
|
|
649
|
+
// artifact_inspect was removed by the platform 2026-09-14), so the parent
|
|
650
|
+
// cannot confirm content and the task parks for verification. QA's
|
|
651
|
+
// provenance check enforces the stamp — an unverified publish fails loudly
|
|
652
|
+
// there instead of passing silently here.
|
|
653
|
+
// Skip-aware (park 2026-09-11): an empty-diff Integrate takes no merge
|
|
654
|
+
// lock, and the deterministic publish path skips rebuild/stamp entirely —
|
|
655
|
+
// there is no new content to verify, so verification is vacuous.
|
|
656
|
+
// publishSkippedNoLock is workflow-computed state from the explicit
|
|
657
|
+
// lock-status read in STEP 0, not agent prose.
|
|
658
|
+
// The parent (tick worker) triggers the ONE read-back inspection it can
|
|
659
|
+
// actually receive (async results go to the root agent, never into a
|
|
660
|
+
// workflow run — a workflow-side trigger would be an orphan). The workflow
|
|
661
|
+
// only parks; the parent's scan builds the request deterministically via
|
|
662
|
+
// lib/build-readback-request.js and ferries the inspection.
|
|
663
|
+
// publishBuildLanded and publishSkippedNoLock are workflow-computed state;
|
|
664
|
+
// a skipped or failed publish has nothing to verify.
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Applies to: chore.
|
|
668
|
+
|
|
669
|
+
```
|
|
670
|
+
// Publish content verification — parent-owned (docs/publish-verification.md).
|
|
671
|
+
// The old block read back the workflow's OWN provenance stamp and compared
|
|
672
|
+
// it to HEAD: that verifies the stamp, not the content. Canary run 8
|
|
673
|
+
// (2026-09-11) passed it with a hollow build — the stamp was honest, the
|
|
674
|
+
// artifact was stale, all eight phases green. The stamp now moves to the
|
|
675
|
+
// parent (docs/publish-verification.md); the independent read-back step
|
|
676
|
+
// is currently unavailable (no agent-callable read-back tool exists —
|
|
677
|
+
// artifact_inspect was removed by the platform 2026-09-14), so the parent
|
|
678
|
+
// cannot confirm content and the task parks for verification.
|
|
679
|
+
// Skip-aware (park 2026-09-11): an empty-diff Integrate takes no merge
|
|
680
|
+
// lock, and the deterministic publish path skips rebuild/stamp entirely —
|
|
681
|
+
// there is no new content to verify, so verification is vacuous.
|
|
682
|
+
// publishSkippedNoLock is workflow-computed state from the explicit
|
|
683
|
+
// lock-status read in STEP 0, not agent prose.
|
|
684
|
+
// The parent (tick worker) triggers the ONE read-back inspection it can
|
|
685
|
+
// actually receive (async results go to the root agent, never into a
|
|
686
|
+
// workflow run — a workflow-side trigger would be an orphan). The workflow
|
|
687
|
+
// only parks; the parent's scan builds the request deterministically via
|
|
688
|
+
// lib/build-readback-request.js and ferries the inspection.
|
|
689
|
+
// publishBuildLanded and publishSkippedNoLock are workflow-computed state;
|
|
690
|
+
// a skipped or failed publish has nothing to verify.
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
<a id="already-merged-corrective"></a>
|
|
695
|
+
## Already-merged corrective
|
|
696
|
+
|
|
697
|
+
Invariant: when Review rejects an empty branch but the work is already on main (workflow-verified sha), Wren must declare it — not re-implement or re-commit already-landed work. Scoped to the empty-branch rejection.
|
|
698
|
+
|
|
699
|
+
Applies to: standard, bugfix, chore.
|
|
700
|
+
|
|
701
|
+
```
|
|
702
|
+
// Already-merged corrective (room #16 blocker 11): when Review rejected
|
|
703
|
+
// an empty branch but the work is already on main (the workflow verified
|
|
704
|
+
// the sha), Wren must declare it — not re-implement or re-commit
|
|
705
|
+
// already-landed work. Scoped to the empty-branch rejection; any other
|
|
706
|
+
// rejection already carries its own specific notes.
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
<a id="merge-record-helpers"></a>
|
|
710
|
+
## Merge-record helpers
|
|
711
|
+
|
|
712
|
+
Invariant: the integrate step records the exact merged commit in $CREW_HOME/.merge-records/<task_id> so a re-dispatched run can verify a landed merge and publish it even after the branch was reclaimed under an expired lease. All pure — no I/O, no clock.
|
|
713
|
+
|
|
714
|
+
Applies to: standard, bugfix, chore.
|
|
715
|
+
|
|
716
|
+
```
|
|
717
|
+
// Merge-record helpers (merge-lease fix, 2026-09-12): the integrate step
|
|
718
|
+
// records the exact merged commit in $CREW_HOME/.merge-records/<task_id>,
|
|
719
|
+
// so a re-dispatched run can verify a landed merge and publish it even
|
|
720
|
+
// after the branch was reclaimed under an expired lease. All pure — no I/O,
|
|
721
|
+
// no clock. Byte-identical in standard.js, bugfix.js, chore.js.
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
<a id="publish-diff-base"></a>
|
|
725
|
+
## Publish diff base (BASE..HEAD)
|
|
726
|
+
|
|
727
|
+
Invariant: the carried diff is BASE..HEAD where BASE is the previously-stamped provenance source_commit — NOT HEAD^1; a push-time reconcile merge puts the task's own changes behind an intermediate merge, so HEAD^1..HEAD silently drops the fix. Empty tree only for a genuine first publish.
|
|
728
|
+
|
|
729
|
+
Applies to: standard, bugfix, chore.
|
|
730
|
+
|
|
731
|
+
```
|
|
732
|
+
// Publish diff base (2026-09-14, task 0c53af4e): the carried diff is
|
|
733
|
+
// BASE..HEAD where BASE is the previously-stamped provenance
|
|
734
|
+
// source_commit — NOT HEAD^1. A push-time reconcile merge puts the
|
|
735
|
+
// task's own changes behind an intermediate merge, so HEAD^1..HEAD
|
|
736
|
+
// silently drops the task's fix while the artifact builds without
|
|
737
|
+
// it. The stamped base is the artifact's actual content; BASE..HEAD
|
|
738
|
+
// is the complete unpublished delta. Empty tree only for a genuine
|
|
739
|
+
// first publish (no provenance stamped yet).
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
<a id="attribution-limitation"></a>
|
|
743
|
+
## Attribution timing limitation
|
|
744
|
+
|
|
745
|
+
Invariant: attribution is timing-based — a stranger's build starting inside the trigger window is indistinguishable and would be misattributed; the consequence is bounded because the parent's mechanical content read-back certifies the exact commit's content. Timing narrows the candidate; content decides.
|
|
746
|
+
|
|
747
|
+
Applies to: standard, bugfix, chore.
|
|
748
|
+
|
|
749
|
+
```
|
|
750
|
+
// Known limitation (failure-mode audit 2026-09-16): attribution
|
|
751
|
+
// is timing-based — any agent_id new relative to the baseline is
|
|
752
|
+
// treated as this edit's receipt. A stranger's build starting inside
|
|
753
|
+
// the trigger window is indistinguishable by timing and would be
|
|
754
|
+
// misattributed here. The consequence is bounded: the completion
|
|
755
|
+
// poll below tracks the recorded id, and the parent's mechanical
|
|
756
|
+
// content read-back (docs/publish-verification.md) certifies the
|
|
757
|
+
// exact commit's content — a wrong build's content fails closed as
|
|
758
|
+
// verification-failed, never stamped. Timing narrows the candidate;
|
|
759
|
+
// content decides.
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
<a id="read-back-request"></a>
|
|
763
|
+
## Publish read-back request (currently unavailable)
|
|
764
|
+
|
|
765
|
+
Invariant: no agent-callable read-back tool exists; verification parks at 'verification-requested'; independent read-back cannot be fabricated from the diff.
|
|
766
|
+
|
|
767
|
+
Applies to: standard.
|
|
768
|
+
|
|
769
|
+
```
|
|
770
|
+
// Publish read-back request (agent path currently unavailable): the
|
|
771
|
+
// verbatim_request the parent protocol (docs/publish-verification.md) would
|
|
772
|
+
// hand to an independent read-back tool after the artifact build lands.
|
|
773
|
+
// artifact_inspect was removed by the platform (2026-09-14);
|
|
774
|
+
// artifact.inspect is malfunction diagnosis, not a substitute — so no
|
|
775
|
+
// agent-callable read-back tool exists and this LLM-inspector request
|
|
776
|
+
// cannot currently be issued. The primary sensor is now the deterministic
|
|
777
|
+
// lib/readback-disk.js (reads the on-disk tree the artifact is served
|
|
778
|
+
// from); this request builder is retained only as the manual fallback.
|
|
779
|
+
// Pure function — no I/O, no clock. The request carries the merged diff as the expected change and asks
|
|
780
|
+
// for an independent read of the artifact's actual source: for each file, the
|
|
781
|
+
// exact current text of the changed regions plus a per-line present/absent
|
|
782
|
+
// finding. Until a read-back path exists, the parent cannot independently
|
|
783
|
+
// confirm content and verification parks at "publish: verification-requested"
|
|
784
|
+
// (see docs/publish-verification.md). This preserves the circularity break
|
|
785
|
+
// that hollowed canary run 8 (2026-09-11): the old verifyAppliedChanges
|
|
786
|
+
// compared the builder's applied-report against the diff the report was
|
|
787
|
+
// derived from — a fabricated report passed by construction. The report
|
|
788
|
+
// itself is gone now (2026-09-16 fire-and-forget trigger). Independent
|
|
789
|
+
// read-back cannot be
|
|
790
|
+
// fabricated from the diff;
|
|
791
|
+
// it must match the artifact's real content.
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
Applies to: bugfix.
|
|
795
|
+
|
|
796
|
+
```
|
|
797
|
+
// Publish read-back request (currently unavailable): the verbatim_request
|
|
798
|
+
// the parent protocol (docs/publish-verification.md) would hand to an
|
|
799
|
+
// independent read-back tool after the artifact build lands. artifact_inspect
|
|
800
|
+
// was removed by the platform (2026-09-14); artifact.inspect is malfunction
|
|
801
|
+
// diagnosis, not a substitute — so no agent-callable read-back tool exists
|
|
802
|
+
// and this request cannot currently be issued. Pure function — no I/O, no
|
|
803
|
+
// clock. The request carries the merged diff as the expected change and asks
|
|
804
|
+
// for an independent read of the artifact's actual source: for each file, the
|
|
805
|
+
// exact current text of the changed regions plus a per-line present/absent
|
|
806
|
+
// finding. Until a read-back path exists, the parent cannot independently
|
|
807
|
+
// confirm content and verification parks at "publish: verification-requested"
|
|
808
|
+
// (see docs/publish-verification.md). This preserves the circularity break
|
|
809
|
+
// that hollowed canary run 8 (2026-09-11): the old verifyAppliedChanges
|
|
810
|
+
// compared the builder's applied-report against the diff the report was
|
|
811
|
+
// derived from — a fabricated report passed by construction. The report
|
|
812
|
+
// itself is gone now (2026-09-16 fire-and-forget trigger). Independent
|
|
813
|
+
// read-back cannot be
|
|
814
|
+
// fabricated from the diff; it must match the artifact's real content.
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
Applies to: chore.
|
|
818
|
+
|
|
819
|
+
```
|
|
820
|
+
// Publish read-back request (currently unavailable): the verbatim_request
|
|
821
|
+
// the parent protocol (docs/publish-verification.md) would hand to an
|
|
822
|
+
// independent read-back tool after the artifact build lands. artifact_inspect
|
|
823
|
+
// was removed by the platform (2026-09-14); artifact.inspect is malfunction
|
|
824
|
+
// diagnosis, not a substitute — so no agent-callable read-back tool exists
|
|
825
|
+
// and this request cannot currently be issued. Pure function — no I/O, no
|
|
826
|
+
// clock. The request carries the merged diff as the expected change and asks
|
|
827
|
+
// for an independent read of the artifact's actual source: for each file, the
|
|
828
|
+
// exact current text of the changed regions plus a per-line present/absent
|
|
829
|
+
// finding. Until a read-back path exists, the parent cannot independently
|
|
830
|
+
// confirm content and verification parks at "publish: verification-requested"
|
|
831
|
+
// (see docs/publish-verification.md). This preserves the circularity break
|
|
832
|
+
// that hollowed canary run 8 (2026-09-11): the old verifyAppliedChanges
|
|
833
|
+
// compared the builder's applied-report against the diff the report was
|
|
834
|
+
// derived from — a fabricated report passed by construction. The report
|
|
835
|
+
// itself is gone now (2026-09-16 fire-and-forget trigger). Independent
|
|
836
|
+
// read-back cannot be
|
|
837
|
+
// fabricated from the diff; it must match the artifact's real content.
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
<a id="step-05-target-check"></a>
|
|
842
|
+
## STEP 0.5: artifact target existence check
|
|
843
|
+
|
|
844
|
+
Invariant: assert the artifact target exists before any trigger; a missing target is conclusive negative evidence -> park rejected; an inconclusive check is fail-closed unknown.
|
|
845
|
+
|
|
846
|
+
Applies to: standard, bugfix, chore.
|
|
847
|
+
|
|
848
|
+
```
|
|
849
|
+
// STEP 0.5 (mechanical, room #16 blocker 10): assert the artifact
|
|
850
|
+
// target exists before any artifact_status / artifact_edit call. The
|
|
851
|
+
// project was classified as an artifact surface (deploy_slug set),
|
|
852
|
+
// but setup never provisioned the artifact — Publish then entered
|
|
853
|
+
// the trigger path against a slug with no on-disk target and the
|
|
854
|
+
// edit failed opaquely ("web artifact <slug> was not found on
|
|
855
|
+
// disk"), which the ledger could only record as unknown. A missing
|
|
856
|
+
// target is conclusive negative evidence: the edit provably did NOT
|
|
857
|
+
// go through, so this parks rejected (not unknown) with the actual
|
|
858
|
+
// missing path — no trigger issued, no blind retry, no observation
|
|
859
|
+
// polling. The check is a pure filesystem stat; the path is
|
|
860
|
+
// workflow-computed, never agent prose. An inconclusive check
|
|
861
|
+
// (throw / unparseable signal) is fail-closed unknown: without
|
|
862
|
+
// proof the target exists, no edit is issued. The slug is
|
|
863
|
+
// interpolated into a shell command — a slug outside [a-zA-Z0-9_-]
|
|
864
|
+
// (e.g. from a hand-edited space.json) is treated as inconclusive
|
|
865
|
+
// rather than risking shell injection.
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
<a id="no-builder-report"></a>
|
|
869
|
+
## No builder report (fire-and-forget)
|
|
870
|
+
|
|
871
|
+
Invariant: the fire-and-forget trigger carries no JSON contract; the builder's old self-report was circular by construction with a demonstrated false-negative mode.
|
|
872
|
+
|
|
873
|
+
Applies to: standard, bugfix, chore.
|
|
874
|
+
|
|
875
|
+
```
|
|
876
|
+
// (2026-09-16) There is no builder report: the fire-and-forget
|
|
877
|
+
// trigger carries no JSON contract, so there is nothing to
|
|
878
|
+
// compare and no pre-hash diagnostic. The builder's old
|
|
879
|
+
// self-report was circular by construction (canary run 8) with a
|
|
880
|
+
// demonstrated false-negative mode (task 23ca8f3f, 2026-09-12:
|
|
881
|
+
// applied:[] for a diff the builder had applied). The flow
|
|
882
|
+
// proceeds to the build poll regardless; real verification is the
|
|
883
|
+
// parent's independent read-back (docs/publish-verification.md)
|
|
884
|
+
// before the provenance stamp.
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
<a id="durable-evidence-snapshot"></a>
|
|
888
|
+
## Durable-evidence snapshot
|
|
889
|
+
|
|
890
|
+
Invariant: snapshot the audit-dir listing BEFORE the trigger; the fallback diffs before/after; if the snapshot fails, both fallback comparisons are disabled.
|
|
891
|
+
|
|
892
|
+
Applies to: standard, bugfix, chore.
|
|
893
|
+
|
|
894
|
+
```
|
|
895
|
+
// Durable-evidence snapshot (2026-09-14): the observation below only
|
|
896
|
+
// detects IN-FLIGHT builds. A build that finished before the
|
|
897
|
+
// observation leaves no in-flight trace — but the platform's audit
|
|
898
|
+
// harness leaves a durable one:
|
|
899
|
+
// ~/workspace/ts-spaces/<slug>/audits/<timestamp>-<id>/ per
|
|
900
|
+
// completed build. Snapshot the listing BEFORE the trigger so the
|
|
901
|
+
// fallback can diff before/after: a directory appearing during the
|
|
902
|
+
// trigger window is positive evidence the edit went through and
|
|
903
|
+
// the build completed. Best-effort and non-gating: if the snapshot
|
|
904
|
+
// fails, auditBeforeOk stays false and BOTH fallback comparisons
|
|
905
|
+
// are disabled (2026-09-16, critic finding 4) — without a baseline,
|
|
906
|
+
// an empty before-list would make every historical audit dir look
|
|
907
|
+
// "new". No wall-clock in-script (deterministic replay) — the
|
|
908
|
+
// comparison is a pure before/after set diff.
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
<a id="already-merged-hydration"></a>
|
|
912
|
+
## Already-merged hydration
|
|
913
|
+
|
|
914
|
+
Invariant: when the run did not execute Build itself, recover the workflow-verified sha; the structured session field is read FIRST, the notes line is only a fallback (session notes are hard-capped at 3000 chars).
|
|
915
|
+
|
|
916
|
+
Applies to: standard, bugfix, chore.
|
|
917
|
+
|
|
918
|
+
```
|
|
919
|
+
// Already-merged hydration: when this run did not execute Build itself
|
|
920
|
+
// (dispatcher resume at Review after a platform death between phases),
|
|
921
|
+
// recover the workflow-verified sha. Room #16 blocker 11: the structured
|
|
922
|
+
// session field is read FIRST — the `already_merged_verified:` notes line
|
|
923
|
+
// is only a fallback, because session notes are hard-capped at 3000
|
|
924
|
+
// chars and a truthful declaration at the report's tail was silently
|
|
925
|
+
// truncated. The structured value was written by the workflow after a
|
|
926
|
+
// mechanical ancestor check — it is trusted; the builder's bare
|
|
927
|
+
// declaration never is. Absent both, the mechanical fact below reads
|
|
928
|
+
// "none declared" and Cass fails closed. The hydration read is best-effort:
|
|
929
|
+
// a transport throw degrades to "none declared" rather than crashing Review.
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
<a id="already-merged-declaration"></a>
|
|
933
|
+
## Already-merged declaration
|
|
934
|
+
|
|
935
|
+
Invariant: when the builder correctly makes no commit because the deliverable is already on main, it declares repo_diff: none (already-merged: <sha>) naming the main commit that carries the work.
|
|
936
|
+
|
|
937
|
+
Applies to: standard, bugfix, chore.
|
|
938
|
+
|
|
939
|
+
```
|
|
940
|
+
// Already-merged idempotency (canary 2026-09-15, task 1d692d91): when the
|
|
941
|
+
// builder correctly makes no commit because the deliverable is already on
|
|
942
|
+
// main (a prior merge or hand-repair landed it), it declares
|
|
943
|
+
// `repo_diff: none (already-merged: <sha>)` naming the main commit that
|
|
944
|
+
// carries the work. Room #16 blocker 11 (2026-09-18): the line anchor
|
|
945
|
+
// missed Wren's mid-paragraph declaration, and the persisted notes truncated
|
|
946
|
+
// the tail — so the anchor is gone and a sha followed by `)`, whitespace, or
|
|
947
|
+
// end-of-string (truncation) is accepted. The sha is hex-only (7-40 chars)
|
|
948
|
+
// so the workflow can interpolate it into the mechanical ancestor check
|
|
949
|
+
// without injection risk; a over-long hex run never matches (the lookahead
|
|
950
|
+
// fails on the extra hex char). Pure — pinned byte-identical across
|
|
951
|
+
// standard/bugfix/chore.
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
<a id="step1-builder-source"></a>
|
|
955
|
+
## STEP 1: builder source tree
|
|
956
|
+
|
|
957
|
+
Invariant: the builder's source tree is NOT the crew's repo; the merge diff is embedded in the edit request; the workflow verifies the report matches the diff BEFORE stamping provenance.
|
|
958
|
+
|
|
959
|
+
Applies to: standard, bugfix, chore.
|
|
960
|
+
|
|
961
|
+
```
|
|
962
|
+
// STEP 1 (mechanical): carry the merged change to the artifact
|
|
963
|
+
// builder. The builder's source tree is NOT the crew's repo —
|
|
964
|
+
// canary run 4 (2026-09-11) proved it: Publish asked for "rebuild
|
|
965
|
+
// from current source. Do not modify any source files" and the
|
|
966
|
+
// builder rebuilt a stale copy predating the canary's changes, then
|
|
967
|
+
// the workflow stamped the new commit hash on the stale build.
|
|
968
|
+
// Provenance fiction; all eight phases passed. The merge diff is
|
|
969
|
+
// embedded in the edit request; the builder applies it to its own
|
|
970
|
+
// tree and reports the applied changes; the workflow verifies the
|
|
971
|
+
// report matches the diff BEFORE stamping provenance. A mismatch
|
|
972
|
+
// parks without stamping — the stamp must never certify a build
|
|
973
|
+
// whose content was not verified.
|
|
974
|
+
// Skipped entirely when no lock was held — nothing merged, nothing
|
|
975
|
+
// to ship.
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
<a id="verification-park"></a>
|
|
979
|
+
## Verification park
|
|
980
|
+
|
|
981
|
+
Invariant: the build landed but provenance is unstamped until parent verification.
|
|
982
|
+
|
|
983
|
+
Applies to: standard, bugfix, chore.
|
|
984
|
+
|
|
985
|
+
```
|
|
986
|
+
// Publish verification park: the build landed and post-deploy finalized,
|
|
987
|
+
// but provenance is UNSTAMPED until the parent's independent read-back
|
|
988
|
+
// (docs/publish-verification.md) confirms the artifact's actual content
|
|
989
|
+
// matches the merged diff. The parent stamps provenance, then re-queues;
|
|
990
|
+
// the dispatcher resumes at QA, whose provenance check enforces the stamp
|
|
991
|
+
// mechanically. A failed Publish never reaches this park — it returned
|
|
992
|
+
// failed above and retries under the dispatcher's cap. The merge lock is
|
|
993
|
+
// already released (post-deploy), so the parked task holds no resources.
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
<a id="pretrigger-baseline"></a>
|
|
997
|
+
## Pre-trigger baseline
|
|
998
|
+
|
|
999
|
+
Invariant: a tiny schema'd baseline is captured before the trigger.
|
|
1000
|
+
|
|
1001
|
+
Applies to: standard, bugfix, chore.
|
|
1002
|
+
|
|
1003
|
+
```
|
|
1004
|
+
// Pre-trigger build-state baseline (tiny, schema'd): one read of
|
|
1005
|
+
// artifact_status. The post-trigger observation diffs against this
|
|
1006
|
+
// baseline — a build whose agent_id was absent from (or differs
|
|
1007
|
+
// from) the baseline is attributed to our edit; a build already in
|
|
1008
|
+
// flight at baseline predates the trigger and is never attributed
|
|
1009
|
+
// to it. If the baseline read itself fails, receipt attribution is
|
|
1010
|
+
// skipped and the durable audit-dir evidence below is the only
|
|
1011
|
+
// positive signal.
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
<a id="agent-id-attribution"></a>
|
|
1015
|
+
## Agent ID attribution
|
|
1016
|
+
|
|
1017
|
+
Invariant: the artifact build's agent_id is attributed to the edit call.
|
|
1018
|
+
|
|
1019
|
+
Applies to: standard, bugfix, chore.
|
|
1020
|
+
|
|
1021
|
+
```
|
|
1022
|
+
// The artifact build's agent_id, attributed to this edit by the
|
|
1023
|
+
// workflow-owned observation below. The agent_id is the artifact
|
|
1024
|
+
// system's in-flight correlation ID (research 2026-09-12):
|
|
1025
|
+
// artifact.edit returns pending_init with NO agent_id, but
|
|
1026
|
+
// artifact_status exposes build.agent_id immediately after
|
|
1027
|
+
// acceptance, stable across polls. Recorded in the ledger so an
|
|
1028
|
+
// attempt correlates to the exact builder run; null when no build
|
|
1029
|
+
// was ever observed.
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
<a id="hung-chunk"></a>
|
|
1033
|
+
## Hung chunk
|
|
1034
|
+
|
|
1035
|
+
Invariant: a hung or failed chunk is inconclusive, never terminal.
|
|
1036
|
+
|
|
1037
|
+
Applies to: standard, bugfix, chore.
|
|
1038
|
+
|
|
1039
|
+
```
|
|
1040
|
+
// A hung or failed chunk is inconclusive, never terminal:
|
|
1041
|
+
// record it and continue to the next chunk. (2026-09-16,
|
|
1042
|
+
// clean-room task 1febe8eb: the platform's 270s agent
|
|
1043
|
+
// timeout killed chunk 2, which threw out of this loop —
|
|
1044
|
+
// skipping chunk 3 AND the STEP 1b audit-dir fallback and
|
|
1045
|
+
// parking on the exception path.) Fail-closed still applies
|
|
1046
|
+
// after chunk 3 and the fallback are exhausted.
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
<a id="no-attributable-build"></a>
|
|
1050
|
+
## No attributable build
|
|
1051
|
+
|
|
1052
|
+
Invariant: no attributable build and no durable evidence means no publish.
|
|
1053
|
+
|
|
1054
|
+
Applies to: standard, bugfix, chore.
|
|
1055
|
+
|
|
1056
|
+
```
|
|
1057
|
+
// No attributable build and no durable evidence — but that proves
|
|
1058
|
+
// nothing (a fast-completing build can finish between polls, or
|
|
1059
|
+
// the checks themselves failed). Verdict UNKNOWN via the same
|
|
1060
|
+
// mapping: record it in the ledger and fall through to
|
|
1061
|
+
// post-deploy. No retry: re-issuing the edit here duplicated it
|
|
1062
|
+
// on 2026-09-12. Never poll blind on a null receipt.
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
<a id="already-merged"></a>
|
|
1066
|
+
## Already-merged attestation
|
|
1067
|
+
|
|
1068
|
+
Invariant: when the Build gate verifies already-merged, attestation is recorded.
|
|
1069
|
+
|
|
1070
|
+
Applies to: standard, bugfix, chore.
|
|
1071
|
+
|
|
1072
|
+
```
|
|
1073
|
+
// Already-merged attestation: when the Build gate verified the builder's
|
|
1074
|
+
// already-merged declaration, the workflow records its own marker line in
|
|
1075
|
+
// the session notes (like the builder markers above, it is appended after
|
|
1076
|
+
// the slice so it can never be amputated). A later run resumed at Review
|
|
1077
|
+
// hydrates alreadyMergedSha from this workflow-attested line — never from
|
|
1078
|
+
// the builder's declaration alone.
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
<a id="explicit-refusal-16"></a>
|
|
1082
|
+
## Explicit refusal
|
|
1083
|
+
|
|
1084
|
+
Invariant: the child must explicitly refuse artifact work.
|
|
1085
|
+
|
|
1086
|
+
Applies to: standard, bugfix, chore.
|
|
1087
|
+
|
|
1088
|
+
```
|
|
1089
|
+
// Explicit refusal (room #16 blocker 10): the child ends its turn
|
|
1090
|
+
// with ARTIFACT_EDIT_REFUSED when artifact_edit explicitly refused.
|
|
1091
|
+
// Conclusive negative evidence — the edit provably did NOT go
|
|
1092
|
+
// through — so this parks rejected and skips observation polling.
|
|
1093
|
+
// A missing/unparseable signal is NOT a refusal: it stays unknown
|
|
1094
|
+
// and fail-closed below.
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
<a id="publish-verify"></a>
|
|
1098
|
+
## Publish verification
|
|
1099
|
+
|
|
1100
|
+
Invariant: the agent cannot verify publish; the parent does.
|
|
1101
|
+
|
|
1102
|
+
Applies to: standard, bugfix, chore.
|
|
1103
|
+
|
|
1104
|
+
```
|
|
1105
|
+
// Deterministic publish verification: the agent cannot self-certify a publish.
|
|
1106
|
+
// Skip-aware (park 2026-09-11): when the deterministic publish script found
|
|
1107
|
+
// no merge lock held (empty-diff Integrate), it skips the publish path
|
|
1108
|
+
// gracefully and emits the machine-readable PUBLISH_SKIPPED=no-lock-held
|
|
1109
|
+
// marker. The preflight (bugfix 2026-09-17) emits
|
|
1110
|
+
// PUBLISH_SKIPPED=no-npm-publish when npm publish is not configured on
|
|
1111
|
+
// this machine (helper or credential absent) — also before any mutation.
|
|
1112
|
+
// Verification is then vacuous — nothing was shipped, and the
|
|
1113
|
+
// registry must NOT have moved. The marker is script-emitted explicit state
|
|
1114
|
+
// (pasted verbatim per the Publish agent instructions), not agent prose; a
|
|
1115
|
+
// report without the marker still runs the full verification fail-closed.
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
<a id="integrate-verify"></a>
|
|
1119
|
+
## Integrate verification
|
|
1120
|
+
|
|
1121
|
+
Invariant: the agent cannot verify integrate mechanically; the workflow checks the diff.
|
|
1122
|
+
|
|
1123
|
+
Applies to: standard, bugfix, chore.
|
|
1124
|
+
|
|
1125
|
+
```
|
|
1126
|
+
// Deterministic integrate verification: the agent cannot self-certify a
|
|
1127
|
+
// merge. After the Integrate agent claims success, the workflow confirms
|
|
1128
|
+
// mechanically that the task branch tip is an ancestor of main via the
|
|
1129
|
+
// lifecycle script's verify-merge command (which resolves the branch
|
|
1130
|
+
// through the crew registry — never by reconstructing "task/"+taskId — so
|
|
1131
|
+
// the check cannot verify the wrong branch). The VERIFIED marker is matched
|
|
1132
|
+
// by regex on the script's own stdout; agent prose is never read. This
|
|
1133
|
+
// closes the hole where an agent reported "merged empty" while approved
|
|
1134
|
+
// commits were still stranded on the task branch (bug b1b1f919). A genuine
|
|
1135
|
+
// empty-diff Integrate (MERGED_EMPTY: no commits ahead of main) verifies
|
|
1136
|
+
// vacuously — the tip is then an ancestor of main. Verification failure is
|
|
1137
|
+
// an operational step failure, not a park: the dispatcher retries Integrate
|
|
1138
|
+
// under its consecutive-failure cap, and the retry finds the commits still
|
|
1139
|
+
// on the branch and performs the real merge — self-healing.
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
<a id="already-merged-idem"></a>
|
|
1143
|
+
## Already-merged idempotency
|
|
1144
|
+
|
|
1145
|
+
Invariant: an already-merged repo_diff is idempotent; no rebuild.
|
|
1146
|
+
|
|
1147
|
+
Applies to: standard, bugfix, chore.
|
|
1148
|
+
|
|
1149
|
+
```
|
|
1150
|
+
// Already-merged idempotency: a `repo_diff: none (already-merged:
|
|
1151
|
+
// <sha>)` declaration is verified mechanically — <sha> must resolve
|
|
1152
|
+
// and be an ancestor of main in the configured repo. A fabricated or
|
|
1153
|
+
// mistaken declaration fails the phase here (the dispatcher retries
|
|
1154
|
+
// Build under its consecutive-failure cap); a verified declaration is
|
|
1155
|
+
// recorded in alreadyMergedSha for Review's no-diff branch. Without
|
|
1156
|
+
// this guard, Build correctly doing nothing left Review with no
|
|
1157
|
+
// mechanical way to accept an empty diff, and Cass rejected for "no
|
|
1158
|
+
// commits ahead of main — the builder likely forgot to commit" while
|
|
1159
|
+
// the deliverable sat on main (canary 2026-09-15, task 1d692d91).
|
|
1160
|
+
// The sha is hex-only by construction (extractAlreadyMerged), so
|
|
1161
|
+
// interpolating it into the shell command cannot inject.
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
<a id="ledger-trigger"></a>
|
|
1165
|
+
## Ledger trigger
|
|
1166
|
+
|
|
1167
|
+
Invariant: record the trigger in the durable ledger.
|
|
1168
|
+
|
|
1169
|
+
Applies to: standard, bugfix, chore.
|
|
1170
|
+
|
|
1171
|
+
```
|
|
1172
|
+
// Durable publish-attempt ledger: record the trigger outcome while the
|
|
1173
|
+
// attempt key and commit are in scope. Every attempt lands here with
|
|
1174
|
+
// its outcome — submitted, rejected, or unknown (unknown is recorded
|
|
1175
|
+
// at the park site above). A later run or human matches commit hash +
|
|
1176
|
+
// attempt key against the builder's eventual completion.
|
|
1177
|
+
// (2026-09-16) The trigger is fire-and-forget: the observation above
|
|
1178
|
+
// already recorded the ledger's submitted line on both positive paths
|
|
1179
|
+
// and parked on unknown — there is no applied report to observe and
|
|
1180
|
+
// no rejection signal to record.
|
|
1181
|
+
// (2026-09-18, H2 verdict-first) The verdict was decided exactly
|
|
1182
|
+
// once above; dispatch on it. landed bypasses the receipt poll
|
|
1183
|
+
// (the audit evidence already proved completion); an explicit
|
|
1184
|
+
// publishFailure is preserved verbatim through post-deploy.
|
|
1185
|
+
// Otherwise the verdict is open — but the poll below is only
|
|
1186
|
+
// legitimate against a real receipt: the null-safe assertion records
|
|
1187
|
+
// UNKNOWN and continues to STEP 2 instead of polling blind.
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
<a id="trigger-await"></a>
|
|
1191
|
+
## Trigger await
|
|
1192
|
+
|
|
1193
|
+
Invariant: the artifact_edit call is awaited.
|
|
1194
|
+
|
|
1195
|
+
Applies to: standard, bugfix, chore.
|
|
1196
|
+
|
|
1197
|
+
```
|
|
1198
|
+
// The trigger itself: the artifact_edit call is AWAITED (the workflow
|
|
1199
|
+
// waits for it to complete) but its return value is intentionally
|
|
1200
|
+
// UNCONSUMED — NO schema, so no schema validation can fail this
|
|
1201
|
+
// call: a schema-less call resolves to the child's raw response as
|
|
1202
|
+
// a plain string (probed live 2026-09-16 — never parsed, never
|
|
1203
|
+
// throws on content). One caveat, also probed: the runtime still
|
|
1204
|
+
// scans the response for a JSON candidate, and an unparseable
|
|
1205
|
+
// {...}-looking substring in the child's prose throws ("response
|
|
1206
|
+
// JSON candidate", probe P6). The prompt tells the child to end its
|
|
1207
|
+
// turn with no prose at all, which keeps the common case clean —
|
|
1208
|
+
// but the channel is stochastic, so any throw is possible and
|
|
1209
|
+
// inconclusive: the edit may still have gone through, so the
|
|
1210
|
+
// outcome stays unknown until the observation below confirms it —
|
|
1211
|
+
// never inferred from the throw, and never blind-retried (a blind
|
|
1212
|
+
// re-trigger duplicated the edit on 2026-09-12).
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
<a id="diff-computation"></a>
|
|
1216
|
+
## Diff computation
|
|
1217
|
+
|
|
1218
|
+
Invariant: the diff is computed, the rebuild is triggered, and the report is verified.
|
|
1219
|
+
|
|
1220
|
+
Applies to: standard, bugfix, chore.
|
|
1221
|
+
|
|
1222
|
+
```
|
|
1223
|
+
// (below) the diff computation, rebuild trigger, application
|
|
1224
|
+
// verification, bounded poll, and provenance stamp. The builder
|
|
1225
|
+
// only makes the artifact_edit call and reports the applied
|
|
1226
|
+
// changes — no prose claim to trust. If the artifact tool namespace
|
|
1227
|
+
// is missing from this child it reports honestly and the workflow
|
|
1228
|
+
// retries once with a fresh key (bounded); anything else parks.
|
|
1229
|
+
// Publish diff base (2026-09-14, task 0c53af4e): the carried diff is
|
|
1230
|
+
// BASE..HEAD where BASE is the previously-stamped provenance
|
|
1231
|
+
// source_commit — NOT HEAD^1. A push-time reconcile merge puts the
|
|
1232
|
+
// task's own changes behind an intermediate merge, so HEAD^1..HEAD
|
|
1233
|
+
// silently drops the task's fix while the artifact builds without
|
|
1234
|
+
// it. The stamped base is the artifact's actual content; BASE..HEAD
|
|
1235
|
+
// is the complete unpublished delta. Empty tree only for a genuine
|
|
1236
|
+
// first publish (no provenance stamped yet).
|
|
1237
|
+
```
|
|
1238
|
+
|
|
1239
|
+
<a id="already-merged-hydra"></a>
|
|
1240
|
+
## Already-merged hydration
|
|
1241
|
+
|
|
1242
|
+
Invariant: when this run did not execute, hydration uses the existing merge.
|
|
1243
|
+
|
|
1244
|
+
Applies to: standard, bugfix, chore.
|
|
1245
|
+
|
|
1246
|
+
```
|
|
1247
|
+
// Already-merged hydration: when this run did not execute Build itself
|
|
1248
|
+
// (dispatcher resume at Review after a platform death between phases),
|
|
1249
|
+
// recover the workflow-verified sha. Room #16 blocker 11: the structured
|
|
1250
|
+
// session field is read FIRST — the `already_merged_verified:` notes line
|
|
1251
|
+
// is only a fallback, because session notes are hard-capped at 3000
|
|
1252
|
+
// chars and a truthful declaration at the report's tail was silently
|
|
1253
|
+
// truncated. The structured value was written by the workflow after a
|
|
1254
|
+
// mechanical ancestor check — it is trusted; the builder's bare
|
|
1255
|
+
// declaration never is. Absent both, the mechanical fact below reads
|
|
1256
|
+
// "none declared" and Cass fails closed. The hydration read is best-effort:
|
|
1257
|
+
// a transport throw degrades to "none declared" rather than crashing Review.
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
<a id="already-merged-idem2"></a>
|
|
1261
|
+
## Already-merged idempotency
|
|
1262
|
+
|
|
1263
|
+
Invariant: idempotency for already-merged tasks.
|
|
1264
|
+
|
|
1265
|
+
Applies to: standard, bugfix, chore.
|
|
1266
|
+
|
|
1267
|
+
```
|
|
1268
|
+
// Already-merged idempotency (canary 2026-09-15, task 1d692d91): when the
|
|
1269
|
+
// builder correctly makes no commit because the deliverable is already on
|
|
1270
|
+
// main (a prior merge or hand-repair landed it), it declares
|
|
1271
|
+
// `repo_diff: none (already-merged: <sha>)` naming the main commit that
|
|
1272
|
+
// carries the work. Room #16 blocker 11 (2026-09-18): the line anchor
|
|
1273
|
+
// missed Wren's mid-paragraph declaration, and the persisted notes truncated
|
|
1274
|
+
// the tail — so the anchor is gone and a sha followed by `)`, whitespace, or
|
|
1275
|
+
// end-of-string (truncation) is accepted. The sha is hex-only (7-40 chars)
|
|
1276
|
+
// so the workflow can interpolate it into the mechanical ancestor check
|
|
1277
|
+
// without injection risk; a over-long hex run never matches (the lookahead
|
|
1278
|
+
// fails on the extra hex char). Pure — pinned byte-identical across
|
|
1279
|
+
// standard/bugfix/chore.
|
|
1280
|
+
```
|