muse-crew 0.7.14 → 0.7.15
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/lib/crew-api.js +47 -2
- package/package.json +1 -1
- package/workflows/bugfix.js +6 -2
- package/workflows/chore.js +6 -2
- package/workflows/docs.js +6 -2
- package/workflows/standard.js +56 -5
package/lib/crew-api.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { readFileSync, existsSync, statSync, readlinkSync, readdirSync, appendFileSync } from "node:fs";
|
|
26
26
|
import { join, resolve, sep, basename } from "node:path";
|
|
27
27
|
import { randomUUID } from "node:crypto";
|
|
28
|
+
import { execFileSync } from "node:child_process";
|
|
28
29
|
import { DatabaseSync } from "node:sqlite";
|
|
29
30
|
import { homedir } from "node:os";
|
|
30
31
|
|
|
@@ -1540,7 +1541,50 @@ commands["record-phase"] = (db, args) => {
|
|
|
1540
1541
|
return { session: mapSession(sessionRow), event: mapEvent(eventRow) };
|
|
1541
1542
|
};
|
|
1542
1543
|
|
|
1543
|
-
|
|
1544
|
+
// Initial provenance stamp (2026-09-16, clean-room task ada95b71): the first
|
|
1545
|
+
// publish of an artifact project fell back to the empty-tree SHA as its diff
|
|
1546
|
+
// base because no provenance was ever stamped, turning the publish diff into
|
|
1547
|
+
// the whole repo tree (52 files / 4768 lines / 228KB) — unparseable through
|
|
1548
|
+
// the JSON transport and over the 200-line budget. The artifact is built from
|
|
1549
|
+
// the repo checkout at registration time, so create-project stamps
|
|
1550
|
+
// source_commit = repo HEAD for artifact-deploy projects; the first publish
|
|
1551
|
+
// then diffs only the task's own changes. The stamp never clobbers an
|
|
1552
|
+
// existing one (provenance storage is crew-home-global, so a second artifact
|
|
1553
|
+
// project keeps the first stamp — per-project provenance is a known gap this
|
|
1554
|
+
// does not introduce). A skipped stamp is reported, never silent, and never
|
|
1555
|
+
// fails project creation.
|
|
1556
|
+
function initialProvenancePlan({ deployType, provenanceExists, headSha, releaseName }) {
|
|
1557
|
+
if (deployType !== "artifact") return { stamped: false, reason: "deploy_type is not artifact" };
|
|
1558
|
+
if (provenanceExists) return { stamped: false, reason: "provenance already stamped" };
|
|
1559
|
+
if (!headSha) return { stamped: false, reason: "repo HEAD is unresolvable (unborn repo?)" };
|
|
1560
|
+
if (!releaseName) return { stamped: false, reason: "active crew release is unresolvable" };
|
|
1561
|
+
return { stamped: true, source_commit: headSha, crew_release: releaseName };
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
function tryStampInitialProvenance(db, crewHome, repoPath, deployType) {
|
|
1565
|
+
const keys = db.prepare(
|
|
1566
|
+
"SELECT key FROM config WHERE key IN ('provenance.source_commit','provenance.crew_release','provenance.published_at')"
|
|
1567
|
+
).all().map((r) => r.key);
|
|
1568
|
+
const provenanceExists = keys.length === 3;
|
|
1569
|
+
let headSha = null;
|
|
1570
|
+
try {
|
|
1571
|
+
headSha = execFileSync("git", ["rev-parse", "HEAD"],
|
|
1572
|
+
{ cwd: repoPath, encoding: "utf8", timeout: 10000 }).trim();
|
|
1573
|
+
if (!/^[0-9a-f]{40}$/.test(headSha)) headSha = null;
|
|
1574
|
+
} catch { headSha = null; }
|
|
1575
|
+
let releaseName = null;
|
|
1576
|
+
try { releaseName = resolveActiveRelease(crewHome); } catch { releaseName = null; }
|
|
1577
|
+
const plan = initialProvenancePlan({ deployType, provenanceExists, headSha, releaseName });
|
|
1578
|
+
if (!plan.stamped) return plan;
|
|
1579
|
+
const upsert = db.prepare(
|
|
1580
|
+
"INSERT INTO config (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value");
|
|
1581
|
+
upsert.run("provenance.source_commit", plan.source_commit);
|
|
1582
|
+
upsert.run("provenance.crew_release", plan.crew_release);
|
|
1583
|
+
upsert.run("provenance.published_at", now());
|
|
1584
|
+
return plan;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
commands["create-project"] = (db, args, ctx) => {
|
|
1544
1588
|
const id = (args.id ?? "").trim();
|
|
1545
1589
|
if (!/^[a-z0-9-]+$/.test(id)) throw usageError("id must be a slug.");
|
|
1546
1590
|
const displayName = (args.display_name ?? "").trim();
|
|
@@ -1572,7 +1616,8 @@ commands["create-project"] = (db, args) => {
|
|
|
1572
1616
|
description, simultaneity, quiesced, visual_protocol, created_at, updated_at)
|
|
1573
1617
|
VALUES (@id, @display_name, @repo_path, @deploy_type, @deploy_slug,
|
|
1574
1618
|
@description, @simultaneity, @quiesced, @visual_protocol, @created_at, @updated_at)`).run(row);
|
|
1575
|
-
|
|
1619
|
+
const initialProvenance = tryStampInitialProvenance(db, ctx.crewHome, repoPath, deployType);
|
|
1620
|
+
return { project: mapProject(row), initial_provenance: initialProvenance };
|
|
1576
1621
|
};
|
|
1577
1622
|
|
|
1578
1623
|
commands["get-project"] = (db, args) => {
|
package/package.json
CHANGED
package/workflows/bugfix.js
CHANGED
|
@@ -50,8 +50,12 @@ const CLAIM_WORKFLOW_PERSIST = (WORKFLOW_WAS_NULL && RESOLVED_WORKFLOW) ? ", \"w
|
|
|
50
50
|
// Manual launches without the arg default to off (previous behavior).
|
|
51
51
|
var VISUAL_PROTOCOL_AVAILABLE = inputs.visual_protocol === true;
|
|
52
52
|
|
|
53
|
-
//
|
|
54
|
-
|
|
53
|
+
// crewHome is required — the dispatcher always passes it (crew-dispatch.js
|
|
54
|
+
// throws without it). Fail closed instead of silently defaulting to the dev
|
|
55
|
+
// home: a missing home is a loud error, a wrong home is silent corruption
|
|
56
|
+
// (2026-09-16: the silent default let a run resolve against the dev home).
|
|
57
|
+
if (!inputs.crewHome) throw new Error("crewHome is required — pass the crew home explicitly; no default");
|
|
58
|
+
const crewHome = inputs.crewHome;
|
|
55
59
|
// Crew API: the workflow calls the crew-owned CLI, not the dashboard.
|
|
56
60
|
// The CLI implements the API.md contract against $CREW_HOME/crew-state.db.
|
|
57
61
|
const CREW_API = crewHome + "/current/lib/crew-api.js";
|
package/workflows/chore.js
CHANGED
|
@@ -47,8 +47,12 @@ const NEXT_PHASE_ROUTED = (typeof inputs.next_phase === "string" && inputs.next_
|
|
|
47
47
|
// Manual launches without the arg default to off (previous behavior).
|
|
48
48
|
var VISUAL_PROTOCOL_AVAILABLE = inputs.visual_protocol === true;
|
|
49
49
|
|
|
50
|
-
//
|
|
51
|
-
|
|
50
|
+
// crewHome is required — the dispatcher always passes it (crew-dispatch.js
|
|
51
|
+
// throws without it). Fail closed instead of silently defaulting to the dev
|
|
52
|
+
// home: a missing home is a loud error, a wrong home is silent corruption
|
|
53
|
+
// (2026-09-16: the silent default let a run resolve against the dev home).
|
|
54
|
+
if (!inputs.crewHome) throw new Error("crewHome is required — pass the crew home explicitly; no default");
|
|
55
|
+
const crewHome = inputs.crewHome;
|
|
52
56
|
// Crew API: the workflow calls the crew-owned CLI, not the dashboard.
|
|
53
57
|
// The CLI implements the API.md contract against $CREW_HOME/crew-state.db.
|
|
54
58
|
const CREW_API = crewHome + "/current/lib/crew-api.js";
|
package/workflows/docs.js
CHANGED
|
@@ -32,8 +32,12 @@ const WORKFLOW_WAS_NULL = inputs.workflow_was_null === true;
|
|
|
32
32
|
// what the dispatcher routed on.
|
|
33
33
|
const NEXT_PHASE_ROUTED = (typeof inputs.next_phase === "string" && inputs.next_phase.length > 0) ? inputs.next_phase : null;
|
|
34
34
|
|
|
35
|
-
//
|
|
36
|
-
|
|
35
|
+
// crewHome is required — the dispatcher always passes it (crew-dispatch.js
|
|
36
|
+
// throws without it). Fail closed instead of silently defaulting to the dev
|
|
37
|
+
// home: a missing home is a loud error, a wrong home is silent corruption
|
|
38
|
+
// (2026-09-16: the silent default let a run resolve against the dev home).
|
|
39
|
+
if (!inputs.crewHome) throw new Error("crewHome is required — pass the crew home explicitly; no default");
|
|
40
|
+
const crewHome = inputs.crewHome;
|
|
37
41
|
// Crew API: the workflow calls the crew-owned CLI, not the dashboard.
|
|
38
42
|
// The CLI implements the API.md contract against $CREW_HOME/crew-state.db.
|
|
39
43
|
const CREW_API = crewHome + "/current/lib/crew-api.js";
|
package/workflows/standard.js
CHANGED
|
@@ -49,8 +49,12 @@ const CLAIM_WORKFLOW_PERSIST = (WORKFLOW_WAS_NULL && RESOLVED_WORKFLOW) ? ", \"w
|
|
|
49
49
|
// Manual launches without the arg default to off (previous behavior).
|
|
50
50
|
var VISUAL_PROTOCOL_AVAILABLE = inputs.visual_protocol === true;
|
|
51
51
|
|
|
52
|
-
//
|
|
53
|
-
|
|
52
|
+
// crewHome is required — the dispatcher always passes it (crew-dispatch.js
|
|
53
|
+
// throws without it). Fail closed instead of silently defaulting to the dev
|
|
54
|
+
// home: a missing home is a loud error, a wrong home is silent corruption
|
|
55
|
+
// (2026-09-16: the silent default let a run resolve against the dev home).
|
|
56
|
+
if (!inputs.crewHome) throw new Error("crewHome is required — pass the crew home explicitly; no default");
|
|
57
|
+
const crewHome = inputs.crewHome;
|
|
54
58
|
// Crew API: the workflow calls the crew-owned CLI, not the dashboard.
|
|
55
59
|
// The CLI implements the API.md contract against $CREW_HOME/crew-state.db.
|
|
56
60
|
const CREW_API = crewHome + "/current/lib/crew-api.js";
|
|
@@ -67,6 +71,14 @@ const ORCH_PATH = crewHome + "/.orchestration";
|
|
|
67
71
|
const LIFECYCLE_SRC = crewHome + "/lib/worktree-lifecycle.sh";
|
|
68
72
|
const MERGE_LOCK_SRC = crewHome + "/lib/merge-lock.sh";
|
|
69
73
|
const RUN_LIB = crewHome + "/.pins/" + taskId; // persistent disk, NOT /tmp (tmpfs wiped by cell reboots — canary b5efd1b1)
|
|
74
|
+
// Deterministic spec path — computed by the workflow, never by the agent.
|
|
75
|
+
// Map writes the spec to exactly SPEC_PATH; the path is handed to the agent
|
|
76
|
+
// as a fact and verified mechanically after the step. Agents must never
|
|
77
|
+
// compose crew-home paths themselves (2026-09-16: a Map agent saved the spec
|
|
78
|
+
// under the platform's ~/workspace/.jarvis/workflow-runs/ run dir because the
|
|
79
|
+
// prompt named a directory and the agent invented the rest of the path).
|
|
80
|
+
const SPEC_DIR = crewHome + "/task-evidence/" + taskId + "/map";
|
|
81
|
+
const SPEC_PATH = SPEC_DIR + "/spec.md";
|
|
70
82
|
const LIFECYCLE = RUN_LIB + "/worktree-lifecycle.sh";
|
|
71
83
|
const MERGE_LOCK = RUN_LIB + "/merge-lock.sh";
|
|
72
84
|
const PUBLISH_NPM_SRC = crewHome + "/lib/publish-npm.sh";
|
|
@@ -1213,7 +1225,7 @@ while (i < STEPS.length) {
|
|
|
1213
1225
|
" If the baseline evidence is missing with no baseline:none recorded, do not write the spec — report 'baseline evidence missing — Map gate bounce required' and stop.\n" +
|
|
1214
1226
|
"Declare capture targets for the post-change visual capture: end your report with a line `capture_targets: <comma-separated views/controls this change affects>` (optional; falls back to the task description).";
|
|
1215
1227
|
}
|
|
1216
|
-
instructions = "Research the problem space, evaluate options, pick the shortest path.\nWrite a clear spec that a builder can execute without asking questions.\nThe builder will edit source files in a git worktree.\nProject: " + PROJECT_DESC + "\nSave the spec to
|
|
1228
|
+
instructions = "Research the problem space, evaluate options, pick the shortest path.\nWrite a clear spec that a builder can execute without asking questions.\nThe builder will edit source files in a git worktree.\nProject: " + PROJECT_DESC + "\nSave the spec to exactly this file: " + SPEC_PATH + " — run mkdir -p \"" + SPEC_DIR + "\" first. Do not save the spec anywhere else; this exact path is fixed and will be checked mechanically after your step.\nReport back in plain prose — what you specified." + mapGatePara;
|
|
1217
1229
|
|
|
1218
1230
|
} else if (step.name === "Build") {
|
|
1219
1231
|
instructions = "STEP 1: Prepare your worktree.\n" +
|
|
@@ -1265,7 +1277,7 @@ while (i < STEPS.length) {
|
|
|
1265
1277
|
}
|
|
1266
1278
|
}
|
|
1267
1279
|
instructions = "Review independently and cold. You have NOT seen any reasoning from the builder.\nDo NOT access the task dashboard, event log, or any comments. Your review is based solely on the spec and the code.\n\n" +
|
|
1268
|
-
(mapperSpec ? "MAPPER'S SPEC (the builder was asked to implement exactly this):\n" + mapperSpec + "\n\n" : "Read the spec (
|
|
1280
|
+
(mapperSpec ? "MAPPER'S SPEC (the builder was asked to implement exactly this):\n" + mapperSpec + "\n\n" : "Read the spec at exactly " + SPEC_PATH + " (fall back to the task description if the file is absent).\n\n") +
|
|
1269
1281
|
"Examine the code changes by running:\n" +
|
|
1270
1282
|
LIFECYCLE_ENV + LIFECYCLE + " inspect " + taskId + "\n\n" +
|
|
1271
1283
|
"The inspect output is authoritative: it prints the task branch's actual tip commit (TIP) and every commit ahead of main. Base your review ONLY on this output — do NOT run git log yourself to pick commits, and do NOT discuss commit hashes from any other source (they may come from stale rework rounds or a different repo).\n\n" +
|
|
@@ -2427,11 +2439,40 @@ while (i < STEPS.length) {
|
|
|
2427
2439
|
}
|
|
2428
2440
|
}
|
|
2429
2441
|
|
|
2442
|
+
// Deterministic spec-path verification: the agent cannot self-certify where
|
|
2443
|
+
// it saved the spec. After Map passes, the workflow confirms mechanically
|
|
2444
|
+
// that the spec file exists at the workflow-computed SPEC_PATH (2026-09-16:
|
|
2445
|
+
// a Map agent saved the spec under the platform's
|
|
2446
|
+
// ~/workspace/.jarvis/workflow-runs/ run dir instead of the crew home,
|
|
2447
|
+
// because the prompt named a directory and the agent composed the path).
|
|
2448
|
+
// A missing spec file is an operational step failure, not a park.
|
|
2449
|
+
if (step.name === "Map" && passed) {
|
|
2450
|
+
var specVerifyOut = "";
|
|
2451
|
+
try {
|
|
2452
|
+
var specVerifyResult = await agent(
|
|
2453
|
+
"Run: test -f \"" + SPEC_PATH + "\" && echo SPEC_PRESENT || echo SPEC_MISSING\n" +
|
|
2454
|
+
"Return JSON { \"output\": \"<the command's full stdout, trimmed>\" } and nothing else.",
|
|
2455
|
+
{ key: attemptKey("verify-spec-" + taskId, totalReworkCount), label: "Verifying spec file landed",
|
|
2456
|
+
schema: { type: "object", properties: { output: { type: "string" } }, required: ["output"] } }
|
|
2457
|
+
);
|
|
2458
|
+
specVerifyOut = (specVerifyResult.output || "").trim();
|
|
2459
|
+
} catch (e) {
|
|
2460
|
+
specVerifyOut = "";
|
|
2461
|
+
}
|
|
2462
|
+
if (/^SPEC_PRESENT/m.test(specVerifyOut)) {
|
|
2463
|
+
log("Spec verified for task " + taskId + " at " + SPEC_PATH);
|
|
2464
|
+
} else {
|
|
2465
|
+
log("Spec verification failed for task " + taskId + ": no spec file at " + SPEC_PATH + " — marking failed for retry");
|
|
2466
|
+
stepResult.summary = (stepResult.summary || "") + "\nspec_verify: FAILED — no spec file at " + SPEC_PATH;
|
|
2467
|
+
passed = false;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2430
2471
|
// "rejected" is an explicit phase verdict routed through rework (Review/QA,
|
|
2431
2472
|
// and the Build/Reproduce reports that feed them). Integrate/Publish work
|
|
2432
2473
|
// that did not finish is operational — "failed", retryable under the
|
|
2433
2474
|
// dispatcher's consecutive-failure cap.
|
|
2434
|
-
const status = passed ? "completed" : (step.name === "Integrate" || step.name === "Publish" ? "failed" : "rejected");
|
|
2475
|
+
const status = passed ? "completed" : (step.name === "Integrate" || step.name === "Publish" || step.name === "Map" ? "failed" : "rejected");
|
|
2435
2476
|
|
|
2436
2477
|
// Deterministic publish verification: the agent cannot self-certify a publish.
|
|
2437
2478
|
// Skip-aware (park 2026-09-11): when the deterministic publish script found
|
|
@@ -2616,6 +2657,16 @@ while (i < STEPS.length) {
|
|
|
2616
2657
|
return { status: "failed", task_id: taskId, reason: "Publish failed: " + summary };
|
|
2617
2658
|
}
|
|
2618
2659
|
|
|
2660
|
+
// Map spec failure is operational (the spec file did not land at the
|
|
2661
|
+
// workflow-computed SPEC_PATH), not a verdict: the session above is
|
|
2662
|
+
// recorded "failed" and the dispatcher retries at Map under its
|
|
2663
|
+
// consecutive-failure cap, parking after the cap. The retry re-runs Map
|
|
2664
|
+
// with the same exact-path instructions against the same SPEC_PATH.
|
|
2665
|
+
if (!passed && step.name === "Map") {
|
|
2666
|
+
log("Map spec verification failed for task " + taskId + " — returning failed for dispatcher retry");
|
|
2667
|
+
return { status: "failed", task_id: taskId, reason: "Map spec verification failed: no spec file at " + SPEC_PATH };
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2619
2670
|
// Publish verification park: the build landed and post-deploy finalized,
|
|
2620
2671
|
// but provenance is UNSTAMPED until the parent's independent read-back
|
|
2621
2672
|
// (docs/publish-verification.md) confirms the artifact's actual content
|