muse-crew 0.14.3 → 0.14.4
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/AGENTS.md +1 -1
- package/docs/decisions/AGENTS.md +1 -0
- package/docs/decisions/publish-path.md +43 -3
- package/docs/publish-verification.md +9 -1
- package/docs/release-integrity.md +60 -0
- package/docs/reviews/critic-0144.md +83 -0
- package/lib/AGENTS.md +10 -3
- package/lib/advance-publish-base.js +8 -0
- package/lib/append-ooda-step.js +12 -3
- package/lib/build-readback-request.js +10 -0
- package/lib/build-registry.js +8 -3
- package/lib/classify-publish-absence.js +11 -0
- package/lib/classify-surface.js +12 -2
- package/lib/commit-scaffold.js +22 -6
- package/lib/compose-evidence-caption.js +15 -4
- package/lib/compute-publish-diff.js +9 -0
- package/lib/crew-api.js +54 -21
- package/lib/crew-release.sh +233 -1
- package/lib/gitignore.js +23 -5
- package/lib/package.json +1 -0
- package/lib/publish-note-vocabulary.js +44 -0
- package/lib/read-ooda-verdict.js +11 -3
- package/lib/readback-disk.js +9 -0
- package/lib/render-html.js +17 -7
- package/lib/repo-orchestration.js +21 -5
- package/lib/retry-publish.js +77 -45
- package/lib/sample-project.js +22 -6
- package/lib/scaffold-crew.js +11 -2
- package/lib/see-act.js +17 -8
- package/lib/serve-artifact.js +12 -6
- package/lib/setup-project-repo.js +26 -7
- package/lib/update-watch.js +34 -17
- package/lib/ux-doctrine.js +31 -6
- package/lib/verify-publish.js +38 -2
- package/lib/write-ooda-verdict.js +12 -3
- package/package.json +1 -1
- package/workflows/bugfix.js +33 -35
- package/workflows/chore.js +33 -35
- package/workflows/standard.js +33 -16
- package/workflows/upgrade.js +4 -2
package/workflows/bugfix.js
CHANGED
|
@@ -70,8 +70,10 @@ const COMPUTE_DIFF_SRC = crewHome + "/current/lib/compute-publish-diff.js";
|
|
|
70
70
|
const COMPUTE_DIFF = RUN_LIB + "/compute-publish-diff.js";
|
|
71
71
|
const CLASSIFY_SURFACE_SRC = crewHome + "/current/lib/classify-surface.js";
|
|
72
72
|
const CLASSIFY_SURFACE = RUN_LIB + "/classify-surface.js";
|
|
73
|
+
const NOTE_VOCAB_SRC = crewHome + "/current/lib/publish-note-vocabulary.js";
|
|
74
|
+
const NOTE_VOCAB = RUN_LIB + "/publish-note-vocabulary.js";
|
|
73
75
|
// See docs/decisions/qa-reproduce.md#pin-basenames: the pin step materializes the required scripts.
|
|
74
|
-
const PIN_BASENAMES = [LIFECYCLE, MERGE_LOCK, PUBLISH_NPM, CREW_API_PINNED, SCHEMA_SQL_PINNED, COMPUTE_DIFF, CLASSIFY_SURFACE].map(function (p) { return p.split("/").pop(); });
|
|
76
|
+
const PIN_BASENAMES = [LIFECYCLE, MERGE_LOCK, PUBLISH_NPM, CREW_API_PINNED, SCHEMA_SQL_PINNED, COMPUTE_DIFF, CLASSIFY_SURFACE, NOTE_VOCAB].map(function (p) { return p.split("/").pop(); });
|
|
75
77
|
|
|
76
78
|
// Project config — passed by dispatcher, falls back to dashboard defaults
|
|
77
79
|
const projectConfig = inputs.project_config || {};
|
|
@@ -191,19 +193,12 @@ async function reaskVerdict(stepName, reworkSuffix, workerText) {
|
|
|
191
193
|
return verdict;
|
|
192
194
|
}
|
|
193
195
|
// Transport retry: the work-agent agent() call can throw even when the agent
|
|
194
|
-
// did the work
|
|
195
|
-
// the
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
// The verdict re-ask covers an unreadable verdict inside a RECEIVED report;
|
|
201
|
-
// this covers the report never arriving. The assignment is retried boundedly
|
|
202
|
-
// with fresh keys (never a cached replay) before failing closed. Re-entry is
|
|
203
|
-
// safe: lifecycle scripts answer REUSED for existing worktrees/branches, the
|
|
204
|
-
// retry trailer tells the agent to check existing state first and report
|
|
205
|
-
// rather than duplicate completed side effects, and the rework path already
|
|
206
|
-
// re-runs Build after rejection — Build re-entry is an established pattern.
|
|
196
|
+
// did the work — the runtime's JSON-candidate heuristic trips on brace-shaped
|
|
197
|
+
// prose. Retry the assignment boundedly with fresh keys (never a cached
|
|
198
|
+
// replay) before failing closed. Re-entry is safe: lifecycle scripts answer
|
|
199
|
+
// REUSED for existing worktrees/branches, the retry trailer tells the agent
|
|
200
|
+
// to check existing state first, and Build re-entry after rejection is an
|
|
201
|
+
// established pattern. Full history: docs/decisions/workflow-core.md.
|
|
207
202
|
function workRetryKey(stepName, reworkSuffix, attempt) {
|
|
208
203
|
return "work-" + stepName + reworkSuffix + "-t" + attempt;
|
|
209
204
|
}
|
|
@@ -215,7 +210,7 @@ function attemptKey(base, reworkCount) {
|
|
|
215
210
|
function pinLifecycle(key) {
|
|
216
211
|
return agent(
|
|
217
212
|
"Snapshot lifecycle scripts for version pinning.\n" +
|
|
218
|
-
"Run: mkdir -p " + RUN_LIB + " && cp " + LIFECYCLE_SRC + " " + LIFECYCLE + " && cp " + MERGE_LOCK_SRC + " " + MERGE_LOCK + " && cp " + PUBLISH_NPM_SRC + " " + PUBLISH_NPM + " && cp " + CREW_API_SRC + " " + CREW_API_PINNED + " && cp " + SCHEMA_SQL_SRC + " " + SCHEMA_SQL_PINNED + " && cp " + COMPUTE_DIFF_SRC + " " + COMPUTE_DIFF + " && cp " + CLASSIFY_SURFACE_SRC + " " + CLASSIFY_SURFACE + " && chmod +x " + LIFECYCLE + " " + MERGE_LOCK + " " + PUBLISH_NPM + " && ls -1 " + RUN_LIB + "\n" +
|
|
213
|
+
"Run: mkdir -p " + RUN_LIB + " && cp " + LIFECYCLE_SRC + " " + LIFECYCLE + " && cp " + MERGE_LOCK_SRC + " " + MERGE_LOCK + " && cp " + PUBLISH_NPM_SRC + " " + PUBLISH_NPM + " && cp " + CREW_API_SRC + " " + CREW_API_PINNED + " && cp " + SCHEMA_SQL_SRC + " " + SCHEMA_SQL_PINNED + " && cp " + COMPUTE_DIFF_SRC + " " + COMPUTE_DIFF + " && cp " + CLASSIFY_SURFACE_SRC + " " + CLASSIFY_SURFACE + " && cp " + NOTE_VOCAB_SRC + " " + NOTE_VOCAB + " && chmod +x " + LIFECYCLE + " " + MERGE_LOCK + " " + PUBLISH_NPM + " && ls -1 " + RUN_LIB + "\n" +
|
|
219
214
|
"Return the verbatim output of the ls -1 command as { \"listing\": \"<verbatim output>\" } and nothing else.",
|
|
220
215
|
{ key: key, label: "Pinning lifecycle scripts",
|
|
221
216
|
schema: { type: "object", properties: { listing: { type: "string" } }, required: ["listing"] } }
|
|
@@ -396,25 +391,6 @@ function hydrateReleaseDecision(rec) {
|
|
|
396
391
|
return null;
|
|
397
392
|
}
|
|
398
393
|
|
|
399
|
-
// Publish read-back request (currently unavailable): the verbatim_request
|
|
400
|
-
// the parent protocol (docs/publish-verification.md) would hand to an
|
|
401
|
-
// independent read-back tool after the artifact build lands. artifact_inspect
|
|
402
|
-
// was removed by the platform (2026-09-14); artifact.inspect is malfunction
|
|
403
|
-
// diagnosis, not a substitute — so no agent-callable read-back tool exists
|
|
404
|
-
// and this request cannot currently be issued. Pure function — no I/O, no
|
|
405
|
-
// clock. The request carries the merged diff as the expected change and asks
|
|
406
|
-
// for an independent read of the artifact's actual source: for each file, the
|
|
407
|
-
// exact current text of the changed regions plus a per-line present/absent
|
|
408
|
-
// finding. Until a read-back path exists, the parent cannot independently
|
|
409
|
-
// confirm content and verification parks at "publish: verification-requested"
|
|
410
|
-
// (see docs/publish-verification.md). This preserves the circularity break
|
|
411
|
-
// that hollowed canary run 8 (2026-09-11): the old verifyAppliedChanges
|
|
412
|
-
// compared the builder's applied-report against the diff the report was
|
|
413
|
-
// derived from — a fabricated report passed by construction. The report
|
|
414
|
-
// itself is gone now (2026-09-16 fire-and-forget trigger). Independent
|
|
415
|
-
// read-back cannot be
|
|
416
|
-
// fabricated from the diff; it must match the artifact's real content.
|
|
417
|
-
|
|
418
394
|
// See docs/decisions/publish-path.md#publish-attempt-ledger: every trigger outcome is recorded in the durable ledger.
|
|
419
395
|
// keyTag (optional): same-outcome ledger writes in one pass need distinct
|
|
420
396
|
// agent-call keys — the runtime replays cached calls by key, so a shared key
|
|
@@ -433,7 +409,10 @@ async function recordPublishLedger(entry, rework, keyTag) {
|
|
|
433
409
|
applied_report: entry.applied_report || null,
|
|
434
410
|
manifest_before: entry.manifest_before || null,
|
|
435
411
|
outcome: entry.outcome,
|
|
436
|
-
detail: entry.detail || ""
|
|
412
|
+
detail: entry.detail || "",
|
|
413
|
+
// D1 (2026-09-19): issued_at = upper bound on the trigger-issuance
|
|
414
|
+
// instant (null when no trigger); ts = ledger-write instant.
|
|
415
|
+
issued_at: entry.issued_at || null,
|
|
437
416
|
});
|
|
438
417
|
var sq = function(s) { return "'" + String(s).split("'").join("'\\''") + "'"; };
|
|
439
418
|
var res = await agent(
|
|
@@ -1665,6 +1644,23 @@ while (i < STEPS.length) {
|
|
|
1665
1644
|
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
1645
|
triggerThrew = true;
|
|
1667
1646
|
}
|
|
1647
|
+
// D1 (2026-09-19; F-A1 upper-bound 2026-09-20): issued_at is an UPPER
|
|
1648
|
+
// bound on issuance — captured AFTER the trigger-agent call returns,
|
|
1649
|
+
// ferried from one shell date call (workflows have no clock) and
|
|
1650
|
+
// validated mechanically; null when unobserved (fallback: ts).
|
|
1651
|
+
// See docs/decisions/publish-path.md#d1-issuance-time.
|
|
1652
|
+
var triggerIssuedAt = null;
|
|
1653
|
+
try {
|
|
1654
|
+
var issuedAtFerry = await agent(
|
|
1655
|
+
"Run: date -u +%Y-%m-%dT%H:%M:%SZ\n" +
|
|
1656
|
+
"Return JSON { \"ts\": \"<the date output, verbatim>\" } and nothing else.",
|
|
1657
|
+
{ key: attemptKey("publish-issued-at-" + taskId, totalReworkCount), label: "Capturing post-trigger instant (upper bound on issuance)",
|
|
1658
|
+
schema: { type: "object", properties: { ts: { type: "string" } }, required: ["ts"] } }
|
|
1659
|
+
);
|
|
1660
|
+
var issuedAtRaw = String((issuedAtFerry && issuedAtFerry.ts) || "").trim();
|
|
1661
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(issuedAtRaw)) triggerIssuedAt = issuedAtRaw;
|
|
1662
|
+
} catch (issuedAtErr) { /* issued_at stays null: the verifier falls back to ts */ }
|
|
1663
|
+
if (!triggerIssuedAt) log("Publish issuance instant unobserved for task " + taskId + " — issued_at null; the freshness gate falls back to ledger-write ts");
|
|
1668
1664
|
// (2026-09-19, room #22) Submitted-on-issuance: see docs/decisions/publish-path.md#submitted-on-issuance.
|
|
1669
1665
|
await recordPublishLedger({
|
|
1670
1666
|
commit: mergeCommitForPublish,
|
|
@@ -1673,6 +1669,7 @@ while (i < STEPS.length) {
|
|
|
1673
1669
|
applied_report: publishAppliedObservation,
|
|
1674
1670
|
manifest_before: preTriggerManifest,
|
|
1675
1671
|
outcome: "submitted",
|
|
1672
|
+
issued_at: triggerIssuedAt,
|
|
1676
1673
|
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
1674
|
}, totalReworkCount, "issuance");
|
|
1678
1675
|
// Post-trigger observation (primary, not fallback): the workflow
|
|
@@ -1794,6 +1791,7 @@ while (i < STEPS.length) {
|
|
|
1794
1791
|
applied_report: publishAppliedObservation,
|
|
1795
1792
|
manifest_before: preTriggerManifest,
|
|
1796
1793
|
outcome: "submitted",
|
|
1794
|
+
issued_at: triggerIssuedAt,
|
|
1797
1795
|
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff); observation, not re-issuance"
|
|
1798
1796
|
}, totalReworkCount, "receipt");
|
|
1799
1797
|
} else {
|
package/workflows/chore.js
CHANGED
|
@@ -127,8 +127,10 @@ const COMPUTE_DIFF_SRC = crewHome + "/current/lib/compute-publish-diff.js";
|
|
|
127
127
|
const COMPUTE_DIFF = RUN_LIB + "/compute-publish-diff.js";
|
|
128
128
|
const CLASSIFY_SURFACE_SRC = crewHome + "/current/lib/classify-surface.js";
|
|
129
129
|
const CLASSIFY_SURFACE = RUN_LIB + "/classify-surface.js";
|
|
130
|
+
const NOTE_VOCAB_SRC = crewHome + "/current/lib/publish-note-vocabulary.js";
|
|
131
|
+
const NOTE_VOCAB = RUN_LIB + "/publish-note-vocabulary.js";
|
|
130
132
|
// See docs/decisions/qa-reproduce.md#pin-basenames: the pin step materializes the required scripts.
|
|
131
|
-
const PIN_BASENAMES = [LIFECYCLE, MERGE_LOCK, PUBLISH_NPM, CREW_API_PINNED, SCHEMA_SQL_PINNED, COMPUTE_DIFF, CLASSIFY_SURFACE].map(function (p) { return p.split("/").pop(); });
|
|
133
|
+
const PIN_BASENAMES = [LIFECYCLE, MERGE_LOCK, PUBLISH_NPM, CREW_API_PINNED, SCHEMA_SQL_PINNED, COMPUTE_DIFF, CLASSIFY_SURFACE, NOTE_VOCAB].map(function (p) { return p.split("/").pop(); });
|
|
132
134
|
|
|
133
135
|
// Project config — passed by dispatcher, falls back to dashboard defaults
|
|
134
136
|
const projectConfig = inputs.project_config || {};
|
|
@@ -250,19 +252,12 @@ async function reaskVerdict(stepName, reworkSuffix, workerText) {
|
|
|
250
252
|
return verdict;
|
|
251
253
|
}
|
|
252
254
|
// Transport retry: the work-agent agent() call can throw even when the agent
|
|
253
|
-
// did the work
|
|
254
|
-
// the
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
// The verdict re-ask covers an unreadable verdict inside a RECEIVED report;
|
|
260
|
-
// this covers the report never arriving. The assignment is retried boundedly
|
|
261
|
-
// with fresh keys (never a cached replay) before failing closed. Re-entry is
|
|
262
|
-
// safe: lifecycle scripts answer REUSED for existing worktrees/branches, the
|
|
263
|
-
// retry trailer tells the agent to check existing state first and report
|
|
264
|
-
// rather than duplicate completed side effects, and the rework path already
|
|
265
|
-
// re-runs Build after rejection — Build re-entry is an established pattern.
|
|
255
|
+
// did the work — the runtime's JSON-candidate heuristic trips on brace-shaped
|
|
256
|
+
// prose. Retry the assignment boundedly with fresh keys (never a cached
|
|
257
|
+
// replay) before failing closed. Re-entry is safe: lifecycle scripts answer
|
|
258
|
+
// REUSED for existing worktrees/branches, the retry trailer tells the agent
|
|
259
|
+
// to check existing state first, and Build re-entry after rejection is an
|
|
260
|
+
// established pattern. Full history: docs/decisions/workflow-core.md.
|
|
266
261
|
function workRetryKey(stepName, reworkSuffix, attempt) {
|
|
267
262
|
return "work-" + stepName + reworkSuffix + "-t" + attempt;
|
|
268
263
|
}
|
|
@@ -274,7 +269,7 @@ function attemptKey(base, reworkCount) {
|
|
|
274
269
|
function pinLifecycle(key) {
|
|
275
270
|
return agent(
|
|
276
271
|
"Snapshot lifecycle scripts for version pinning.\n" +
|
|
277
|
-
"Run: mkdir -p " + RUN_LIB + " && cp " + LIFECYCLE_SRC + " " + LIFECYCLE + " && cp " + MERGE_LOCK_SRC + " " + MERGE_LOCK + " && cp " + PUBLISH_NPM_SRC + " " + PUBLISH_NPM + " && cp " + CREW_API_SRC + " " + CREW_API_PINNED + " && cp " + SCHEMA_SQL_SRC + " " + SCHEMA_SQL_PINNED + " && cp " + COMPUTE_DIFF_SRC + " " + COMPUTE_DIFF + " && cp " + CLASSIFY_SURFACE_SRC + " " + CLASSIFY_SURFACE + " && chmod +x " + LIFECYCLE + " " + MERGE_LOCK + " " + PUBLISH_NPM + " && ls -1 " + RUN_LIB + "\n" +
|
|
272
|
+
"Run: mkdir -p " + RUN_LIB + " && cp " + LIFECYCLE_SRC + " " + LIFECYCLE + " && cp " + MERGE_LOCK_SRC + " " + MERGE_LOCK + " && cp " + PUBLISH_NPM_SRC + " " + PUBLISH_NPM + " && cp " + CREW_API_SRC + " " + CREW_API_PINNED + " && cp " + SCHEMA_SQL_SRC + " " + SCHEMA_SQL_PINNED + " && cp " + COMPUTE_DIFF_SRC + " " + COMPUTE_DIFF + " && cp " + CLASSIFY_SURFACE_SRC + " " + CLASSIFY_SURFACE + " && cp " + NOTE_VOCAB_SRC + " " + NOTE_VOCAB + " && chmod +x " + LIFECYCLE + " " + MERGE_LOCK + " " + PUBLISH_NPM + " && ls -1 " + RUN_LIB + "\n" +
|
|
278
273
|
"Return the verbatim output of the ls -1 command as { \"listing\": \"<verbatim output>\" } and nothing else.",
|
|
279
274
|
{ key: key, label: "Pinning lifecycle scripts",
|
|
280
275
|
schema: { type: "object", properties: { listing: { type: "string" } }, required: ["listing"] } }
|
|
@@ -455,25 +450,6 @@ function hydrateReleaseDecision(rec) {
|
|
|
455
450
|
return null;
|
|
456
451
|
}
|
|
457
452
|
|
|
458
|
-
// Publish read-back request (currently unavailable): the verbatim_request
|
|
459
|
-
// the parent protocol (docs/publish-verification.md) would hand to an
|
|
460
|
-
// independent read-back tool after the artifact build lands. artifact_inspect
|
|
461
|
-
// was removed by the platform (2026-09-14); artifact.inspect is malfunction
|
|
462
|
-
// diagnosis, not a substitute — so no agent-callable read-back tool exists
|
|
463
|
-
// and this request cannot currently be issued. Pure function — no I/O, no
|
|
464
|
-
// clock. The request carries the merged diff as the expected change and asks
|
|
465
|
-
// for an independent read of the artifact's actual source: for each file, the
|
|
466
|
-
// exact current text of the changed regions plus a per-line present/absent
|
|
467
|
-
// finding. Until a read-back path exists, the parent cannot independently
|
|
468
|
-
// confirm content and verification parks at "publish: verification-requested"
|
|
469
|
-
// (see docs/publish-verification.md). This preserves the circularity break
|
|
470
|
-
// that hollowed canary run 8 (2026-09-11): the old verifyAppliedChanges
|
|
471
|
-
// compared the builder's applied-report against the diff the report was
|
|
472
|
-
// derived from — a fabricated report passed by construction. The report
|
|
473
|
-
// itself is gone now (2026-09-16 fire-and-forget trigger). Independent
|
|
474
|
-
// read-back cannot be
|
|
475
|
-
// fabricated from the diff; it must match the artifact's real content.
|
|
476
|
-
|
|
477
453
|
// See docs/decisions/publish-path.md#publish-attempt-ledger: every trigger outcome is recorded in the durable ledger.
|
|
478
454
|
// keyTag (optional): same-outcome ledger writes in one pass need distinct
|
|
479
455
|
// agent-call keys — the runtime replays cached calls by key, so a shared key
|
|
@@ -492,7 +468,10 @@ async function recordPublishLedger(entry, rework, keyTag) {
|
|
|
492
468
|
applied_report: entry.applied_report || null,
|
|
493
469
|
manifest_before: entry.manifest_before || null,
|
|
494
470
|
outcome: entry.outcome,
|
|
495
|
-
detail: entry.detail || ""
|
|
471
|
+
detail: entry.detail || "",
|
|
472
|
+
// D1 (2026-09-19): issued_at = upper bound on the trigger-issuance
|
|
473
|
+
// instant (null when no trigger); ts = ledger-write instant.
|
|
474
|
+
issued_at: entry.issued_at || null,
|
|
496
475
|
});
|
|
497
476
|
var sq = function(s) { return "'" + String(s).split("'").join("'\\''") + "'"; };
|
|
498
477
|
var res = await agent(
|
|
@@ -1571,6 +1550,23 @@ while (i < STEPS.length) {
|
|
|
1571
1550
|
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
1551
|
triggerThrew = true;
|
|
1573
1552
|
}
|
|
1553
|
+
// D1 (2026-09-19; F-A1 upper-bound 2026-09-20): issued_at is an UPPER
|
|
1554
|
+
// bound on issuance — captured AFTER the trigger-agent call returns,
|
|
1555
|
+
// ferried from one shell date call (workflows have no clock) and
|
|
1556
|
+
// validated mechanically; null when unobserved (fallback: ts).
|
|
1557
|
+
// See docs/decisions/publish-path.md#d1-issuance-time.
|
|
1558
|
+
var triggerIssuedAt = null;
|
|
1559
|
+
try {
|
|
1560
|
+
var issuedAtFerry = await agent(
|
|
1561
|
+
"Run: date -u +%Y-%m-%dT%H:%M:%SZ\n" +
|
|
1562
|
+
"Return JSON { \"ts\": \"<the date output, verbatim>\" } and nothing else.",
|
|
1563
|
+
{ key: attemptKey("publish-issued-at-" + taskId, reworkCount), label: "Capturing post-trigger instant (upper bound on issuance)",
|
|
1564
|
+
schema: { type: "object", properties: { ts: { type: "string" } }, required: ["ts"] } }
|
|
1565
|
+
);
|
|
1566
|
+
var issuedAtRaw = String((issuedAtFerry && issuedAtFerry.ts) || "").trim();
|
|
1567
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(issuedAtRaw)) triggerIssuedAt = issuedAtRaw;
|
|
1568
|
+
} catch (issuedAtErr) { /* issued_at stays null: the verifier falls back to ts */ }
|
|
1569
|
+
if (!triggerIssuedAt) log("Publish issuance instant unobserved for task " + taskId + " — issued_at null; the freshness gate falls back to ledger-write ts");
|
|
1574
1570
|
// (2026-09-19, room #22) Submitted-on-issuance: see docs/decisions/publish-path.md#submitted-on-issuance.
|
|
1575
1571
|
await recordPublishLedger({
|
|
1576
1572
|
commit: mergeCommitForPublish,
|
|
@@ -1579,6 +1575,7 @@ while (i < STEPS.length) {
|
|
|
1579
1575
|
applied_report: publishAppliedObservation,
|
|
1580
1576
|
manifest_before: preTriggerManifest,
|
|
1581
1577
|
outcome: "submitted",
|
|
1578
|
+
issued_at: triggerIssuedAt,
|
|
1582
1579
|
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
1580
|
}, reworkCount, "issuance");
|
|
1584
1581
|
// Post-trigger observation (primary, not fallback): the workflow
|
|
@@ -1700,6 +1697,7 @@ while (i < STEPS.length) {
|
|
|
1700
1697
|
applied_report: publishAppliedObservation,
|
|
1701
1698
|
manifest_before: preTriggerManifest,
|
|
1702
1699
|
outcome: "submitted",
|
|
1700
|
+
issued_at: triggerIssuedAt,
|
|
1703
1701
|
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff); observation, not re-issuance"
|
|
1704
1702
|
}, reworkCount, "receipt");
|
|
1705
1703
|
} else {
|
package/workflows/standard.js
CHANGED
|
@@ -78,8 +78,10 @@ const COMPUTE_DIFF_SRC = crewHome + "/current/lib/compute-publish-diff.js";
|
|
|
78
78
|
const COMPUTE_DIFF = RUN_LIB + "/compute-publish-diff.js";
|
|
79
79
|
const CLASSIFY_SURFACE_SRC = crewHome + "/current/lib/classify-surface.js";
|
|
80
80
|
const CLASSIFY_SURFACE = RUN_LIB + "/classify-surface.js";
|
|
81
|
+
const NOTE_VOCAB_SRC = crewHome + "/current/lib/publish-note-vocabulary.js";
|
|
82
|
+
const NOTE_VOCAB = RUN_LIB + "/publish-note-vocabulary.js";
|
|
81
83
|
// See docs/decisions/qa-reproduce.md#pin-basenames: the pin step materializes the required scripts.
|
|
82
|
-
const PIN_BASENAMES = [LIFECYCLE, MERGE_LOCK, PUBLISH_NPM, CREW_API_PINNED, SCHEMA_SQL_PINNED, COMPUTE_DIFF, CLASSIFY_SURFACE].map(function (p) { return p.split("/").pop(); });
|
|
84
|
+
const PIN_BASENAMES = [LIFECYCLE, MERGE_LOCK, PUBLISH_NPM, CREW_API_PINNED, SCHEMA_SQL_PINNED, COMPUTE_DIFF, CLASSIFY_SURFACE, NOTE_VOCAB].map(function (p) { return p.split("/").pop(); });
|
|
83
85
|
|
|
84
86
|
// Project config — passed by dispatcher, falls back to dashboard defaults
|
|
85
87
|
const projectConfig = inputs.project_config || {};
|
|
@@ -199,19 +201,12 @@ async function reaskVerdict(stepName, reworkSuffix, workerText) {
|
|
|
199
201
|
return verdict;
|
|
200
202
|
}
|
|
201
203
|
// Transport retry: the work-agent agent() call can throw even when the agent
|
|
202
|
-
// did the work
|
|
203
|
-
// the
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
// The verdict re-ask covers an unreadable verdict inside a RECEIVED report;
|
|
209
|
-
// this covers the report never arriving. The assignment is retried boundedly
|
|
210
|
-
// with fresh keys (never a cached replay) before failing closed. Re-entry is
|
|
211
|
-
// safe: lifecycle scripts answer REUSED for existing worktrees/branches, the
|
|
212
|
-
// retry trailer tells the agent to check existing state first and report
|
|
213
|
-
// rather than duplicate completed side effects, and the rework path already
|
|
214
|
-
// re-runs Build after rejection — Build re-entry is an established pattern.
|
|
204
|
+
// did the work — the runtime's JSON-candidate heuristic trips on brace-shaped
|
|
205
|
+
// prose. Retry the assignment boundedly with fresh keys (never a cached
|
|
206
|
+
// replay) before failing closed. Re-entry is safe: lifecycle scripts answer
|
|
207
|
+
// REUSED for existing worktrees/branches, the retry trailer tells the agent
|
|
208
|
+
// to check existing state first, and Build re-entry after rejection is an
|
|
209
|
+
// established pattern. Full history: docs/decisions/workflow-core.md.
|
|
215
210
|
function workRetryKey(stepName, reworkSuffix, attempt) {
|
|
216
211
|
return "work-" + stepName + reworkSuffix + "-t" + attempt;
|
|
217
212
|
}
|
|
@@ -223,7 +218,7 @@ function attemptKey(base, reworkCount) {
|
|
|
223
218
|
function pinLifecycle(key) {
|
|
224
219
|
return agent(
|
|
225
220
|
"Snapshot lifecycle scripts for version pinning.\n" +
|
|
226
|
-
"Run: mkdir -p " + RUN_LIB + " && cp " + LIFECYCLE_SRC + " " + LIFECYCLE + " && cp " + MERGE_LOCK_SRC + " " + MERGE_LOCK + " && cp " + PUBLISH_NPM_SRC + " " + PUBLISH_NPM + " && cp " + CREW_API_SRC + " " + CREW_API_PINNED + " && cp " + SCHEMA_SQL_SRC + " " + SCHEMA_SQL_PINNED + " && cp " + COMPUTE_DIFF_SRC + " " + COMPUTE_DIFF + " && cp " + CLASSIFY_SURFACE_SRC + " " + CLASSIFY_SURFACE + " && chmod +x " + LIFECYCLE + " " + MERGE_LOCK + " " + PUBLISH_NPM + " && ls -1 " + RUN_LIB + "\n" +
|
|
221
|
+
"Run: mkdir -p " + RUN_LIB + " && cp " + LIFECYCLE_SRC + " " + LIFECYCLE + " && cp " + MERGE_LOCK_SRC + " " + MERGE_LOCK + " && cp " + PUBLISH_NPM_SRC + " " + PUBLISH_NPM + " && cp " + CREW_API_SRC + " " + CREW_API_PINNED + " && cp " + SCHEMA_SQL_SRC + " " + SCHEMA_SQL_PINNED + " && cp " + COMPUTE_DIFF_SRC + " " + COMPUTE_DIFF + " && cp " + CLASSIFY_SURFACE_SRC + " " + CLASSIFY_SURFACE + " && cp " + NOTE_VOCAB_SRC + " " + NOTE_VOCAB + " && chmod +x " + LIFECYCLE + " " + MERGE_LOCK + " " + PUBLISH_NPM + " && ls -1 " + RUN_LIB + "\n" +
|
|
227
222
|
"Return the verbatim output of the ls -1 command as { \"listing\": \"<verbatim output>\" } and nothing else.",
|
|
228
223
|
{ key: key, label: "Pinning lifecycle scripts",
|
|
229
224
|
schema: { type: "object", properties: { listing: { type: "string" } }, required: ["listing"] } }
|
|
@@ -445,7 +440,10 @@ async function recordPublishLedger(entry, rework, keyTag) {
|
|
|
445
440
|
applied_report: entry.applied_report || null,
|
|
446
441
|
manifest_before: entry.manifest_before || null,
|
|
447
442
|
outcome: entry.outcome,
|
|
448
|
-
detail: entry.detail || ""
|
|
443
|
+
detail: entry.detail || "",
|
|
444
|
+
// D1 (2026-09-19): issued_at = upper bound on the trigger-issuance
|
|
445
|
+
// instant (null when no trigger); ts = ledger-write instant.
|
|
446
|
+
issued_at: entry.issued_at || null,
|
|
449
447
|
});
|
|
450
448
|
var sq = function(s) { return "'" + String(s).split("'").join("'\\''") + "'"; };
|
|
451
449
|
var res = await agent(
|
|
@@ -1558,6 +1556,23 @@ while (i < STEPS.length) {
|
|
|
1558
1556
|
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
1557
|
triggerThrew = true;
|
|
1560
1558
|
}
|
|
1559
|
+
// D1 (2026-09-19; F-A1 upper-bound 2026-09-20): issued_at is an UPPER
|
|
1560
|
+
// bound on issuance — captured AFTER the trigger-agent call returns,
|
|
1561
|
+
// ferried from one shell date call (workflows have no clock) and
|
|
1562
|
+
// validated mechanically; null when unobserved (fallback: ts).
|
|
1563
|
+
// See docs/decisions/publish-path.md#d1-issuance-time.
|
|
1564
|
+
var triggerIssuedAt = null;
|
|
1565
|
+
try {
|
|
1566
|
+
var issuedAtFerry = await agent(
|
|
1567
|
+
"Run: date -u +%Y-%m-%dT%H:%M:%SZ\n" +
|
|
1568
|
+
"Return JSON { \"ts\": \"<the date output, verbatim>\" } and nothing else.",
|
|
1569
|
+
{ key: attemptKey("publish-issued-at-" + taskId, totalReworkCount), label: "Capturing post-trigger instant (upper bound on issuance)",
|
|
1570
|
+
schema: { type: "object", properties: { ts: { type: "string" } }, required: ["ts"] } }
|
|
1571
|
+
);
|
|
1572
|
+
var issuedAtRaw = String((issuedAtFerry && issuedAtFerry.ts) || "").trim();
|
|
1573
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(issuedAtRaw)) triggerIssuedAt = issuedAtRaw;
|
|
1574
|
+
} catch (issuedAtErr) { /* issued_at stays null: the verifier falls back to ts */ }
|
|
1575
|
+
if (!triggerIssuedAt) log("Publish issuance instant unobserved for task " + taskId + " — issued_at null; the freshness gate falls back to ledger-write ts");
|
|
1561
1576
|
// (2026-09-19, room #22) Submitted-on-issuance: see docs/decisions/publish-path.md#submitted-on-issuance.
|
|
1562
1577
|
await recordPublishLedger({
|
|
1563
1578
|
commit: mergeCommitForPublish,
|
|
@@ -1566,6 +1581,7 @@ while (i < STEPS.length) {
|
|
|
1566
1581
|
applied_report: publishAppliedObservation,
|
|
1567
1582
|
manifest_before: preTriggerManifest,
|
|
1568
1583
|
outcome: "submitted",
|
|
1584
|
+
issued_at: triggerIssuedAt,
|
|
1569
1585
|
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
1586
|
}, totalReworkCount, "issuance");
|
|
1571
1587
|
// Post-trigger observation (primary, not fallback): the workflow
|
|
@@ -1687,6 +1703,7 @@ while (i < STEPS.length) {
|
|
|
1687
1703
|
applied_report: publishAppliedObservation,
|
|
1688
1704
|
manifest_before: preTriggerManifest,
|
|
1689
1705
|
outcome: "submitted",
|
|
1706
|
+
issued_at: triggerIssuedAt,
|
|
1690
1707
|
detail: "fire-and-forget trigger; build receipt captured by workflow-owned build-state observation (pre/post-trigger diff); observation, not re-issuance"
|
|
1691
1708
|
}, totalReworkCount, "receipt");
|
|
1692
1709
|
} else {
|
package/workflows/upgrade.js
CHANGED
|
@@ -165,12 +165,14 @@ const SCHEMA_SQL_SRC = crewHome + "/lib/schema.sql";
|
|
|
165
165
|
const SCHEMA_SQL_PINNED = RUN_LIB + "/schema.sql";
|
|
166
166
|
const CLASSIFY_SURFACE_SRC = crewHome + "/current/lib/classify-surface.js";
|
|
167
167
|
const CLASSIFY_SURFACE = RUN_LIB + "/classify-surface.js";
|
|
168
|
+
const NOTE_VOCAB_SRC = crewHome + "/current/lib/publish-note-vocabulary.js";
|
|
169
|
+
const NOTE_VOCAB = RUN_LIB + "/publish-note-vocabulary.js";
|
|
168
170
|
// The six basenames the pin step must materialize — asserted mechanically
|
|
169
171
|
// by workflow code from the verbatim listing, never from agent prose.
|
|
170
172
|
// CLASSIFY_SURFACE is the surface classifier (room #15, 2026-09-18):
|
|
171
173
|
// crew-api.js statically imports it, so the pin must carry it — a pin
|
|
172
174
|
// without it kills every claim with ERR_MODULE_NOT_FOUND.
|
|
173
|
-
const PIN_BASENAMES = [LIFECYCLE, MERGE_LOCK, PUBLISH_NPM, CREW_API_PINNED, SCHEMA_SQL_PINNED, CLASSIFY_SURFACE].map(function (p) { return p.split("/").pop(); });
|
|
175
|
+
const PIN_BASENAMES = [LIFECYCLE, MERGE_LOCK, PUBLISH_NPM, CREW_API_PINNED, SCHEMA_SQL_PINNED, CLASSIFY_SURFACE, NOTE_VOCAB].map(function (p) { return p.split("/").pop(); });
|
|
174
176
|
|
|
175
177
|
// Project config — passed by dispatcher, falls back to dashboard defaults
|
|
176
178
|
const projectConfig = inputs.project_config || {};
|
|
@@ -201,7 +203,7 @@ if (!taskId) {
|
|
|
201
203
|
function pinLifecycle(key) {
|
|
202
204
|
return agent(
|
|
203
205
|
"Snapshot lifecycle scripts for version pinning.\n" +
|
|
204
|
-
"Run: mkdir -p " + sq(RUN_LIB) + " && cp " + sq(LIFECYCLE_SRC) + " " + sq(LIFECYCLE) + " && cp " + sq(MERGE_LOCK_SRC) + " " + sq(MERGE_LOCK) + " && cp " + sq(PUBLISH_NPM_SRC) + " " + sq(PUBLISH_NPM) + " && cp " + sq(CREW_API_SRC) + " " + sq(CREW_API_PINNED) + " && cp " + sq(SCHEMA_SQL_SRC) + " " + sq(SCHEMA_SQL_PINNED) + " && cp " + sq(CLASSIFY_SURFACE_SRC) + " " + sq(CLASSIFY_SURFACE) + " && chmod +x " + sq(LIFECYCLE) + " " + sq(MERGE_LOCK) + " " + sq(PUBLISH_NPM) + " && ls -1 " + sq(RUN_LIB) + "\n" +
|
|
206
|
+
"Run: mkdir -p " + sq(RUN_LIB) + " && cp " + sq(LIFECYCLE_SRC) + " " + sq(LIFECYCLE) + " && cp " + sq(MERGE_LOCK_SRC) + " " + sq(MERGE_LOCK) + " && cp " + sq(PUBLISH_NPM_SRC) + " " + sq(PUBLISH_NPM) + " && cp " + sq(CREW_API_SRC) + " " + sq(CREW_API_PINNED) + " && cp " + sq(SCHEMA_SQL_SRC) + " " + sq(SCHEMA_SQL_PINNED) + " && cp " + sq(CLASSIFY_SURFACE_SRC) + " " + sq(CLASSIFY_SURFACE) + " && cp " + sq(NOTE_VOCAB_SRC) + " " + sq(NOTE_VOCAB) + " && chmod +x " + sq(LIFECYCLE) + " " + sq(MERGE_LOCK) + " " + sq(PUBLISH_NPM) + " && ls -1 " + sq(RUN_LIB) + "\n" +
|
|
205
207
|
"Return the verbatim output of the ls -1 command as { \"listing\": \"<verbatim output>\" } and nothing else.",
|
|
206
208
|
{ key: key, label: "Pinning lifecycle scripts",
|
|
207
209
|
schema: { type: "object", properties: { listing: { type: "string" } }, required: ["listing"] } }
|