muse-crew 0.14.3 → 0.14.5

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.
Files changed (46) hide show
  1. package/AGENTS.md +2 -2
  2. package/docs/decisions/AGENTS.md +1 -0
  3. package/docs/decisions/publish-path.md +96 -3
  4. package/docs/guide.md +2 -2
  5. package/docs/publish-unknown-recovery.md +10 -3
  6. package/docs/publish-verification.md +126 -62
  7. package/docs/release-integrity.md +62 -0
  8. package/docs/reviews/critic-0144.md +83 -0
  9. package/docs/reviews/critic-0145.md +106 -0
  10. package/lib/AGENTS.md +12 -4
  11. package/lib/advance-publish-base.js +8 -0
  12. package/lib/append-ooda-step.js +12 -3
  13. package/lib/build-readback-request.js +10 -0
  14. package/lib/build-registry.js +8 -3
  15. package/lib/check-intent-freshness.js +101 -0
  16. package/lib/classify-publish-absence.js +11 -0
  17. package/lib/classify-surface.js +12 -2
  18. package/lib/commit-scaffold.js +22 -6
  19. package/lib/compose-evidence-caption.js +15 -4
  20. package/lib/compute-publish-diff.js +41 -11
  21. package/lib/crew-api.js +478 -26
  22. package/lib/crew-release.sh +233 -1
  23. package/lib/gitignore.js +23 -5
  24. package/lib/package.json +1 -0
  25. package/lib/publish-note-vocabulary.js +64 -0
  26. package/lib/read-ooda-verdict.js +11 -3
  27. package/lib/readback-disk.js +9 -0
  28. package/lib/render-html.js +17 -7
  29. package/lib/repo-orchestration.js +21 -5
  30. package/lib/retry-publish.js +116 -72
  31. package/lib/sample-project.js +22 -6
  32. package/lib/scaffold-crew.js +11 -2
  33. package/lib/see-act.js +17 -8
  34. package/lib/serve-artifact.js +12 -6
  35. package/lib/setup-project-repo.js +26 -7
  36. package/lib/update-watch.js +34 -17
  37. package/lib/ux-doctrine.js +31 -6
  38. package/lib/verify-publish.js +44 -6
  39. package/lib/write-ooda-verdict.js +12 -3
  40. package/package.json +1 -1
  41. package/seed/cron-body-template.md +50 -16
  42. package/workflows/bugfix.js +110 -643
  43. package/workflows/chore.js +110 -643
  44. package/workflows/crew-dispatch.js +36 -0
  45. package/workflows/standard.js +118 -633
  46. package/workflows/upgrade.js +4 -2
@@ -14,7 +14,7 @@
14
14
  // small JSON summary verbatim. The diff bytes live in a file; the LLM
15
15
  // never carries them:
16
16
  //
17
- // node compute-publish-diff.js --repo-path <path> --base <sha> --out <file>
17
+ // node compute-publish-diff.js --repo-path <path> --base <sha> --out <file> [--commit <sha>]
18
18
  //
19
19
  // --base is the previously-stamped provenance source_commit (or the
20
20
  // empty-tree sha 4b825dc642cb6eb9a060e54bf8d69288fbee4904 for a first
@@ -22,6 +22,14 @@
22
22
  // script echoes the base it used so the workflow can guard against the
23
23
  // agent running the wrong command (fail-closed, as before).
24
24
  //
25
+ // --commit is the diff's head end (2026-09-20, critic-0145 F-O2): the
26
+ // script used to ignore it and always diff base..HEAD. Optional, defaults
27
+ // to HEAD; the workflow's first-prepare call passes nothing (HEAD is the
28
+ // merge commit it just created), while the tick's byte-identical
29
+ // regeneration passes the intent's commit explicitly. The ancestor check,
30
+ // the diff range, and the reported `commit` all target --commit, never an
31
+ // implicit HEAD.
32
+ //
25
33
  // Stdout contract: exactly one JSON object, always small (no diff text):
26
34
  // { ok:true, commit, base, ancestor:"yes", diff_file, sha256, bytes,
27
35
  // file_count, changed_lines, files:[<paths>], has_binary, has_rename }
@@ -40,6 +48,15 @@ import { createHash } from "node:crypto";
40
48
  import { existsSync, mkdirSync, statSync, writeFileSync, readFileSync } from "node:fs";
41
49
  import { dirname, resolve } from "node:path";
42
50
 
51
+ // --help: before required-arg parsing (shebang⇔CLI contract).
52
+ if (process.argv.slice(2).includes("--help")) {
53
+ console.log(
54
+ "usage: node compute-publish-diff.js --repo-path <path> --base <sha> --out <file> [--commit <sha>]\n" +
55
+ "runs git diff <base> <commit> (default <commit> = HEAD), writes the raw diff bytes to --out, prints the small JSON summary on stdout"
56
+ );
57
+ process.exit(0);
58
+ }
59
+
43
60
  const EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
44
61
 
45
62
  function arg(name, required = true) {
@@ -65,36 +82,49 @@ function git(repoPath, args) {
65
82
  const repoPath = arg("--repo-path");
66
83
  const base = arg("--base");
67
84
  const outFile = arg("--out");
85
+ const commitArg = arg("--commit", false);
68
86
 
69
87
  if (!/^[0-9a-f]{40}$/.test(base)) fail("usage", "--base must be a 40-char hex commit SHA (or the empty-tree SHA).", 2);
70
88
  let st = null;
71
89
  try { st = statSync(repoPath); } catch { /* falls through */ }
72
90
  if (!st || !st.isDirectory()) fail("usage", "--repo-path must be an existing directory.", 2);
73
91
 
74
- const head = git(repoPath, ["rev-parse", "HEAD"]).trim();
75
- if (!/^[0-9a-f]{40}$/.test(head)) fail("git-failed", "rev-parse HEAD did not return a commit SHA.", 1);
92
+ // The diff's head end: explicit --commit, or HEAD when omitted (the
93
+ // workflow's first-prepare call passes nothing — HEAD is the merge commit
94
+ // it just created). Validated as a 40-hex commit object in this repo;
95
+ // everything below (ancestor check, diff range, reported commit) targets
96
+ // it, never an implicit HEAD (2026-09-20, critic-0145 F-O2).
97
+ let commit;
98
+ if (commitArg === null) {
99
+ commit = git(repoPath, ["rev-parse", "HEAD"]).trim();
100
+ } else {
101
+ if (!/^[0-9a-f]{40}$/.test(commitArg)) fail("usage", "--commit must be a 40-char hex commit SHA.", 2);
102
+ commit = git(repoPath, ["rev-parse", "--verify", commitArg + "^{commit}"]).trim();
103
+ }
104
+ if (!/^[0-9a-f]{40}$/.test(commit)) fail("git-failed", "could not resolve the diff target to a commit SHA.", 1);
76
105
 
77
- // Ancestor check — deterministic, in code. The empty tree is not a commit:
78
- // git merge-base --is-ancestor fails on it, so a first publish hardcodes
79
- // ANCESTOR=yes here instead of executing the conditional (clean-room
80
- // 2026-09-16: an agent ran merge-base on the empty tree directly and parked).
106
+ // Ancestor check — deterministic, in code, against the diff target. The
107
+ // empty tree is not a commit: git merge-base --is-ancestor fails on it, so
108
+ // a first publish hardcodes ANCESTOR=yes here instead of executing the
109
+ // conditional (clean-room 2026-09-16: an agent ran merge-base on the empty
110
+ // tree directly and parked).
81
111
  let ancestor = "yes";
82
112
  if (base !== EMPTY_TREE) {
83
113
  try {
84
- execFileSync("git", ["-C", repoPath, "merge-base", "--is-ancestor", base, head], { stdio: "ignore" });
114
+ execFileSync("git", ["-C", repoPath, "merge-base", "--is-ancestor", base, commit], { stdio: "ignore" });
85
115
  ancestor = "yes";
86
116
  } catch {
87
117
  ancestor = "no";
88
118
  }
89
119
  }
90
120
  if (ancestor !== "yes") {
91
- process.stdout.write(JSON.stringify({ ok: false, error: "not-ancestor", commit: head, base }) + "\n");
121
+ process.stdout.write(JSON.stringify({ ok: false, error: "not-ancestor", commit, base }) + "\n");
92
122
  process.exit(0);
93
123
  }
94
124
 
95
125
  const outPath = resolve(outFile);
96
126
  mkdirSync(dirname(outPath), { recursive: true });
97
- const diffText = git(repoPath, ["diff", base, head]);
127
+ const diffText = git(repoPath, ["diff", base, commit]);
98
128
  writeFileSync(outPath, diffText, "utf8");
99
129
  const bytes = readFileSync(outPath);
100
130
  const sha256 = createHash("sha256").update(bytes).digest("hex");
@@ -131,7 +161,7 @@ for (const line of diffText.split("\n")) {
131
161
 
132
162
  process.stdout.write(JSON.stringify({
133
163
  ok: true,
134
- commit: head,
164
+ commit,
135
165
  base,
136
166
  ancestor: "yes",
137
167
  diff_file: outPath,