muse-crew 0.14.2 → 0.14.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 +3 -0
- package/docs/decisions/publish-path.md +189 -0
- package/docs/publish-verification.md +5 -2
- package/docs/reviews/critic-0143-redo.md +94 -0
- package/docs/reviews/critic-0143.md +108 -0
- package/lib/AGENTS.md +4 -3
- package/lib/crew-api.js +8 -6
- package/lib/publish-npm.sh +39 -10
- package/lib/retry-publish.js +5 -2
- package/lib/test-detached-integrate.sh +79 -3
- package/lib/test-publish-preflight.sh +94 -5
- package/lib/verify-publish.js +3 -4
- package/lib/worktree-lifecycle.sh +67 -25
- package/package.json +1 -1
- package/workflows/bugfix.js +27 -26
- package/workflows/chore.js +27 -26
- package/workflows/standard.js +27 -26
package/workflows/bugfix.js
CHANGED
|
@@ -416,7 +416,10 @@ function hydrateReleaseDecision(rec) {
|
|
|
416
416
|
// fabricated from the diff; it must match the artifact's real content.
|
|
417
417
|
|
|
418
418
|
// See docs/decisions/publish-path.md#publish-attempt-ledger: every trigger outcome is recorded in the durable ledger.
|
|
419
|
-
|
|
419
|
+
// keyTag (optional): same-outcome ledger writes in one pass need distinct
|
|
420
|
+
// agent-call keys — the runtime replays cached calls by key, so a shared key
|
|
421
|
+
// silently drops the second write (2026-09-19, submitted-on-issuance).
|
|
422
|
+
async function recordPublishLedger(entry, rework, keyTag) {
|
|
420
423
|
try {
|
|
421
424
|
var ledgerDir = crewHome + "/.publish-ledger";
|
|
422
425
|
var line = JSON.stringify({
|
|
@@ -438,7 +441,7 @@ async function recordPublishLedger(entry, rework) {
|
|
|
438
441
|
"Run: mkdir -p " + sq(ledgerDir) + " && printf '%s\n' " + sq(line) +
|
|
439
442
|
" | sed \"s/@LEDGER_TS@/$(date -u +%Y-%m-%dT%H:%M:%SZ)/\" >> " + sq(ledgerDir + "/" + PUBLISH_SLUG + ".jsonl") + " && echo LEDGER_OK\n" +
|
|
440
443
|
"Return JSON { \"result\": \"<verbatim output>\" } and nothing else.",
|
|
441
|
-
{ key: attemptKey("publish-ledger-" + taskId + "-" + entry.outcome, rework),
|
|
444
|
+
{ key: attemptKey("publish-ledger-" + taskId + "-" + entry.outcome + (keyTag ? "-" + keyTag : ""), rework),
|
|
442
445
|
label: "Recording publish attempt in ledger",
|
|
443
446
|
schema: { type: "object", properties: { result: { type: "string" } }, required: ["result"] } }
|
|
444
447
|
);
|
|
@@ -1289,7 +1292,7 @@ while (i < STEPS.length) {
|
|
|
1289
1292
|
"The script merges, reconciles, records, and pushes under the merge lock. Informational only: RECONCILED, NO_REMOTE, NO_REMOTE_RECONCILE. Read the output:\n" +
|
|
1290
1293
|
"- STAGED_BASE_MISMATCH: the line left the staged base (stray checkout). VERDICT: FAIL.\n" +
|
|
1291
1294
|
"- MERGED_EMPTY — check FIRST (it contains the word MERGED): either a runtime-state deliverable or an already-merged sha the workflow verified (Build declared repo_diff: none). No lock taken, no new commit. Report 'merged empty: no repo changes'. VERDICT: PASS.\n" +
|
|
1292
|
-
"- MERGED: the merge landed; the script pushed inline. Read the push line: PUSHED — report the hash
|
|
1295
|
+
"- MERGED: the merge landed; the script pushed inline. Read the push line: PUSHED — report the hash (detached prints PUSHED: origin/main (refspec HEAD:main)), VERDICT: PASS; NO_REMOTE_PUSH — no remote configured, VERDICT: PASS; ERROR after MERGED — a retry recovers the push; report it, VERDICT: FAIL.\n" +
|
|
1293
1296
|
"- LOCK_HELD: 10-minute backoff exhausted. VERDICT: FAIL.\n" +
|
|
1294
1297
|
"- CONFLICT: the plain merge failed — aborted, the target is clean, and your task still holds the merge lock. Do NOT fail yet. Resolve it:\n" +
|
|
1295
1298
|
"RESOLUTION:\n" +
|
|
@@ -1302,7 +1305,7 @@ while (i < STEPS.length) {
|
|
|
1302
1305
|
"ESCALATE — 'conflict needs human resolution', VERDICT: FAIL — when: 3 attempts are exhausted; the conflict touches generated files, migrations, or public API contracts; or 'looks right + checks pass' is not sufficient for any other reason. On escalation, RELEASE THE LOCK so the task can be reworked later: run CREW_HOME=" + crewHome + " CREW_REPO=" + REPO_PATH + " " + MERGE_LOCK + " release " + taskId + " (release is keyed on task id; no PID needed). Do NOT run post-deploy on escalation — it would delete the untouched task branch the human still needs.\n" +
|
|
1303
1306
|
"- ERROR: report it. VERDICT: FAIL.\n\n" +
|
|
1304
1307
|
"R5-PUSH (manual R5 resolution only — the normal path pushed inline). Run: " + LIFECYCLE_ENV + LIFECYCLE + " push-target " + taskId + "\n" +
|
|
1305
|
-
"PUSHED — report the merged hash, VERDICT: PASS; PUSH_SKIPPED
|
|
1308
|
+
"PUSHED — report the merged hash (detached prints PUSHED: origin/main (refspec HEAD:main)), VERDICT: PASS; PUSH_SKIPPED — no push attempted (no record + no lock); NO_REMOTE_PUSH — no remote; VERDICT: PASS; ERROR or CONFLICT — report it, VERDICT: FAIL.\n" +
|
|
1306
1309
|
"NEVER force-push.\n\n" +
|
|
1307
1310
|
"Report what happened at each step, ending with exactly one line: VERDICT: PASS or VERDICT: FAIL.";
|
|
1308
1311
|
|
|
@@ -1640,7 +1643,7 @@ while (i < STEPS.length) {
|
|
|
1640
1643
|
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");
|
|
1641
1644
|
}
|
|
1642
1645
|
// See docs/decisions/publish-path.md#trigger-await: the artifact_edit call is awaited.
|
|
1643
|
-
var rebuildTrigger = null;
|
|
1646
|
+
var rebuildTrigger = null, triggerThrew = false;
|
|
1644
1647
|
try {
|
|
1645
1648
|
var triggerText = String(await agent(rebuildPrompt,
|
|
1646
1649
|
{ key: rebuildAttemptKey, label: "Triggering artifact rebuild" }) || "");
|
|
@@ -1660,7 +1663,18 @@ while (i < STEPS.length) {
|
|
|
1660
1663
|
}
|
|
1661
1664
|
} catch (triggerErr) {
|
|
1662
1665
|
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");
|
|
1666
|
+
triggerThrew = true;
|
|
1663
1667
|
}
|
|
1668
|
+
// (2026-09-19, room #22) Submitted-on-issuance: see docs/decisions/publish-path.md#submitted-on-issuance.
|
|
1669
|
+
await recordPublishLedger({
|
|
1670
|
+
commit: mergeCommitForPublish,
|
|
1671
|
+
attempt: rebuildAttemptKey,
|
|
1672
|
+
agent_id: null,
|
|
1673
|
+
applied_report: publishAppliedObservation,
|
|
1674
|
+
manifest_before: preTriggerManifest,
|
|
1675
|
+
outcome: "submitted",
|
|
1676
|
+
detail: "trigger issuance: rebuild-trigger agent invoked; receipt/observation pending. Invocation only - not evidence of artifact_edit or build acceptance." + (triggerThrew ? "; trigger-threw" : "; trigger-returned")
|
|
1677
|
+
}, totalReworkCount, "issuance");
|
|
1664
1678
|
// Post-trigger observation (primary, not fallback): the workflow
|
|
1665
1679
|
// attributes the edit itself. First the in-flight build state — a
|
|
1666
1680
|
// build whose agent_id is new relative to the pre-trigger baseline
|
|
@@ -1780,8 +1794,8 @@ while (i < STEPS.length) {
|
|
|
1780
1794
|
applied_report: publishAppliedObservation,
|
|
1781
1795
|
manifest_before: preTriggerManifest,
|
|
1782
1796
|
outcome: "submitted",
|
|
1783
|
-
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff)"
|
|
1784
|
-
}, totalReworkCount);
|
|
1797
|
+
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff); observation, not re-issuance"
|
|
1798
|
+
}, totalReworkCount, "receipt");
|
|
1785
1799
|
} else {
|
|
1786
1800
|
var newAuditDirs = [];
|
|
1787
1801
|
try {
|
|
@@ -1912,10 +1926,6 @@ while (i < STEPS.length) {
|
|
|
1912
1926
|
// its outcome — submitted, rejected, or unknown (unknown is recorded
|
|
1913
1927
|
// at the park site above). A later run or human matches commit hash +
|
|
1914
1928
|
// attempt key against the builder's eventual completion.
|
|
1915
|
-
// (2026-09-16) The trigger is fire-and-forget: the observation above
|
|
1916
|
-
// already recorded the ledger's submitted line on both positive paths
|
|
1917
|
-
// and parked on unknown — there is no applied report to observe and
|
|
1918
|
-
// no rejection signal to record.
|
|
1919
1929
|
// (2026-09-18, H2 verdict-first) The verdict was decided exactly
|
|
1920
1930
|
// once above; dispatch on it. landed bypasses the receipt poll
|
|
1921
1931
|
// (the audit evidence already proved completion); an explicit
|
|
@@ -2049,21 +2059,12 @@ while (i < STEPS.length) {
|
|
|
2049
2059
|
log("Publish build landed for task " + taskId + " — provenance stamp deferred to parent content verification");
|
|
2050
2060
|
} else {
|
|
2051
2061
|
// STEP 1b durable audit-dir fallback (2026-09-15, task aadeccc3):
|
|
2052
|
-
// the poll
|
|
2053
|
-
//
|
|
2054
|
-
//
|
|
2055
|
-
//
|
|
2056
|
-
//
|
|
2057
|
-
//
|
|
2058
|
-
// that appeared during the attempt window is evidence a build
|
|
2059
|
-
// completed. Attribution is by window, not by build identity:
|
|
2060
|
-
// the poll's saw_stranger signal only catches stranger builds in
|
|
2061
|
-
// flight AT a check — a stranger that finished entirely inside
|
|
2062
|
-
// the window is indistinguishable, so any observed stranger
|
|
2063
|
-
// blocks attribution and the outcome stays unknown. This never
|
|
2064
|
-
// re-issues the edit and never stamps provenance — ok=true only
|
|
2065
|
-
// routes to the parent's independent content read-back, which
|
|
2066
|
-
// remains the real verification.
|
|
2062
|
+
// the poll observes only in-flight builds; a build that finished
|
|
2063
|
+
// inside the window leaves a durable audit dir. Diff the audit
|
|
2064
|
+
// listing against the pre-trigger snapshot: a new timestamped dir
|
|
2065
|
+
// is evidence a build completed. Attribution is by window, not by build identity: any
|
|
2066
|
+
// observed stranger blocks it. Never re-issues, never stamps;
|
|
2067
|
+
// ok=true routes to the parent's read-back (the verification).
|
|
2067
2068
|
//
|
|
2068
2069
|
// The poll end-state is read from the poll's own observations,
|
|
2069
2070
|
// not from build_done alone: a build in flight at the last check
|
package/workflows/chore.js
CHANGED
|
@@ -475,7 +475,10 @@ function hydrateReleaseDecision(rec) {
|
|
|
475
475
|
// fabricated from the diff; it must match the artifact's real content.
|
|
476
476
|
|
|
477
477
|
// See docs/decisions/publish-path.md#publish-attempt-ledger: every trigger outcome is recorded in the durable ledger.
|
|
478
|
-
|
|
478
|
+
// keyTag (optional): same-outcome ledger writes in one pass need distinct
|
|
479
|
+
// agent-call keys — the runtime replays cached calls by key, so a shared key
|
|
480
|
+
// silently drops the second write (2026-09-19, submitted-on-issuance).
|
|
481
|
+
async function recordPublishLedger(entry, rework, keyTag) {
|
|
479
482
|
try {
|
|
480
483
|
var ledgerDir = crewHome + "/.publish-ledger";
|
|
481
484
|
var line = JSON.stringify({
|
|
@@ -497,7 +500,7 @@ async function recordPublishLedger(entry, rework) {
|
|
|
497
500
|
"Run: mkdir -p " + sq(ledgerDir) + " && printf '%s\n' " + sq(line) +
|
|
498
501
|
" | sed \"s/@LEDGER_TS@/$(date -u +%Y-%m-%dT%H:%M:%SZ)/\" >> " + sq(ledgerDir + "/" + PUBLISH_SLUG + ".jsonl") + " && echo LEDGER_OK\n" +
|
|
499
502
|
"Return JSON { \"result\": \"<verbatim output>\" } and nothing else.",
|
|
500
|
-
{ key: attemptKey("publish-ledger-" + taskId + "-" + entry.outcome, rework),
|
|
503
|
+
{ key: attemptKey("publish-ledger-" + taskId + "-" + entry.outcome + (keyTag ? "-" + keyTag : ""), rework),
|
|
501
504
|
label: "Recording publish attempt in ledger",
|
|
502
505
|
schema: { type: "object", properties: { result: { type: "string" } }, required: ["result"] } }
|
|
503
506
|
);
|
|
@@ -1195,7 +1198,7 @@ while (i < STEPS.length) {
|
|
|
1195
1198
|
"The script merges, reconciles, records, and pushes under the merge lock. Informational only: RECONCILED, NO_REMOTE, NO_REMOTE_RECONCILE. Read the output:\n" +
|
|
1196
1199
|
"- STAGED_BASE_MISMATCH: the line left the staged base (stray checkout). VERDICT: FAIL.\n" +
|
|
1197
1200
|
"- MERGED_EMPTY — check FIRST (it contains the word MERGED): either a runtime-state deliverable or an already-merged sha the workflow verified (Build declared repo_diff: none). No lock taken, no new commit. Report 'merged empty: no repo changes'. VERDICT: PASS.\n" +
|
|
1198
|
-
"- MERGED: the merge landed; the script pushed inline. Read the push line: PUSHED — report the hash
|
|
1201
|
+
"- MERGED: the merge landed; the script pushed inline. Read the push line: PUSHED — report the hash (detached prints PUSHED: origin/main (refspec HEAD:main)), VERDICT: PASS; NO_REMOTE_PUSH — no remote configured, VERDICT: PASS; ERROR after MERGED — a retry recovers the push; report it, VERDICT: FAIL.\n" +
|
|
1199
1202
|
"- LOCK_HELD: 10-minute backoff exhausted. VERDICT: FAIL.\n" +
|
|
1200
1203
|
"- CONFLICT: the plain merge failed — aborted, the target is clean, and your task still holds the merge lock. Do NOT fail yet. Resolve it:\n" +
|
|
1201
1204
|
"RESOLUTION:\n" +
|
|
@@ -1208,7 +1211,7 @@ while (i < STEPS.length) {
|
|
|
1208
1211
|
"ESCALATE — 'conflict needs human resolution', VERDICT: FAIL — when: 3 attempts are exhausted; the conflict touches generated files, migrations, or public API contracts; or 'looks right + checks pass' is not sufficient for any other reason. On escalation, RELEASE THE LOCK so the task can be reworked later: run CREW_HOME=" + crewHome + " CREW_REPO=" + REPO_PATH + " " + MERGE_LOCK + " release " + taskId + " (release is keyed on task id; no PID needed). Do NOT run post-deploy on escalation — it would delete the untouched task branch the human still needs.\n" +
|
|
1209
1212
|
"- ERROR: report it. VERDICT: FAIL.\n\n" +
|
|
1210
1213
|
"R5-PUSH (manual R5 resolution only — the normal path pushed inline). Run: " + LIFECYCLE_ENV + LIFECYCLE + " push-target " + taskId + "\n" +
|
|
1211
|
-
"PUSHED — report the merged hash, VERDICT: PASS; PUSH_SKIPPED
|
|
1214
|
+
"PUSHED — report the merged hash (detached prints PUSHED: origin/main (refspec HEAD:main)), VERDICT: PASS; PUSH_SKIPPED — no push attempted (no record + no lock); NO_REMOTE_PUSH — no remote; VERDICT: PASS; ERROR or CONFLICT — report it, VERDICT: FAIL.\n" +
|
|
1212
1215
|
"NEVER force-push.\n\n" +
|
|
1213
1216
|
"Report what happened at each step, ending with exactly one line: VERDICT: PASS or VERDICT: FAIL.";
|
|
1214
1217
|
|
|
@@ -1546,7 +1549,7 @@ while (i < STEPS.length) {
|
|
|
1546
1549
|
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");
|
|
1547
1550
|
}
|
|
1548
1551
|
// See docs/decisions/publish-path.md#trigger-await: the artifact_edit call is awaited.
|
|
1549
|
-
var rebuildTrigger = null;
|
|
1552
|
+
var rebuildTrigger = null, triggerThrew = false;
|
|
1550
1553
|
try {
|
|
1551
1554
|
var triggerText = String(await agent(rebuildPrompt,
|
|
1552
1555
|
{ key: rebuildAttemptKey, label: "Triggering artifact rebuild" }) || "");
|
|
@@ -1566,7 +1569,18 @@ while (i < STEPS.length) {
|
|
|
1566
1569
|
}
|
|
1567
1570
|
} catch (triggerErr) {
|
|
1568
1571
|
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");
|
|
1572
|
+
triggerThrew = true;
|
|
1569
1573
|
}
|
|
1574
|
+
// (2026-09-19, room #22) Submitted-on-issuance: see docs/decisions/publish-path.md#submitted-on-issuance.
|
|
1575
|
+
await recordPublishLedger({
|
|
1576
|
+
commit: mergeCommitForPublish,
|
|
1577
|
+
attempt: rebuildAttemptKey,
|
|
1578
|
+
agent_id: null,
|
|
1579
|
+
applied_report: publishAppliedObservation,
|
|
1580
|
+
manifest_before: preTriggerManifest,
|
|
1581
|
+
outcome: "submitted",
|
|
1582
|
+
detail: "trigger issuance: rebuild-trigger agent invoked; receipt/observation pending. Invocation only - not evidence of artifact_edit or build acceptance." + (triggerThrew ? "; trigger-threw" : "; trigger-returned")
|
|
1583
|
+
}, reworkCount, "issuance");
|
|
1570
1584
|
// Post-trigger observation (primary, not fallback): the workflow
|
|
1571
1585
|
// attributes the edit itself. First the in-flight build state — a
|
|
1572
1586
|
// build whose agent_id is new relative to the pre-trigger baseline
|
|
@@ -1686,8 +1700,8 @@ while (i < STEPS.length) {
|
|
|
1686
1700
|
applied_report: publishAppliedObservation,
|
|
1687
1701
|
manifest_before: preTriggerManifest,
|
|
1688
1702
|
outcome: "submitted",
|
|
1689
|
-
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff)"
|
|
1690
|
-
}, reworkCount);
|
|
1703
|
+
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff); observation, not re-issuance"
|
|
1704
|
+
}, reworkCount, "receipt");
|
|
1691
1705
|
} else {
|
|
1692
1706
|
var newAuditDirs = [];
|
|
1693
1707
|
try {
|
|
@@ -1818,10 +1832,6 @@ while (i < STEPS.length) {
|
|
|
1818
1832
|
// its outcome — submitted, rejected, or unknown (unknown is recorded
|
|
1819
1833
|
// at the park site above). A later run or human matches commit hash +
|
|
1820
1834
|
// attempt key against the builder's eventual completion.
|
|
1821
|
-
// (2026-09-16) The trigger is fire-and-forget: the observation above
|
|
1822
|
-
// already recorded the ledger's submitted line on both positive paths
|
|
1823
|
-
// and parked on unknown — there is no applied report to observe and
|
|
1824
|
-
// no rejection signal to record.
|
|
1825
1835
|
// (2026-09-18, H2 verdict-first) The verdict was decided exactly
|
|
1826
1836
|
// once above; dispatch on it. landed bypasses the receipt poll
|
|
1827
1837
|
// (the audit evidence already proved completion); an explicit
|
|
@@ -1955,21 +1965,12 @@ while (i < STEPS.length) {
|
|
|
1955
1965
|
log("Publish build landed for task " + taskId + " — provenance stamp deferred to parent content verification");
|
|
1956
1966
|
} else {
|
|
1957
1967
|
// STEP 1b durable audit-dir fallback (2026-09-15, task aadeccc3):
|
|
1958
|
-
// the poll
|
|
1959
|
-
//
|
|
1960
|
-
//
|
|
1961
|
-
//
|
|
1962
|
-
//
|
|
1963
|
-
//
|
|
1964
|
-
// that appeared during the attempt window is evidence a build
|
|
1965
|
-
// completed. Attribution is by window, not by build identity:
|
|
1966
|
-
// the poll's saw_stranger signal only catches stranger builds in
|
|
1967
|
-
// flight AT a check — a stranger that finished entirely inside
|
|
1968
|
-
// the window is indistinguishable, so any observed stranger
|
|
1969
|
-
// blocks attribution and the outcome stays unknown. This never
|
|
1970
|
-
// re-issues the edit and never stamps provenance — ok=true only
|
|
1971
|
-
// routes to the parent's independent content read-back, which
|
|
1972
|
-
// remains the real verification.
|
|
1968
|
+
// the poll observes only in-flight builds; a build that finished
|
|
1969
|
+
// inside the window leaves a durable audit dir. Diff the audit
|
|
1970
|
+
// listing against the pre-trigger snapshot: a new timestamped dir
|
|
1971
|
+
// is evidence a build completed. Attribution is by window, not by build identity: any
|
|
1972
|
+
// observed stranger blocks it. Never re-issues, never stamps;
|
|
1973
|
+
// ok=true routes to the parent's read-back (the verification).
|
|
1973
1974
|
//
|
|
1974
1975
|
// The poll end-state is read from the poll's own observations,
|
|
1975
1976
|
// not from build_done alone: a build in flight at the last check
|
package/workflows/standard.js
CHANGED
|
@@ -428,7 +428,10 @@ function hydrateReleaseDecision(rec) {
|
|
|
428
428
|
// it must match the artifact's real content.
|
|
429
429
|
|
|
430
430
|
// See docs/decisions/publish-path.md#publish-attempt-ledger: every trigger outcome is recorded in the durable ledger.
|
|
431
|
-
|
|
431
|
+
// keyTag (optional): same-outcome ledger writes in one pass need distinct
|
|
432
|
+
// agent-call keys — the runtime replays cached calls by key, so a shared key
|
|
433
|
+
// silently drops the second write (2026-09-19, submitted-on-issuance).
|
|
434
|
+
async function recordPublishLedger(entry, rework, keyTag) {
|
|
432
435
|
try {
|
|
433
436
|
var ledgerDir = crewHome + "/.publish-ledger";
|
|
434
437
|
var line = JSON.stringify({
|
|
@@ -450,7 +453,7 @@ async function recordPublishLedger(entry, rework) {
|
|
|
450
453
|
"Run: mkdir -p " + sq(ledgerDir) + " && printf '%s\n' " + sq(line) +
|
|
451
454
|
" | sed \"s/@LEDGER_TS@/$(date -u +%Y-%m-%dT%H:%M:%SZ)/\" >> " + sq(ledgerDir + "/" + PUBLISH_SLUG + ".jsonl") + " && echo LEDGER_OK\n" +
|
|
452
455
|
"Return JSON { \"result\": \"<verbatim output>\" } and nothing else.",
|
|
453
|
-
{ key: attemptKey("publish-ledger-" + taskId + "-" + entry.outcome, rework),
|
|
456
|
+
{ key: attemptKey("publish-ledger-" + taskId + "-" + entry.outcome + (keyTag ? "-" + keyTag : ""), rework),
|
|
454
457
|
label: "Recording publish attempt in ledger",
|
|
455
458
|
schema: { type: "object", properties: { result: { type: "string" } }, required: ["result"] } }
|
|
456
459
|
);
|
|
@@ -1182,7 +1185,7 @@ while (i < STEPS.length) {
|
|
|
1182
1185
|
"The script merges, reconciles, records, and pushes under the merge lock. Informational only: RECONCILED, NO_REMOTE, NO_REMOTE_RECONCILE. Read the output:\n" +
|
|
1183
1186
|
"- STAGED_BASE_MISMATCH: the line left the staged base (stray checkout). VERDICT: FAIL.\n" +
|
|
1184
1187
|
"- MERGED_EMPTY — check FIRST (it contains the word MERGED): either a runtime-state deliverable or an already-merged sha the workflow verified (Build declared repo_diff: none). No lock taken, no new commit. Report 'merged empty: no repo changes'. VERDICT: PASS.\n" +
|
|
1185
|
-
"- MERGED: the merge landed; the script pushed inline. Read the push line: PUSHED — report the hash
|
|
1188
|
+
"- MERGED: the merge landed; the script pushed inline. Read the push line: PUSHED — report the hash (detached prints PUSHED: origin/main (refspec HEAD:main)), VERDICT: PASS; NO_REMOTE_PUSH — no remote configured, VERDICT: PASS; ERROR after MERGED — a retry recovers the push; report it, VERDICT: FAIL.\n" +
|
|
1186
1189
|
"- LOCK_HELD: 10-minute backoff exhausted. VERDICT: FAIL.\n" +
|
|
1187
1190
|
"- CONFLICT: the plain merge failed — aborted, the target is clean, and your task still holds the merge lock. Do NOT fail yet. Resolve it:\n" +
|
|
1188
1191
|
"RESOLUTION:\n" +
|
|
@@ -1195,7 +1198,7 @@ while (i < STEPS.length) {
|
|
|
1195
1198
|
"ESCALATE — 'conflict needs human resolution', VERDICT: FAIL — when: 3 attempts are exhausted; the conflict touches generated files, migrations, or public API contracts; or 'looks right + checks pass' is not sufficient for any other reason. On escalation, RELEASE THE LOCK so the task can be reworked later: run CREW_HOME=" + crewHome + " CREW_REPO=" + REPO_PATH + " " + MERGE_LOCK + " release " + taskId + " (release is keyed on task id; no PID needed). Do NOT run post-deploy on escalation — it would delete the untouched task branch the human still needs.\n" +
|
|
1196
1199
|
"- ERROR: report it. VERDICT: FAIL.\n\n" +
|
|
1197
1200
|
"R5-PUSH (manual R5 resolution only — the normal path pushed inline). Run: " + LIFECYCLE_ENV + LIFECYCLE + " push-target " + taskId + "\n" +
|
|
1198
|
-
"PUSHED — report the merged hash, VERDICT: PASS; PUSH_SKIPPED
|
|
1201
|
+
"PUSHED — report the merged hash (detached prints PUSHED: origin/main (refspec HEAD:main)), VERDICT: PASS; PUSH_SKIPPED — no push attempted (no record + no lock); NO_REMOTE_PUSH — no remote; VERDICT: PASS; ERROR or CONFLICT — report it, VERDICT: FAIL.\n" +
|
|
1199
1202
|
"NEVER force-push.\n\n" +
|
|
1200
1203
|
"Report what happened at each step, ending with exactly one line: VERDICT: PASS or VERDICT: FAIL.";
|
|
1201
1204
|
|
|
@@ -1533,7 +1536,7 @@ while (i < STEPS.length) {
|
|
|
1533
1536
|
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");
|
|
1534
1537
|
}
|
|
1535
1538
|
// See docs/decisions/publish-path.md#trigger-await: the artifact_edit call is awaited.
|
|
1536
|
-
var rebuildTrigger = null;
|
|
1539
|
+
var rebuildTrigger = null, triggerThrew = false;
|
|
1537
1540
|
try {
|
|
1538
1541
|
var triggerText = String(await agent(rebuildPrompt,
|
|
1539
1542
|
{ key: rebuildAttemptKey, label: "Triggering artifact rebuild" }) || "");
|
|
@@ -1553,7 +1556,18 @@ while (i < STEPS.length) {
|
|
|
1553
1556
|
}
|
|
1554
1557
|
} catch (triggerErr) {
|
|
1555
1558
|
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");
|
|
1559
|
+
triggerThrew = true;
|
|
1556
1560
|
}
|
|
1561
|
+
// (2026-09-19, room #22) Submitted-on-issuance: see docs/decisions/publish-path.md#submitted-on-issuance.
|
|
1562
|
+
await recordPublishLedger({
|
|
1563
|
+
commit: mergeCommitForPublish,
|
|
1564
|
+
attempt: rebuildAttemptKey,
|
|
1565
|
+
agent_id: null,
|
|
1566
|
+
applied_report: publishAppliedObservation,
|
|
1567
|
+
manifest_before: preTriggerManifest,
|
|
1568
|
+
outcome: "submitted",
|
|
1569
|
+
detail: "trigger issuance: rebuild-trigger agent invoked; receipt/observation pending. Invocation only - not evidence of artifact_edit or build acceptance." + (triggerThrew ? "; trigger-threw" : "; trigger-returned")
|
|
1570
|
+
}, totalReworkCount, "issuance");
|
|
1557
1571
|
// Post-trigger observation (primary, not fallback): the workflow
|
|
1558
1572
|
// attributes the edit itself. First the in-flight build state — a
|
|
1559
1573
|
// build whose agent_id is new relative to the pre-trigger baseline
|
|
@@ -1673,8 +1687,8 @@ while (i < STEPS.length) {
|
|
|
1673
1687
|
applied_report: publishAppliedObservation,
|
|
1674
1688
|
manifest_before: preTriggerManifest,
|
|
1675
1689
|
outcome: "submitted",
|
|
1676
|
-
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff)"
|
|
1677
|
-
}, totalReworkCount);
|
|
1690
|
+
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff); observation, not re-issuance"
|
|
1691
|
+
}, totalReworkCount, "receipt");
|
|
1678
1692
|
} else {
|
|
1679
1693
|
var newAuditDirs = [];
|
|
1680
1694
|
try {
|
|
@@ -1805,10 +1819,6 @@ while (i < STEPS.length) {
|
|
|
1805
1819
|
// its outcome — submitted, rejected, or unknown (unknown is recorded
|
|
1806
1820
|
// at the park site above). A later run or human matches commit hash +
|
|
1807
1821
|
// attempt key against the builder's eventual completion.
|
|
1808
|
-
// (2026-09-16) The trigger is fire-and-forget: the observation above
|
|
1809
|
-
// already recorded the ledger's submitted line on both positive paths
|
|
1810
|
-
// and parked on unknown — there is no applied report to observe and
|
|
1811
|
-
// no rejection signal to record.
|
|
1812
1822
|
// (2026-09-18, H2 verdict-first) The verdict was decided exactly
|
|
1813
1823
|
// once above; dispatch on it. landed bypasses the receipt poll
|
|
1814
1824
|
// (the audit evidence already proved completion); an explicit
|
|
@@ -1942,21 +1952,12 @@ while (i < STEPS.length) {
|
|
|
1942
1952
|
log("Publish build landed for task " + taskId + " — provenance stamp deferred to parent content verification");
|
|
1943
1953
|
} else {
|
|
1944
1954
|
// STEP 1b durable audit-dir fallback (2026-09-15, task aadeccc3):
|
|
1945
|
-
// the poll
|
|
1946
|
-
//
|
|
1947
|
-
//
|
|
1948
|
-
//
|
|
1949
|
-
//
|
|
1950
|
-
//
|
|
1951
|
-
// that appeared during the attempt window is evidence a build
|
|
1952
|
-
// completed. Attribution is by window, not by build identity:
|
|
1953
|
-
// the poll's saw_stranger signal only catches stranger builds in
|
|
1954
|
-
// flight AT a check — a stranger that finished entirely inside
|
|
1955
|
-
// the window is indistinguishable, so any observed stranger
|
|
1956
|
-
// blocks attribution and the outcome stays unknown. This never
|
|
1957
|
-
// re-issues the edit and never stamps provenance — ok=true only
|
|
1958
|
-
// routes to the parent's independent content read-back, which
|
|
1959
|
-
// remains the real verification.
|
|
1955
|
+
// the poll observes only in-flight builds; a build that finished
|
|
1956
|
+
// inside the window leaves a durable audit dir. Diff the audit
|
|
1957
|
+
// listing against the pre-trigger snapshot: a new timestamped dir
|
|
1958
|
+
// is evidence a build completed. Attribution is by window, not by build identity: any
|
|
1959
|
+
// observed stranger blocks it. Never re-issues, never stamps;
|
|
1960
|
+
// ok=true routes to the parent's read-back (the verification).
|
|
1960
1961
|
//
|
|
1961
1962
|
// The poll end-state is read from the poll's own observations,
|
|
1962
1963
|
// not from build_done alone: a build in flight at the last check
|