muse-crew 0.7.18 → 0.7.19

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.
@@ -450,48 +450,6 @@ function parseUnifiedDiff(diffText) {
450
450
  return files;
451
451
  }
452
452
 
453
- // Compare the artifact builder's reported applied-changes against the diff's
454
- // expected changes. Every added/removed line must match exactly per path,
455
- // and the file counts must match — the builder applies exactly the carried
456
- // change, nothing more, nothing less. Pure function — no I/O, no clock.
457
- // OBSERVATION INPUT ONLY (2026-09-12, task 23ca8f3f): the applied report
458
- // has a demonstrated false-negative mode (applied:[] for a diff the builder
459
- // had actually applied), and it is derived from the carried diff, so a match
460
- // certifies nothing either. A mismatch is logged as observation; it never
461
- // parks and never blocks the stamp. The verification is the independent
462
- // read-back (docs/publish-verification.md).
463
- function verifyAppliedChanges(expected, applied) {
464
- if (!Array.isArray(applied)) {
465
- return { ok: false, reason: "builder returned no applied-changes list" };
466
- }
467
- function sorted(a) { return (a || []).slice().sort(); }
468
- function eq(a, b) {
469
- a = sorted(a); b = sorted(b);
470
- if (a.length !== b.length) return false;
471
- for (var i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
472
- return true;
473
- }
474
- for (var i = 0; i < expected.length; i++) {
475
- var exp = expected[i];
476
- var got = null;
477
- for (var j = 0; j < applied.length; j++) {
478
- if (applied[j] && applied[j].path === exp.path) { got = applied[j]; break; }
479
- }
480
- if (!got) {
481
- return { ok: false, reason: "builder did not report changing '" + exp.path + "'" };
482
- }
483
- if (!eq(exp.added, got.added)) {
484
- return { ok: false, reason: "added lines for '" + exp.path + "' do not match the carried diff" };
485
- }
486
- if (!eq(exp.removed, got.removed)) {
487
- return { ok: false, reason: "removed lines for '" + exp.path + "' do not match the carried diff" };
488
- }
489
- }
490
- if (applied.length !== expected.length) {
491
- return { ok: false, reason: "builder reported changing " + applied.length + " file(s), diff carries " + expected.length };
492
- }
493
- return { ok: true };
494
- }
495
453
 
496
454
  // Publish read-back request (currently unavailable): the verbatim_request
497
455
  // the parent protocol (docs/publish-verification.md) would hand to an
@@ -505,36 +463,13 @@ function verifyAppliedChanges(expected, applied) {
505
463
  // finding. Until a read-back path exists, the parent cannot independently
506
464
  // confirm content and verification parks at "publish: verification-requested"
507
465
  // (see docs/publish-verification.md). This preserves the circularity break
508
- // that hollowed canary run 8 (2026-09-11): verifyAppliedChanges compares the
509
- // builder's applied-report against the diff the report was derived from — a
510
- // fabricated report passes by construction. Independent read-back cannot be
466
+ // that hollowed canary run 8 (2026-09-11): the old verifyAppliedChanges
467
+ // compared the builder's applied-report against the diff the report was
468
+ // derived from — a fabricated report passed by construction. The report
469
+ // itself is gone now (2026-09-16 fire-and-forget trigger). Independent
470
+ // read-back cannot be
511
471
  // fabricated from the diff;
512
472
  // it must match the artifact's real content.
513
- // Pre-publish base observation (diagnostic, 2026-09-12): instruction fragment
514
- // for the builder's edit request, asking it to report the sha256 of each
515
- // touched file's CURRENT content BEFORE applying the diff. Pure function —
516
- // no I/O, no clock.
517
- //
518
- // Why: the builder applies the carried diff to its own source tree, whose
519
- // base state is unrecorded. The post-hoc read-back only checks the changed
520
- // regions AFTER the edit; it cannot tell us what base the diff landed on.
521
- // If the tree was dirty or drifted before the edit, the read-back still
522
- // passes (the diff's lines are present) while the artifact silently carries
523
- // uncommitted content — the production validateRepoPath incident
524
- // (2026-09-12), where the live artifact contained code absent from every git
525
- // ref. These pre-hashes, compared against the workflow-computed expected
526
- // base hashes (merge parent), reveal what the publish actually read.
527
- // Observation only — the workflow logs mismatches but never parks on them.
528
- function buildPreHashInstruction(files) {
529
- var paths = files.map(function(f) { return f.path; }).join(", ");
530
- return (
531
- "- BEFORE applying anything, compute the sha256 hash of each file below as it CURRENTLY exists in your source tree (before your changes). Use the exact bytes of the current file content.\n" +
532
- "- Report these hashes in the \"pre_hashes\" field of your return JSON, as { \"<path>\": \"<sha256 hex>\" }.\n" +
533
- "- If a file does not exist in your tree, report its hash as the string \"MISSING\".\n" +
534
- "- Do this BEFORE applying the diff — the hashes must reflect the pre-edit state.\n" +
535
- " Files: " + paths + "\n"
536
- );
537
- }
538
473
 
539
474
  // Durable publish-attempt ledger (2026-09-12): every artifact publish
540
475
  // attempt is recorded append-only at $CREW_HOME/.publish-ledger/<slug>.jsonl
@@ -1476,38 +1411,8 @@ while (i < STEPS.length) {
1476
1411
  if (expectedChanges.length === 0) {
1477
1412
  return await parkTask("Publish diff parsed to zero files for commit " + (mergeCommitForPublish || "unknown") + " — cannot verify application. Human attention needed.");
1478
1413
  }
1479
- // Pre-publish base observation (diagnostic, 2026-09-12): the builder
1480
- // applies the diff to its own source tree, whose base state is
1481
- // unrecorded. Compute the trustworthy expected base — the sha256 of
1482
- // each touched file at the merge parent commit — so the builder's
1483
- // self-reported pre-edit hashes (see buildPreHashInstruction) can be
1484
- // compared against it. Observation only: a mismatch is logged loudly
1485
- // but never parks. The observation tells us what the publish actually
1486
- // reads, so the subsequent fix can require the right base.
1487
- var expectedBaseHashes = {};
1488
- if (publishBase === EMPTY_TREE_SHA) {
1489
- // First publish: every file in the diff is new to the artifact.
1490
- expectedChanges.forEach(function (f) { expectedBaseHashes[f.path] = "NEW-FILE"; });
1491
- log("Publish expected base hashes for task " + taskId + ": empty tree (first publish) — all " + expectedChanges.length + " file(s) new");
1492
- } else try {
1493
- // Shell-quote helper (no regex-with-quote: the test parser does not
1494
- // understand regex literals containing quotes).
1495
- var sq = function(s) { return "'" + String(s).split("'").join("'\\''") + "'"; };
1496
- var baseHashResult = await agent(
1497
- "Run: cd " + REPO_PATH + " && parent=" + sq(publishBase) + " && for f in " + expectedChanges.map(function(f) { return sq(f.path); }).join(" ") + "; do printf '%s:' \"$f\"; git show \"$parent:$f\" 2>/dev/null | sha256sum | cut -d' ' -f1; done\n" +
1498
- "Return JSON { \"hashes\": \"<newline-separated <path>:<sha256> lines, empty hash means the file is new in this diff>\" } and nothing else.",
1499
- { key: attemptKey("publish-base-hashes-" + taskId, totalReworkCount), label: "Computing expected base content hashes",
1500
- schema: { type: "object", properties: { hashes: { type: "string" } }, required: ["hashes"] } }
1501
- );
1502
- (baseHashResult.hashes || "").split("\n").forEach(function(line) {
1503
- var m = /^([^:]+):([0-9a-f]*)$/.exec(line.trim());
1504
- if (m) expectedBaseHashes[m[1]] = m[2] || "NEW-FILE";
1505
- });
1506
- log("Publish expected base hashes for task " + taskId + " (stamped base " + publishBase.slice(0, 12) + "): " + JSON.stringify(expectedBaseHashes));
1507
- } catch (e) {
1508
- log("Publish expected base hash computation failed for task " + taskId + " (non-fatal, observation degraded): " + (e && e.message ? e.message : e));
1509
- }
1510
1414
  var rebuildPrompt =
1415
+ ARTIFACT_LOAD_PREAMBLE +
1511
1416
  "Call artifact_edit with slug \"" + PUBLISH_SLUG + "\" and verbatim_request:\n" +
1512
1417
  "'Apply the following change to your source tree, then rebuild and deploy.\n" +
1513
1418
  "\n" +
@@ -1520,70 +1425,36 @@ while (i < STEPS.length) {
1520
1425
  "- For a deleted file (+++ /dev/null), delete it.\n" +
1521
1426
  "- If any hunk does not apply cleanly, STOP and report the failure — do not improvise or skip hunks.\n" +
1522
1427
  "- Do not make any other source changes.\n" +
1523
- "- After applying, rebuild and deploy.\n" +
1524
- "- Report, for each file you changed: its path, the exact lines you added, and the exact lines you removed.\n" +
1525
- buildPreHashInstruction(expectedChanges) + "'\n" +
1526
- ARTIFACT_LOAD_PREAMBLE +
1527
- "If artifact_edit is still not available after the load, do NOT improvise — return { \"edit_started\": false, \"build_agent_id\": null, \"error\": \"artifact_tools missing after load\", \"applied\": [] }.\n" +
1528
- "RECEIPT CAPTURE (receipt-chained publish, 2026-09-13): the edit is only half the contract — you must also capture the platform build's receipt, the in-flight correlation ID the follow-up poll chains to.\n" +
1529
- "- The artifact namespace is already loaded (see below). BEFORE calling artifact_edit, call artifact_status with slug \"" + PUBLISH_SLUG + "\" and note the running build's agent_id (or null when no build is running). This is the pre-edit baseline.\n" +
1530
- "- Call artifact_edit as instructed above.\n" +
1531
- "- IMMEDIATELY after artifact_edit returns, call artifact_status again. If a build is running whose agent_id DIFFERS from the pre-edit baseline (or the baseline was null), that build is this edit's — its agent_id is the receipt.\n" +
1532
- "- If the post-edit status shows the SAME agent_id as the baseline, or no build at all, wait about 15 seconds and check artifact_status again, up to 4 more times. If a build with a NEW agent_id appears, that is the receipt.\n" +
1533
- "- If no new build appears, the receipt is null: the build may be pending_init-invisible, may have finished before the capture, or may be queued behind the earlier build. Return null — do NOT guess, and do NOT substitute the baseline build's agent_id.\n" +
1534
- "Make no other calls. Return JSON { \"edit_started\": <true if the edit was accepted, false otherwise>, \"build_agent_id\": <the receipt agent_id string, or null when no build could be attributed to this edit>, \"error\": \"<details or empty string>\", \"applied\": [{\"path\": \"<file path>\", \"added\": [\"<added lines>\"], \"removed\": [\"<removed lines>\"]}], \"pre_hashes\": {\"<file path>\": \"<sha256 of that file's content BEFORE you applied the diff, or \"MISSING\">\"} } and nothing else.";
1535
- var rebuildSchema =
1536
- { type: "object",
1537
- properties: {
1538
- edit_started: { type: "boolean" },
1539
- build_agent_id: {
1540
- type: ["string", "null"],
1541
- description: "Receipt-chained publish (2026-09-13): the platform build's in-flight correlation ID (build.agent_id from artifact_status) captured immediately after the edit was accepted — the receipt the follow-up poll chains to. Null when no build could be attributed to this edit. Required: an accepted edit with a null receipt parks fail-closed as unknown."
1542
- },
1543
- error: { type: "string" },
1544
- applied: {
1545
- type: "array",
1546
- items: {
1547
- type: "object",
1548
- properties: {
1549
- path: { type: "string" },
1550
- added: { type: "array", items: { type: "string" } },
1551
- removed: { type: "array", items: { type: "string" } }
1552
- },
1553
- required: ["path", "added", "removed"]
1554
- }
1555
- },
1556
- pre_hashes: {
1557
- type: "object",
1558
- description: "Diagnostic (2026-09-12): sha256 of each touched file's content BEFORE the builder applied the diff, as reported by the builder. Compared against the workflow-computed expected base hashes (merge parent) — observation only, never gating."
1559
- }
1560
- },
1561
- required: ["edit_started", "build_agent_id", "applied"] };
1562
- var rebuildTrigger = null;
1563
- var rebuildReportMissing = false; // true if the edit went through but the agent returned no applied report (structured-output failure) — the smoke-check is skipped; the parent's independent read-back is the verification
1564
- var rebuildEvidenceNote = null; // human-readable evidence line for the ledger when the edit is confirmed via fallback evidence (in-flight poll or durable audit dir) rather than the trigger's own report
1428
+ "- After applying, rebuild and deploy.'\n" +
1429
+ "Edit-request contract (read carefully):\n" +
1430
+ "- Call artifact_edit exactly once with the slug and verbatim_request above. Never retry the edit yourself: if the edit is not accepted, do NOT call artifact_edit again — end your turn.\n" +
1431
+ "- If artifact_edit is not available after the load, do NOT improvise — end your turn.\n" +
1432
+ "- You do NOT call setprovenance, artifact_inspect, or post-deploy yourself.\n" +
1433
+ "No report is needed: do not return JSON, do not summarize what you did, do not echo the diff. End your turn after the artifact_edit call.\n";
1565
1434
  // The trigger key of the attempt that last ran, for the publish ledger.
1566
1435
  // Minted once here (not re-minted per use site) so the ledger always
1567
1436
  // records the exact key that was issued — and so a re-minted duplicate
1568
1437
  // can never drift from it.
1569
1438
  var rebuildAttemptKey = attemptKey("publish-artifact-rebuild-" + taskId, totalReworkCount);
1570
- // The artifact build's agent_id, captured from artifact_status when the
1571
- // trigger's outcome is ambiguous (structured-output failure). The
1572
- // agent_id is the artifact system's in-flight correlation ID (not durable post-completion)
1573
- // (research 2026-09-12): artifact.edit returns pending_init with NO
1574
- // agent_id, but artifact_status exposes build.agent_id immediately
1575
- // after acceptance, stable across polls. Recorded in the ledger so an
1576
- // ambiguous attempt correlates to the exact builder run; null when no
1577
- // build was ever observed.
1439
+ // The artifact build's agent_id, attributed to this edit by the
1440
+ // workflow-owned observation below. The agent_id is the artifact
1441
+ // system's in-flight correlation ID (research 2026-09-12):
1442
+ // artifact.edit returns pending_init with NO agent_id, but
1443
+ // artifact_status exposes build.agent_id immediately after
1444
+ // acceptance, stable across polls. Recorded in the ledger so an
1445
+ // attempt correlates to the exact builder run; null when no build
1446
+ // was ever observed.
1578
1447
  var rebuildAgentId = null;
1579
- // The builder's applied-report is an observation, not a gate
1580
- // (2026-09-12, task 23ca8f3f): computed once the trigger outcome is
1581
- // known, logged loudly, never a park.
1582
- var publishAppliedObservation = null; // "match" | "mismatch: <reason>" | "missing-report" — observation only, never a park
1583
- // Durable-evidence snapshot (2026-09-14): the structured-output
1584
- // fallback below only observes IN-FLIGHT builds. A build that
1585
- // finished before the poll leaves no in-flight trace — but the
1586
- // platform's audit harness leaves a durable one:
1448
+ // The builder's applied report is gone (2026-09-16): it rode on the
1449
+ // trigger's JSON closeout contract, which is removed below. The
1450
+ // parent's independent read-back (docs/publish-verification.md) is
1451
+ // the verification — this field stays "missing-report" on every
1452
+ // ledger line the workflow writes.
1453
+ var publishAppliedObservation = "missing-report";
1454
+ // Durable-evidence snapshot (2026-09-14): the observation below only
1455
+ // detects IN-FLIGHT builds. A build that finished before the
1456
+ // observation leaves no in-flight trace — but the platform's audit
1457
+ // harness leaves a durable one:
1587
1458
  // ~/workspace/ts-spaces/<slug>/audits/<timestamp>-<id>/ per
1588
1459
  // completed build. Snapshot the listing BEFORE the trigger so the
1589
1460
  // fallback can diff before/after: a directory appearing during the
@@ -1606,238 +1477,219 @@ while (i < STEPS.length) {
1606
1477
  } catch (auditBeforeErr) {
1607
1478
  log("Publish audit-dir snapshot before trigger failed for task " + taskId + " (non-fatal, durable-evidence check degraded): " + (auditBeforeErr && auditBeforeErr.message ? auditBeforeErr.message : auditBeforeErr));
1608
1479
  }
1480
+ // Fire-and-forget trigger + workflow-owned observation (2026-09-16,
1481
+ // clean-room task e2a8d9f8): the trigger's JSON closeout contract
1482
+ // traveled over the stochastic text channel, and the runtime's
1483
+ // JSON-candidate heuristic misfired on it ("workflow agent output
1484
+ // was not JSON: no JSON object or array found in final response"),
1485
+ // parking a task whose edit may have gone through. The contract's
1486
+ // content was already observation-only (the applied report never
1487
+ // gated; the pre_hashes were diagnostic-only), so the contract is
1488
+ // removed: the trigger carries NO schema and its return value is
1489
+ // never consumed, which takes the extraction heuristic out of this
1490
+ // call entirely. The workflow attributes the edit itself through
1491
+ // the tiny schema'd reads below — no prose is parsed for the
1492
+ // trigger outcome.
1493
+ // (Probe, 2026-09-16: the workflow scope exposes only agent() —
1494
+ // tool_search, artifact_edit and artifact_status are undefined
1495
+ // there — so the workflow cannot call the artifact tools directly;
1496
+ // observation still goes through minimal child calls with tiny
1497
+ // schemas, never a broad JSON contract.)
1498
+ //
1499
+ // Pre-trigger toolcheck (tiny, schema'd): the artifact namespace is
1500
+ // deferred for workflow children — the child self-loads it and emits
1501
+ // one exact signal line, read mechanically (never English prose).
1502
+ // Explicit negative evidence (missing) gets one bounded retry with a
1503
+ // fresh key, then parks: without the tools the edit provably did NOT
1504
+ // go through, so this is the one safe retry on the publish path.
1505
+ var publishToolsOk = false;
1506
+ for (var toolcheckAttempt = 1; toolcheckAttempt <= 2 && !publishToolsOk; toolcheckAttempt++) {
1507
+ try {
1508
+ var toolcheckResult = await agent(
1509
+ "Check whether the artifact tool namespace is available.\n" +
1510
+ "Call tool_search.load_tool_namespace with paths [\"artifact\"].\n" +
1511
+ "Then emit exactly one line and nothing else: ARTIFACT_TOOLS: <ok if the load succeeded and artifact_edit and artifact_status are now functions, missing otherwise>.\n" +
1512
+ "Return JSON { \"signal\": \"<the exact ARTIFACT_TOOLS line>\" } and nothing else.",
1513
+ { key: attemptKey("publish-artifact-toolcheck-" + taskId + (toolcheckAttempt === 1 ? "" : "-retry2"), totalReworkCount),
1514
+ label: "Checking artifact tool availability" + (toolcheckAttempt === 1 ? "" : " (retry)"),
1515
+ schema: { type: "object", properties: { signal: { type: "string" } }, required: ["signal"] } }
1516
+ );
1517
+ publishToolsOk = /ARTIFACT_TOOLS:\s*ok/.test(String((toolcheckResult && toolcheckResult.signal) || ""));
1518
+ log("Publish artifact toolcheck for task " + taskId + " (attempt " + toolcheckAttempt + " of 2): " + (publishToolsOk ? "tools ok" : "tools missing"));
1519
+ } catch (toolcheckErr) {
1520
+ log("Publish artifact toolcheck for task " + taskId + " (attempt " + toolcheckAttempt + " of 2) failed (" + (toolcheckErr && toolcheckErr.message ? toolcheckErr.message : toolcheckErr) + ") — counted as missing for this attempt");
1521
+ }
1522
+ }
1523
+ if (!publishToolsOk) {
1524
+ await recordPublishLedger({
1525
+ commit: mergeCommitForPublish,
1526
+ attempt: rebuildAttemptKey,
1527
+ agent_id: null,
1528
+ applied_report: null,
1529
+ outcome: "rejected",
1530
+ detail: "artifact tool namespace missing in two toolcheck attempts (explicit negative evidence): the edit provably did not go through — no trigger issued, no blind retry"
1531
+ }, totalReworkCount);
1532
+ return await parkTask("Publish cannot proceed for task " + taskId + ": the artifact tool namespace was missing in two toolcheck attempts (explicit negative evidence — the edit provably did not go through, so no trigger was issued and nothing was retried blindly). Human attention needed.");
1533
+ }
1534
+ // Pre-trigger build-state baseline (tiny, schema'd): one read of
1535
+ // artifact_status. The post-trigger observation diffs against this
1536
+ // baseline — a build whose agent_id was absent from (or differs
1537
+ // from) the baseline is attributed to our edit; a build already in
1538
+ // flight at baseline predates the trigger and is never attributed
1539
+ // to it. If the baseline read itself fails, receipt attribution is
1540
+ // skipped and the durable audit-dir evidence below is the only
1541
+ // positive signal.
1542
+ var baselineAgentId = null;
1543
+ var baselineFailed = false;
1609
1544
  try {
1610
- rebuildTrigger = await agent(rebuildPrompt,
1611
- { key: rebuildAttemptKey, label: "Triggering artifact rebuild", schema: rebuildSchema });
1612
- } catch (rebuildErr) {
1613
- // Structured-output failure (canary run 9, 2026-09-11): the agent
1614
- // called artifact_edit (tool_call_count > 0) but returned prose
1615
- // instead of JSON. The side effect may have happened — the outcome
1616
- // is UNKNOWN, not "did not go through". The old code asked a child
1617
- // for derived booleans and retried on all-false; that check issued
1618
- // the DUPLICATE artifact_edit on 2026-09-12.
1619
- //
1620
- // Build-ID research (2026-09-12) corrected the model: artifact.edit
1621
- // returns pending_init with NO agent_id, but artifact_status exposes
1622
- // the build's agent_id (the artifact system's in-flight build
1623
- // correlation ID, stable across polls while the build runs)
1624
- // immediately after acceptance.
1625
- // So the recovery no longer asks the child to derive booleans —
1626
- // the layer where the 2026-09-12 signal was lost. It reads the RAW
1627
- // build object and extracts build.agent_id mechanically in the
1628
- // workflow script. An observed agent_id is positive evidence the
1629
- // edit went through; no build after a bounded poll is still
1630
- // inconclusive (unknown), never proof the edit failed. Mechanical
1631
- // rule: never blind-retry on unknown — record the attempt and park
1632
- // fail-closed; correlate via the ledger, never by re-issuing.
1633
- log("Publish rebuild trigger: structured-output failure (" + (rebuildErr && rebuildErr.message ? rebuildErr.message : rebuildErr) + ") — checking build state before deciding; outcome unknown until state confirms it");
1634
- var buildState = null;
1635
- var buildStateFailed = false;
1545
+ var publishBaseline = await agent(
1546
+ ARTIFACT_LOAD_PREAMBLE +
1547
+ "Call artifact_status with slug \"" + PUBLISH_SLUG + "\" once.\n" +
1548
+ "Return JSON { \"build\": <the raw \"build\" value exactly as returned, or null when there is none> } and nothing else.",
1549
+ { key: attemptKey("publish-artifact-baseline-" + taskId, totalReworkCount), label: "Reading pre-trigger build state",
1550
+ schema: { type: "object", properties: { build: { type: ["object", "null"] } }, required: ["build"] } }
1551
+ );
1552
+ baselineAgentId = (publishBaseline && publishBaseline.build && typeof publishBaseline.build.agent_id === "string" && publishBaseline.build.agent_id) || null;
1553
+ log("Publish pre-trigger baseline for task " + taskId + ": " + (baselineAgentId ? "build " + baselineAgentId + " already in flight (predates the trigger — never attributed to this edit)" : "no build in flight"));
1554
+ } catch (baselineErr) {
1555
+ baselineFailed = true;
1556
+ log("Publish pre-trigger baseline read failed for task " + taskId + " (" + (baselineErr && baselineErr.message ? baselineErr.message : baselineErr) + ") — receipt attribution skipped; durable audit-dir evidence is the only positive signal");
1557
+ }
1558
+ // The trigger itself: fire-and-forget transport for the
1559
+ // artifact_edit call. NO schema — the return value is not consumed,
1560
+ // so the runtime's JSON-candidate heuristic never runs on this
1561
+ // call. A transport throw is possible and inconclusive: the edit
1562
+ // may still have gone through, so the outcome stays unknown until
1563
+ // the observation below confirms it — never inferred from the
1564
+ // throw, and never blind-retried (a blind re-trigger duplicated the
1565
+ // edit on 2026-09-12).
1566
+ var rebuildTrigger = null;
1567
+ try {
1568
+ var triggerResultLength = String(await agent(rebuildPrompt,
1569
+ { key: rebuildAttemptKey, label: "Triggering artifact rebuild" }) || "").length;
1570
+ log("Publish rebuild trigger for task " + taskId + " returned (" + triggerResultLength + " chars, fire-and-forget: not consumed)");
1571
+ } catch (triggerErr) {
1572
+ log("Publish rebuild trigger for task " + taskId + " threw (" + (triggerErr && triggerErr.message ? triggerErr.message : triggerErr) + ") — outcome unknown until observation confirms it; the edit may have gone through");
1573
+ }
1574
+ // Post-trigger observation (primary, not fallback): the workflow
1575
+ // attributes the edit itself. First the in-flight build state — a
1576
+ // build whose agent_id is new relative to the pre-trigger baseline
1577
+ // is this edit's receipt. Then the durable audit-dir diff — a
1578
+ // timestamped directory appearing during the trigger window proves
1579
+ // the edit went through and the build completed even when no
1580
+ // in-flight build was ever observed (the 2026-09-14 attempt-7 gap).
1581
+ // Absence of both signals proves nothing: the outcome is UNKNOWN,
1582
+ // never "did not go through". No blind retry — record the attempt
1583
+ // and park fail-closed; correlate via the ledger, never by
1584
+ // re-issuing.
1585
+ log("Publish rebuild trigger issued for task " + taskId + " — observing build state to attribute the edit");
1586
+ var buildState = null;
1587
+ var buildStateFailed = false;
1588
+ try {
1589
+ buildState = await agent(
1590
+ ARTIFACT_LOAD_PREAMBLE +
1591
+ "Call artifact_status with slug \"" + PUBLISH_SLUG + "\".\n" +
1592
+ "Poll up to 3 times, about 20 seconds apart, until the response shows a build (the \"build\" value is an object, not null). " +
1593
+ "Return the raw \"build\" value verbatim as JSON — the build object exactly as returned, with its agent_id, operation, status, and any other fields untouched. " +
1594
+ "Do not summarize, interpret, or derive booleans from it. " +
1595
+ "If no build appears after 3 polls, return null. " +
1596
+ "Return JSON { \"build\": <the raw build object or null> } and nothing else.",
1597
+ { key: attemptKey("publish-artifact-buildcheck-" + taskId, totalReworkCount), label: "Reading artifact build state after trigger",
1598
+ schema: { type: "object", properties: { build: { type: ["object", "null"] } }, required: ["build"] } }
1599
+ );
1600
+ } catch (buildCheckErr) {
1601
+ buildStateFailed = true;
1602
+ log("Publish post-trigger build-state check failed for task " + taskId + " (" + (buildCheckErr && buildCheckErr.message ? buildCheckErr.message : buildCheckErr) + ") — this signal is unknown, not negative");
1603
+ }
1604
+ var observedAgentId = (buildState && buildState.build && typeof buildState.build.agent_id === "string" && buildState.build.agent_id) || null;
1605
+ var receiptAgentId = (!buildStateFailed && !baselineFailed && observedAgentId && observedAgentId !== baselineAgentId) ? observedAgentId : null;
1606
+ if (receiptAgentId) {
1607
+ // The edit went through — a build with a new agent_id appeared
1608
+ // after the trigger. The parent's independent read-back
1609
+ // (docs/publish-verification.md) is the verification, not any
1610
+ // builder report.
1611
+ rebuildTrigger = { edit_started: true };
1612
+ rebuildAgentId = receiptAgentId;
1613
+ log("Publish rebuild trigger for task " + taskId + ": artifact_status shows build " + receiptAgentId + " for slug " + PUBLISH_SLUG + " (new relative to the pre-trigger baseline) — the edit went through.");
1614
+ await recordPublishLedger({
1615
+ commit: mergeCommitForPublish,
1616
+ attempt: rebuildAttemptKey,
1617
+ agent_id: rebuildAgentId,
1618
+ applied_report: publishAppliedObservation,
1619
+ outcome: "submitted",
1620
+ detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff)"
1621
+ }, totalReworkCount);
1622
+ } else {
1623
+ var newAuditDirs = [];
1636
1624
  try {
1637
- buildState = await agent(
1638
- ARTIFACT_LOAD_PREAMBLE +
1639
- "Call artifact_status with slug \"" + PUBLISH_SLUG + "\".\n" +
1640
- "Poll up to 3 times, about 20 seconds apart, until the response shows a build (the \"build\" value is an object, not null). " +
1641
- "Return the raw \"build\" value verbatim as JSON — the build object exactly as returned, with its agent_id, operation, status, and any other fields untouched. " +
1642
- "Do not summarize, interpret, or derive booleans from it. " +
1643
- "If no build appears after 3 polls, return null. " +
1644
- "Return JSON { \"build\": <the raw build object or null> } and nothing else.",
1645
- { key: attemptKey("publish-artifact-buildcheck-" + taskId, totalReworkCount), label: "Reading artifact build state after trigger failure",
1646
- schema: { type: "object", properties: { build: { type: ["object", "null"] } }, required: ["build"] } }
1625
+ var auditAfter = await agent(
1626
+ "List the artifact audit directories for slug \"" + PUBLISH_SLUG + "\" (best-effort, never a gate).\n" +
1627
+ "Run: ls -1 ~/workspace/ts-spaces/" + PUBLISH_SLUG + "/audits/ 2>/dev/null\n" +
1628
+ "Return JSON { \"dirs\": \"<newline-separated names, empty string when the audits directory does not exist or is empty>\" } and nothing else.",
1629
+ { key: attemptKey("publish-audit-after-" + taskId, totalReworkCount), label: "Re-listing audit dirs after trigger",
1630
+ schema: { type: "object", properties: { dirs: { type: "string" } }, required: ["dirs"] } }
1647
1631
  );
1648
- } catch (buildCheckErr) {
1649
- buildStateFailed = true;
1650
- log("Publish rebuild trigger: build-state check itself failed (" + (buildCheckErr && buildCheckErr.message ? buildCheckErr.message : buildCheckErr) + ") — treating the outcome as unknown");
1632
+ var auditDirsAfterTrigger = String((auditAfter && auditAfter.dirs) || "").split("\n").map(function (s) { return s.trim(); }).filter(function (s) { return s.length > 0; });
1633
+ // Only timestamped build dirs count — the "latest" symlink
1634
+ // and anything else are not builds.
1635
+ newAuditDirs = auditDirsAfterTrigger.filter(function (d) {
1636
+ return auditDirsBeforeTrigger.indexOf(d) === -1 && /^20\d\d-\d\d-\d\dT\d\d-\d\d-\d\dZ-/.test(d);
1637
+ });
1638
+ } catch (auditAfterErr) {
1639
+ log("Publish audit-dir re-list after trigger failed for task " + taskId + " (non-fatal, durable-evidence check degraded): " + (auditAfterErr && auditAfterErr.message ? auditAfterErr.message : auditAfterErr));
1651
1640
  }
1652
- var acceptedAgentId = (buildState && buildState.build && typeof buildState.build.agent_id === "string" && buildState.build.agent_id) || null;
1653
- if (!buildStateFailed && acceptedAgentId) {
1654
- // The edit went through — the agent just failed to return JSON.
1655
- // The build's agent_id is positive evidence: it appears in
1656
- // artifact_status immediately after our accepted edit (pending_init
1657
- // acceptance is followed by a visible build with a stable agent_id,
1658
- // per the 2026-09-12 research). No applied report to smoke-check;
1659
- // the parent's independent read-back (docs/publish-verification.md)
1660
- // is the real verification, not the circular applied-report. The
1661
- // agent_id is recorded in the ledger so this attempt correlates to
1662
- // the exact builder run, not just commit + attempt key.
1663
- log("Publish rebuild trigger: artifact_status shows build " + acceptedAgentId + " for slug " + PUBLISH_SLUG + " — the edit went through despite the structured-output failure. Skipping applied-report smoke-check; parent read-back is the verification.");
1664
- rebuildTrigger = { edit_started: true, error: "", applied: null };
1665
- rebuildReportMissing = true;
1666
- rebuildAgentId = acceptedAgentId;
1667
- rebuildEvidenceNote = "edit confirmed via build-state poll after structured-output failure (build " + acceptedAgentId + "); builder applied-report missing";
1668
- } else {
1669
- // Durable completion check (2026-09-14): the in-flight poll
1670
- // above only sees RUNNING builds. Attempt 7 (2026-09-14) proved
1671
- // the gap: the trigger child applied the edit, the build ran
1672
- // and completed — the platform's audit harness captured it
1673
- // mid-window — then the child failed to return JSON. The
1674
- // fallback poll saw no in-flight build, so a successful publish
1675
- // parked as "unknown". Diff the audit-dir listing against the
1676
- // pre-trigger snapshot: a timestamped directory that appeared
1677
- // during the trigger window is positive evidence the edit went
1678
- // through and the build completed. This never re-issues the
1679
- // edit and never stamps provenance — it only routes to the
1680
- // parent's independent content read-back, which remains the
1681
- // real verification.
1682
- var newAuditDirs = [];
1683
- try {
1684
- var auditAfter = await agent(
1685
- "List the artifact audit directories for slug \"" + PUBLISH_SLUG + "\" (best-effort, never a gate).\n" +
1686
- "Run: ls -1 ~/workspace/ts-spaces/" + PUBLISH_SLUG + "/audits/ 2>/dev/null\n" +
1687
- "Return JSON { \"dirs\": \"<newline-separated names, empty string when the audits directory does not exist or is empty>\" } and nothing else.",
1688
- { key: attemptKey("publish-audit-after-" + taskId, totalReworkCount), label: "Re-listing audit dirs after trigger failure",
1689
- schema: { type: "object", properties: { dirs: { type: "string" } }, required: ["dirs"] } }
1690
- );
1691
- var auditDirsAfterTrigger = String((auditAfter && auditAfter.dirs) || "").split("\n").map(function (s) { return s.trim(); }).filter(function (s) { return s.length > 0; });
1692
- // Only timestamped build dirs count — the "latest" symlink
1693
- // and anything else are not builds.
1694
- newAuditDirs = auditDirsAfterTrigger.filter(function (d) {
1695
- return auditDirsBeforeTrigger.indexOf(d) === -1 && /^20\d\d-\d\d-\d\dT\d\d-\d\d-\d\dZ-/.test(d);
1696
- });
1697
- } catch (auditAfterErr) {
1698
- log("Publish audit-dir re-list after trigger failure failed for task " + taskId + " (non-fatal, durable-evidence check degraded): " + (auditAfterErr && auditAfterErr.message ? auditAfterErr.message : auditAfterErr));
1699
- }
1700
- if (newAuditDirs.length > 0) {
1701
- log("Publish rebuild trigger: new audit dir(s) during the trigger window (" + newAuditDirs.join(", ") + ") — the edit went through and the build completed despite the structured-output failure. Skipping applied-report smoke-check; parent read-back is the verification.");
1702
- rebuildTrigger = { edit_started: true, error: "", applied: null };
1703
- rebuildReportMissing = true;
1704
- rebuildAgentId = null;
1705
- rebuildEvidenceNote = "edit confirmed via durable audit evidence after structured-output failure (new audit dir " + newAuditDirs[0] + "); builder applied-report missing";
1706
- } else {
1707
- // No build observed — but that proves nothing (a fast-completing
1708
- // build can finish between polls, or the check itself failed). The
1709
- // outcome is UNKNOWN. No retry: re-issuing the edit here duplicated
1710
- // it on 2026-09-12. Record the attempt durably and park fail-closed;
1711
- // correlate via the ledger, never by guessing from a blind poll.
1712
- log("Publish rebuild trigger: no build observed after structured-output failure — outcome UNKNOWN. Recording the attempt and parking fail-closed; no blind retry.");
1641
+ if (newAuditDirs.length > 0) {
1642
+ rebuildTrigger = { edit_started: true };
1643
+ rebuildAgentId = null;
1644
+ log("Publish rebuild trigger for task " + taskId + ": new audit dir(s) during the trigger window (" + newAuditDirs.join(", ") + ") — the edit went through and the build completed; no in-flight receipt was observed.");
1713
1645
  await recordPublishLedger({
1714
1646
  commit: mergeCommitForPublish,
1715
1647
  attempt: rebuildAttemptKey,
1716
1648
  agent_id: null,
1717
- applied_report: null,
1718
- outcome: "unknown",
1719
- detail: "structured-output failure on rebuild trigger; build-state poll saw no build (or the check itself failed); edit may have been accepted as pending_init"
1649
+ applied_report: publishAppliedObservation,
1650
+ outcome: "submitted",
1651
+ detail: "fire-and-forget trigger; edit confirmed via durable audit evidence (new audit dir " + newAuditDirs[0] + "); no in-flight receipt observed"
1720
1652
  }, totalReworkCount);
1721
- return await parkTask("Publish outcome unknown: the rebuild trigger's child did not return JSON, and the follow-up build-state poll could not observe a build for slug " + PUBLISH_SLUG + ". The edit may have been accepted as pending_init, so no retry was issued — a blind retry duplicated the edit on 2026-09-12. The attempt is recorded in the publish ledger at " + crewHome + "/.publish-ledger/" + PUBLISH_SLUG + ".jsonl (commit " + String(mergeCommitForPublish || "unknown").slice(0, 12) + "). Correlate the accepted edit via the ledger and the builder's eventual completion before re-driving Publish. Fail-closed.");
1722
- }
1723
- }
1724
- }
1725
- if (!rebuildReportMissing && !rebuildTrigger.edit_started && rebuildTrigger.error === "artifact_tools missing after load") {
1726
- log("Publish rebuild trigger: artifact_tools missing after load — one bounded retry with a fresh key");
1727
- rebuildTrigger = await agent(rebuildPrompt,
1728
- { key: attemptKey("publish-artifact-rebuild-" + taskId + "-retry2", totalReworkCount), label: "Triggering artifact rebuild (retry)", schema: rebuildSchema });
1729
- rebuildAttemptKey = attemptKey("publish-artifact-rebuild-" + taskId + "-retry2", totalReworkCount);
1730
- }
1731
- // Receipt chaining (2026-09-13): adopt the trigger's build receipt,
1732
- // or park on an uncorrelated acceptance. The trigger's closeout schema
1733
- // requires build_agent_id — the platform build's in-flight correlation
1734
- // ID captured immediately after the edit was accepted.
1735
- // An accepted edit (edit_started=true) with a null receipt is UNKNOWN,
1736
- // not "did not go through": the build may be pending_init-invisible,
1737
- // may have finished before the capture window, or may be queued behind
1738
- // a still-running earlier build. No re-trigger is issued on unknown —
1739
- // a blind re-trigger duplicated the edit on 2026-09-12, and the platform
1740
- // offers no idempotency proof that would make re-issue safe.
1741
- // (Retry-semantics reconciliation, 2026-09-13: the "no receipt → safe
1742
- // re-trigger" sketch assumed the edit command idempotently publishes
1743
- // what's on git; the duplicate-edit incident disproves the assumption,
1744
- // and the standing rule retries only on explicit negative evidence.
1745
- // Re-trigger stays exactly where it was: the edit_started=false
1746
- // explicit-rejection path above.) Record the attempt and park
1747
- // fail-closed; correlate via the ledger and the parent's content
1748
- // read-back before re-driving Publish.
1749
- if (rebuildTrigger && rebuildTrigger.edit_started && !rebuildReportMissing) {
1750
- var triggerAgentId = (typeof rebuildTrigger.build_agent_id === "string" && rebuildTrigger.build_agent_id.length > 0) ? rebuildTrigger.build_agent_id : null;
1751
- if (!triggerAgentId) {
1752
- var uncorrelatedObservation = (function () { var c = verifyAppliedChanges(expectedChanges, rebuildTrigger.applied); return c.ok ? "match" : "mismatch: " + c.reason; })();
1753
- log("Publish receipt missing for task " + taskId + ": the trigger reported edit_started=true but captured no build receipt (build_agent_id null) — no build attributable to this edit. Parking fail-closed without re-triggering.");
1653
+ } else {
1654
+ // No attributable build and no durable evidence — but that
1655
+ // proves nothing (a fast-completing build can finish between
1656
+ // polls, or the checks themselves failed). The outcome is
1657
+ // UNKNOWN. No retry: re-issuing the edit here duplicated it on
1658
+ // 2026-09-12. Record the attempt durably and park fail-closed;
1659
+ // correlate via the ledger, never by guessing from a blind poll.
1660
+ log("Publish rebuild trigger for task " + taskId + ": no attributable build observed and no new audit dir — outcome UNKNOWN. Recording the attempt and parking fail-closed; no blind retry.");
1754
1661
  await recordPublishLedger({
1755
1662
  commit: mergeCommitForPublish,
1756
1663
  attempt: rebuildAttemptKey,
1757
1664
  agent_id: null,
1758
- applied_report: uncorrelatedObservation,
1665
+ applied_report: null,
1759
1666
  outcome: "unknown",
1760
- detail: "edit accepted (edit_started=true) but the trigger captured no build receipt in its capture window: no build attributable to this edit (pending_init-invisible, finished before capture, or queued behind an earlier build). No re-trigger issued — a blind re-trigger on an unknown outcome duplicated the edit on 2026-09-12 and the platform offers no idempotency proof."
1667
+ detail: "fire-and-forget trigger; post-trigger build-state poll saw no attributable build (or the check failed) and the audit-dir diff found no new dir; the edit may have been accepted as pending_init"
1761
1668
  }, totalReworkCount);
1762
- return await parkTask("Publish outcome unknown: the rebuild trigger reported the edit was accepted but captured no build receipt (build_agent_id null) — no build could be attributed to this edit in the capture window. The edit may be pending_init-invisible, already finished, or queued behind an earlier build, so no re-trigger was issued: a blind retry duplicated the edit on 2026-09-12. The attempt is recorded in the publish ledger at " + crewHome + "/.publish-ledger/" + PUBLISH_SLUG + ".jsonl (commit " + String(mergeCommitForPublish || "unknown").slice(0, 12) + "). Correlate via the ledger and re-drive Publish only after the parent's content read-back resolves what actually landed. Fail-closed.");
1669
+ return await parkTask("Publish outcome unknown for task " + taskId + ": the rebuild trigger was issued fire-and-forget (no JSON closeout for the runtime heuristic to misfire on), and the follow-up observation could not attribute a build to the edit for slug " + PUBLISH_SLUG + " — no in-flight build with a new agent_id appeared in the poll window and no new audit dir landed. The edit may have been accepted as pending_init, so no retry was issued: a blind retry duplicated the edit on 2026-09-12. The attempt is recorded in the publish ledger at " + crewHome + "/.publish-ledger/" + PUBLISH_SLUG + ".jsonl (commit " + String(mergeCommitForPublish || "unknown").slice(0, 12) + "). Correlate the accepted edit via the ledger and the builder's eventual completion before re-driving Publish. Fail-closed.");
1763
1670
  }
1764
- rebuildAgentId = triggerAgentId;
1765
- log("Publish receipt chained for task " + taskId + ": build " + triggerAgentId + " — the follow-up poll waits on this build only.");
1766
1671
  }
1672
+
1767
1673
  // Durable publish-attempt ledger: record the trigger outcome while the
1768
1674
  // attempt key and commit are in scope. Every attempt lands here with
1769
1675
  // its outcome — submitted, rejected, or unknown (unknown is recorded
1770
1676
  // at the park site above). A later run or human matches commit hash +
1771
1677
  // attempt key against the builder's eventual completion.
1772
- if (rebuildTrigger && rebuildTrigger.edit_started) {
1773
- // Applied-report observation (2026-09-12, task 23ca8f3f): the
1774
- // builder's applied-report is logged as observation only — it
1775
- // never parks. The report is derived from the carried diff, so a
1776
- // "match" certifies nothing (canary run 8); and it has a
1777
- // demonstrated false-negative mode (applied:[] for a diff the
1778
- // builder had actually applied). The independent read-back below
1779
- // plus the parent protocol (docs/publish-verification.md) are the
1780
- // verification — this block always proceeds to them.
1781
- publishAppliedObservation = rebuildReportMissing
1782
- ? "missing-report"
1783
- : (function () { var c = verifyAppliedChanges(expectedChanges, rebuildTrigger.applied); return c.ok ? "match" : "mismatch: " + c.reason; })();
1784
- log("Publish applied-report observation for task " + taskId + ": " + publishAppliedObservation + " — observation only, never a park: the applied report is derived from the carried diff and proved unreliable in the false-negative direction (task 23ca8f3f, 2026-09-12: applied:[] for a diff the builder had applied). The independent read-back below plus the parent protocol are the verification.");
1785
- await recordPublishLedger({
1786
- commit: mergeCommitForPublish,
1787
- attempt: rebuildAttemptKey,
1788
- agent_id: rebuildAgentId,
1789
- applied_report: publishAppliedObservation,
1790
- outcome: "submitted",
1791
- detail: rebuildReportMissing
1792
- ? (rebuildEvidenceNote || "edit confirmed via build-state poll after structured-output failure (build " + (rebuildAgentId || "agent_id unknown") + "); builder applied-report missing")
1793
- : "edit accepted; builder applied-report received"
1794
- }, totalReworkCount);
1795
- } else if (rebuildTrigger) {
1796
- await recordPublishLedger({
1797
- commit: mergeCommitForPublish,
1798
- attempt: rebuildAttemptKey,
1799
- applied_report: null,
1800
- outcome: "rejected",
1801
- detail: "edit not accepted: " + (rebuildTrigger.error || "no error detail")
1802
- }, totalReworkCount);
1803
- }
1678
+ // (2026-09-16) The trigger is fire-and-forget: the observation above
1679
+ // already recorded the ledger's submitted line on both positive paths
1680
+ // and parked on unknown — there is no applied report to observe and
1681
+ // no rejection signal to record.
1804
1682
  var publishFailure = null;
1805
1683
  if (rebuildTrigger.edit_started) {
1806
- // STEP 1b (observation only): publishAppliedObservation was
1807
- // computed and logged above, inside the ledger block — the
1808
- // builder's applied-report never parks and never blocks the stamp.
1809
- // Task 23ca8f3f (2026-09-12) proved its false-negative mode:
1810
- // applied:[] for a diff the builder had actually applied, which
1811
- // parked a successful publish as unverified. A "match" certifies
1812
- // nothing either — the report is derived from the carried diff
1813
- // (canary run 8). The flow proceeds to the build poll and the
1814
- // independent read-back trigger regardless of what the report
1815
- // claimed; real verification is the parent's read-back
1816
- // (docs/publish-verification.md) before the provenance stamp.
1817
- // Pre-publish base observation (diagnostic, 2026-09-12): compare
1818
- // the builder's self-reported pre-edit hashes against the
1819
- // workflow-computed expected base (merge parent). This tells us
1820
- // what base state the publish actually read. OBSERVATION ONLY —
1821
- // a mismatch is logged loudly but never parks and never blocks
1822
- // the stamp. If the tree was dirty or drifted, the evidence is
1823
- // here; the fix (requiring the right base) comes after we see it.
1824
- try {
1825
- var preHashes = (rebuildTrigger && rebuildTrigger.pre_hashes) || {};
1826
- var baseLines = expectedChanges.map(function(f) {
1827
- var expected = expectedBaseHashes[f.path];
1828
- var actual = preHashes[f.path];
1829
- var expShort = (expected || "UNKNOWN").slice(0, 12);
1830
- var actShort = String(actual || "NOT-REPORTED").slice(0, 12);
1831
- var match = (expected !== undefined && actual !== undefined) ? (expected === actual) : "unknown";
1832
- return " " + f.path + ": expected_base=" + expShort + " builder_pre=" + actShort + " match=" + match;
1833
- });
1834
- var anyMismatch = expectedChanges.some(function(f) {
1835
- return expectedBaseHashes[f.path] !== undefined && preHashes[f.path] !== undefined && expectedBaseHashes[f.path] !== preHashes[f.path];
1836
- });
1837
- log("Publish pre-tree base observation for task " + taskId + (anyMismatch ? " — BASE MISMATCH DETECTED (tree was not at the expected merge-parent state when the diff was applied):" : " — base matches expected merge-parent state:") + "\n" + baseLines.join("\n"));
1838
- } catch (e) {
1839
- log("Publish pre-tree base observation failed for task " + taskId + " (non-fatal): " + (e && e.message ? e.message : e));
1840
- }
1684
+ // (2026-09-16) There is no builder report: the fire-and-forget
1685
+ // trigger carries no JSON contract, so there is nothing to
1686
+ // compare and no pre-hash diagnostic. The builder's old
1687
+ // self-report was circular by construction (canary run 8) with a
1688
+ // demonstrated false-negative mode (task 23ca8f3f, 2026-09-12:
1689
+ // applied:[] for a diff the builder had applied). The flow
1690
+ // proceeds to the build poll regardless; real verification is the
1691
+ // parent's independent read-back (docs/publish-verification.md)
1692
+ // before the provenance stamp.
1841
1693
  // STEP 1b (mechanical): bounded poll for build completion, chunked so
1842
1694
  // the merge-lock lease is refreshed before it can expire. The 600s
1843
1695
  // lease is shorter than the worst-case 10-minute build poll, so the
@@ -1925,9 +1777,9 @@ while (i < STEPS.length) {
1925
1777
  if (buildPoll.build_done && pollSawOurBuild) {
1926
1778
  // STEP 1c (mechanical): NO provenance stamp here. Canary run 8
1927
1779
  // (2026-09-11) proved the stamp cannot certify content: the
1928
- // builder's applied-report is derived from the carried diff, so
1929
- // verifyAppliedChanges above is circular — a fabricated report
1930
- // passes by construction, and every phase went green on a hollow
1780
+ // builder's applied-report was derived from the carried diff, so
1781
+ // the old report check was circular — a fabricated report
1782
+ // passed by construction, and every phase went green on a hollow
1931
1783
  // build. The stamp moves to the parent (docs/publish-verification.md);
1932
1784
  // the independent read-back step is currently unavailable (no
1933
1785
  // agent-callable read-back tool exists — artifact_inspect was
@@ -2053,7 +1905,9 @@ while (i < STEPS.length) {
2053
1905
  }
2054
1906
  }
2055
1907
  } else {
2056
- publishFailure = "Artifact rebuild trigger failed: " + (rebuildTrigger.error || "artifact_edit not accepted") + ". The publish did not land.";
1908
+ // Unreachable: the observation above either attributes the edit
1909
+ // (edit_started) or parks. Defensive only — never a silent pass.
1910
+ publishFailure = "Artifact rebuild trigger failed: the edit was not attributed to any observed build. The publish did not land.";
2057
1911
  }
2058
1912
  } // end: publishSkippedNoLock — no rebuild, no stamp, nothing to ship
2059
1913
  // STEP 2 (mechanical, always — skip path included): post-deploy