@milehimikey/em 1.18.0 → 1.19.0

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 (86) hide show
  1. package/.claude/agents/em-implementer.md +7 -4
  2. package/.claude/agents/em-validator.md +2 -2
  3. package/.claude/skills/event-modeling/SKILL.md +1 -1
  4. package/.claude/skills/event-modeling-conform/SKILL.md +1 -1
  5. package/.claude/skills/event-modeling-design/SKILL.md +5 -2
  6. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  7. package/.claude/skills/event-modeling-discover/reference/prd-rubric.md +1 -1
  8. package/.claude/skills/event-modeling-discover/reference/prd-spec-path.md +1 -1
  9. package/.claude/skills/event-modeling-engagement/SKILL.md +15 -8
  10. package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
  11. package/.claude/skills/event-modeling-implement/reference/implement.md +10 -0
  12. package/.claude/skills/event-modeling-review/SKILL.md +4 -2
  13. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +14 -8
  14. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +14 -2
  15. package/.claude-plugin/marketplace.json +2 -2
  16. package/dist/catalog/docJoin.d.ts +2 -1
  17. package/dist/catalog/docJoin.js.map +1 -1
  18. package/dist/catalog/docModelConsistencyValidate.js +17 -1
  19. package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
  20. package/dist/catalog/signedBodyValidate.js.map +1 -1
  21. package/dist/catalog/sliceDoc.d.ts +47 -15
  22. package/dist/catalog/sliceDoc.js +82 -22
  23. package/dist/catalog/sliceDoc.js.map +1 -1
  24. package/dist/catalog/sliceReadyValidate.d.ts +3 -2
  25. package/dist/catalog/sliceReadyValidate.js +30 -2
  26. package/dist/catalog/sliceReadyValidate.js.map +1 -1
  27. package/dist/catalog/sliceSections.d.ts +9 -0
  28. package/dist/catalog/sliceSections.js +29 -3
  29. package/dist/catalog/sliceSections.js.map +1 -1
  30. package/dist/cli/ciInit.d.ts +17 -0
  31. package/dist/cli/ciInit.js +47 -3
  32. package/dist/cli/ciInit.js.map +1 -1
  33. package/dist/cli/defer.d.ts +1 -1
  34. package/dist/cli/defer.js +5 -4
  35. package/dist/cli/defer.js.map +1 -1
  36. package/dist/cli/engagement.d.ts +15 -1
  37. package/dist/cli/engagement.js +84 -5
  38. package/dist/cli/engagement.js.map +1 -1
  39. package/dist/cli/engagementFile.d.ts +2 -0
  40. package/dist/cli/engagementFile.js +3 -1
  41. package/dist/cli/engagementFile.js.map +1 -1
  42. package/dist/cli/prdTriage.js +15 -0
  43. package/dist/cli/prdTriage.js.map +1 -1
  44. package/dist/cli/review.d.ts +17 -2
  45. package/dist/cli/review.js +39 -10
  46. package/dist/cli/review.js.map +1 -1
  47. package/dist/cli/sliceSync.d.ts +37 -0
  48. package/dist/cli/sliceSync.js +84 -4
  49. package/dist/cli/sliceSync.js.map +1 -1
  50. package/dist/cli/upgrade.d.ts +2 -2
  51. package/dist/cli/upgrade.js +169 -38
  52. package/dist/cli/upgrade.js.map +1 -1
  53. package/dist/cli.js +51 -17
  54. package/dist/cli.js.map +1 -1
  55. package/dist/emit/engagementJson.d.ts +4 -3
  56. package/dist/emit/engagementJson.js +9 -4
  57. package/dist/emit/engagementJson.js.map +1 -1
  58. package/dist/emit/validateJson.d.ts +4 -3
  59. package/dist/emit/validateJson.js +4 -3
  60. package/dist/emit/validateJson.js.map +1 -1
  61. package/dist/model/model.d.ts +5 -0
  62. package/dist/model/model.js +15 -0
  63. package/dist/model/model.js.map +1 -1
  64. package/dist/model/rules.d.ts +8 -1
  65. package/dist/model/rules.js +8 -1
  66. package/dist/model/rules.js.map +1 -1
  67. package/dist/model/validate.js +46 -31
  68. package/dist/model/validate.js.map +1 -1
  69. package/dist/query/verbs.js +4 -4
  70. package/dist/query/verbs.js.map +1 -1
  71. package/package.json +1 -1
  72. package/plugin/.claude-plugin/plugin.json +2 -2
  73. package/plugin/agents/em-implementer.md +7 -4
  74. package/plugin/agents/em-validator.md +2 -2
  75. package/plugin/shared/reference/em-dsl.md +14 -8
  76. package/plugin/shared/reference/slice-doc-schema.md +14 -2
  77. package/plugin/skills/conform/SKILL.md +1 -1
  78. package/plugin/skills/design/SKILL.md +5 -2
  79. package/plugin/skills/discover/SKILL.md +1 -1
  80. package/plugin/skills/discover/reference/prd-rubric.md +1 -1
  81. package/plugin/skills/discover/reference/prd-spec-path.md +1 -1
  82. package/plugin/skills/engagement/SKILL.md +15 -8
  83. package/plugin/skills/event-modeling/SKILL.md +1 -1
  84. package/plugin/skills/implement/SKILL.md +1 -1
  85. package/plugin/skills/implement/reference/implement.md +10 -0
  86. package/plugin/skills/review/SKILL.md +4 -2
@@ -20,12 +20,24 @@
20
20
  // rewritten, exit 1.
21
21
  // `--check` never writes; it exits 1 when any doc is stale (or malformed).
22
22
  //
23
+ // GH #347 — a doc that records a `ratifiedHash:` (a ratified or shipped doc) is synced like any
24
+ // other and KEEPS its signature: since 1.19.0 the hash covers the authored text only
25
+ // (catalog/sliceDoc.ts `ratifiedBody` leaves the inside of every generated region out), so a
26
+ // region rewrite never moves it. A hash still in an older scope (1.17–1.18, regions inside; or
27
+ // pre-1.17 whole-body) whose body is intact is re-stamped in the current scope in the same write
28
+ // — same authored text, not a re-signing, `ratifiedRef` untouched — because the old-scope hash
29
+ // would not survive the rewrite. A doc whose body already fails its hash is synced too, nothing
30
+ // re-stamped (the signature was broken before; `--check-signed-bodies` still says so). Each case
31
+ // prints a `note:` line (`signatureNote`).
32
+ //
23
33
  // Pure core (`syncDocText`) + fs-aware runner (`runSliceSync`); the CLI and the MCP `slice_sync`
24
34
  // tool (check-only) share `buildSliceSyncJson`.
25
35
  import { readFileSync, writeFileSync } from "node:fs";
26
36
  import { join } from "node:path";
27
37
  import { resolveSliceDocJoin } from "../catalog/docJoin.js";
28
38
  import { buildSliceRegions, scanSliceRegions } from "../catalog/sliceSections.js";
39
+ import { isOldHashScope, parseSliceDoc, ratifiedHashScope, sliceDocBodyHash } from "../catalog/sliceDoc.js";
40
+ import { replaceFieldValue } from "./frontmatterSurgery.js";
29
41
  import { GENERATOR_NAME, GENERATOR_VERSION } from "../emit/json.js";
30
42
  // 1.0 (MIL-266): initial shape.
31
43
  export const SLICE_SYNC_SCHEMA_VERSION = "1.0";
@@ -65,6 +77,36 @@ export function syncDocText(raw, expected) {
65
77
  next = next.slice(0, e.start) + e.text + next.slice(e.end);
66
78
  return { status: edits.length > 0 ? "stale" : "ok", regions, problems: [], next };
67
79
  }
80
+ /**
81
+ * Pure (GH #347): the signature half of a sync. `raw` is the doc as read, `next` the doc with its
82
+ * regions regenerated. A doc with no `ratifiedHash:` passes through (`signature: null`). Otherwise
83
+ * the recorded hash is judged on `raw` through `ratifiedHashScope`: `current` is kept; an intact
84
+ * `v1.17`/`legacy` hash is re-stamped with the current-scope hash of the same body; `mismatch`
85
+ * passes through untouched. Then — the invariant this rests on — an intact signature must match
86
+ * `next` in the current scope, or nothing is written.
87
+ */
88
+ export function keepSignature(raw, next) {
89
+ const recorded = parseSliceDoc(raw).ratifiedHash;
90
+ if (recorded === null)
91
+ return { ok: true, content: next, signature: null };
92
+ const scope = ratifiedHashScope(raw, recorded);
93
+ if (scope === "mismatch")
94
+ return { ok: true, content: next, signature: { kind: "broken" } };
95
+ let content = next;
96
+ let signature = { kind: "kept" };
97
+ if (isOldHashScope(scope)) {
98
+ const restamped = replaceFieldValue(next, "ratifiedHash", sliceDocBodyHash(raw));
99
+ if (restamped === null)
100
+ return { ok: false, message: "could not locate ratifiedHash: in the frontmatter" };
101
+ content = restamped;
102
+ signature = { kind: "restamped", from: scope };
103
+ }
104
+ const after = parseSliceDoc(content).ratifiedHash;
105
+ if (after === null || ratifiedHashScope(content, after) !== "current") {
106
+ return { ok: false, message: "rewriting the generated regions would leave the authored text unmatched by ratifiedHash — nothing written (this is a bug in em; please report it)" };
107
+ }
108
+ return { ok: true, content, signature };
109
+ }
68
110
  /**
69
111
  * Sync every bound doc of `model` (or only the doc `sliceKey` resolves to). `check` never
70
112
  * writes. `baseDir` is the `.em` file's directory (doc paths are relative to it).
@@ -109,12 +151,36 @@ export function runSliceSync(model, refs, baseDir, sliceKey, check) {
109
151
  const result = syncDocText(raw, buildSliceRegions(model, indices));
110
152
  let status = result.status;
111
153
  let regions = result.regions;
154
+ let problems = result.problems;
155
+ let signature = null;
156
+ let signatureRefused = null;
157
+ if (check && result.status === "stale") {
158
+ const probe = keepSignature(raw, result.next);
159
+ if (!probe.ok)
160
+ signatureRefused = probe.message;
161
+ }
112
162
  if (!check && result.status === "stale") {
113
- writeFileSync(abs, result.next);
114
- status = "synced";
115
- regions = regions.map((r) => (r.status === "stale" ? { name: r.name, status: "synced" } : r));
163
+ const signed = keepSignature(raw, result.next);
164
+ if (signed.ok) {
165
+ writeFileSync(abs, signed.content);
166
+ status = "synced";
167
+ regions = regions.map((r) => (r.status === "stale" ? { name: r.name, status: "synced" } : r));
168
+ signature = signed.signature;
169
+ }
170
+ else {
171
+ status = "malformed";
172
+ problems = [signed.message];
173
+ }
116
174
  }
117
- docs.push({ key: refs.sliceKeys[ownerIdx], path, status, regions, problems: result.problems });
175
+ docs.push({
176
+ key: refs.sliceKeys[ownerIdx],
177
+ path,
178
+ status,
179
+ regions,
180
+ problems,
181
+ ...(signature !== null ? { signature } : {}),
182
+ ...(signatureRefused !== null ? { signatureRefused } : {}),
183
+ });
118
184
  }
119
185
  return { ok: true, docs };
120
186
  }
@@ -131,6 +197,18 @@ export function buildSliceSyncJson(file, docs) {
131
197
  export function sliceSyncFailed(docs, check) {
132
198
  return docs.some((d) => d.status === "malformed" || (check && d.status === "stale"));
133
199
  }
200
+ /** GH #347: the line `em slice sync` prints after rewriting the regions of a doc that records a
201
+ * `ratifiedHash:`. */
202
+ export function signatureNote(path, signature) {
203
+ switch (signature.kind) {
204
+ case "kept":
205
+ return `note: ${path} is signed — generated regions rewritten, ratifiedHash kept (it covers the authored text only)`;
206
+ case "restamped":
207
+ return `note: ${path} is signed — generated regions rewritten, ratifiedHash re-stamped from the ${signature.from === "v1.17" ? "1.17" : "pre-1.17 whole-body"} scope to the current scope (same authored text, not a re-signing; ratifiedRef untouched)`;
208
+ case "broken":
209
+ return `note: ${path}'s body already failed its ratifiedHash before this sync (edited after ratification) — generated regions rewritten, nothing re-stamped; see em validate --check-signed-bodies`;
210
+ }
211
+ }
134
212
  /** Text-mode report: `{ out, err }` lines (status lines on stdout, notes and problems on stderr). */
135
213
  export function formatSliceSync(docs, check) {
136
214
  const out = [];
@@ -146,6 +224,8 @@ export function formatSliceSync(docs, check) {
146
224
  break;
147
225
  case "synced":
148
226
  out.push(`synced: ${d.path} (${changed.join(", ")})`);
227
+ if (d.signature)
228
+ err.push(signatureNote(d.path, d.signature));
149
229
  break;
150
230
  case "no-regions":
151
231
  if (check)
@@ -1 +1 @@
1
- {"version":3,"file":"sliceSync.js","sourceRoot":"","sources":["../../src/cli/sliceSync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sliceSync.js","sourceRoot":"","sources":["../../src/cli/sliceSync.ts"],"names":[],"mappings":"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"}
@@ -35,7 +35,7 @@ export type StepApplyOutcome = {
35
35
  ok: false;
36
36
  message: string;
37
37
  };
38
- export type UpgradeStepId = "skill-bundle" | "skill-plugin" | "reaction-shape" | "state-file" | "ci-block" | "constitution" | "ratified-signoff" | "ratified-hash-scope" | "collapse-alternative-events" | "fold-continuations" | "view-doc-sources-stale" | "slice-index-stale" | "parking-lot-fragments" | "system-manifest";
38
+ export type UpgradeStepId = "skill-bundle" | "skill-plugin" | "reaction-shape" | "state-file" | "ci-block" | "api-contract-missing" | "constitution" | "ratified-signoff" | "ratified-hash-scope" | "collapse-alternative-events" | "fold-continuations" | "view-doc-sources-stale" | "slice-index-stale" | "parking-lot-fragments" | "system-manifest";
39
39
  export interface UpgradeStepDef {
40
40
  id: UpgradeStepId;
41
41
  /** The em release that introduced the feature this step brings a repo up to date with —
@@ -58,7 +58,7 @@ export { GRANDFATHERED_RATIFIER };
58
58
  * `null` when the doc isn't a candidate (no frontmatter/status, or `ratifiedBy` already set). */
59
59
  export declare function applyGrandfatherSignoff(raw: string, ratifiedOn: string): string | null;
60
60
  export declare const UPGRADE_STEPS: readonly UpgradeStepDef[];
61
- export type HumanItemId = "no-model-version" | "continuation-has-own-doc" | "ready-to-implement-no-ratifiedby" | "coverage-scope-default" | "unratified-constitution" | "predates-1.6" | "public-field-types-unresolved" | "plugin-install-locally" | "slice-doc-stale-signed" | "lineage-backlink-missing" | "fold-continuations-opt-in" | "shared-event-now-legal" | "roster-not-initialised";
61
+ export type HumanItemId = "no-model-version" | "continuation-has-own-doc" | "ready-to-implement-no-ratifiedby" | "coverage-scope-default" | "unratified-constitution" | "predates-1.6" | "public-field-types-unresolved" | "plugin-install-locally" | "slice-doc-stale-signed" | "lineage-backlink-missing" | "fold-continuations-opt-in" | "shared-event-now-legal" | "roster-not-initialised" | "api-contract-blocked" | "ci-paths-hand-edited";
62
62
  export interface HumanItem {
63
63
  id: HumanItemId;
64
64
  reason: string;
@@ -34,12 +34,12 @@ import { planSkillSyncBundle, applySkillSyncBundle, planAgentSync, applySkillSyn
34
34
  import { EM_ALL_SKILL_BUNDLE_DIRS, EM_SKILL_ANCHOR_DIR, EM_AGENT_FILES } from "./skillDirs.js";
35
35
  import { detectPlugin, checkPlugin, claudePluginsDir, mergePluginSettings, pluginInstallCommands, pluginSettingsEntries } from "./pluginPin.js";
36
36
  import { syncAgentsMd } from "./agentsMd.js";
37
- import { ciWorkflowPath, conformWorkflowPath, buildCiWorkflowFile, buildConformWorkflowFile, buildCiWorkflowFileMulti, buildConformWorkflowFileMulti, ciManagedBody, ciManagedBodyMulti, conformManagedBody, conformManagedBodyMulti, ciModelsFromManifest, planCiFile, applyCiFile, CI_WORKFLOW_MARKER, CONFORM_WORKFLOW_MARKER, } from "./ciInit.js";
37
+ import { ciWorkflowPath, conformWorkflowPath, buildCiWorkflowFile, buildConformWorkflowFile, buildCiWorkflowFileMulti, buildConformWorkflowFileMulti, ciManagedBody, ciManagedBodyMulti, conformManagedBody, conformManagedBodyMulti, ciModelsFromManifest, planCiFile, applyCiFile, refreshCiPullRequestPaths, applyCiPullRequestPathsRefresh, missingCiPullRequestPaths, CI_WORKFLOW_MARKER, CONFORM_WORKFLOW_MARKER, } from "./ciInit.js";
38
38
  import { findSpecifyRoot, locateConstitution } from "./status.js";
39
39
  import { scaffoldConstitution, scaffoldStateFile } from "../templates.js";
40
40
  import { loadStateFile, parseState, setEmVersion, ensureOptionalBullets, resolveStateFilePath } from "./stateFile.js";
41
41
  import { fieldLineRegex, normalizeFieldValue, locateFrontmatterInner, replaceFieldValue } from "./frontmatterSurgery.js";
42
- import { parseSliceDoc, ratifiedHashScope, sliceDocBodyHash } from "../catalog/sliceDoc.js";
42
+ import { isOldHashScope, parseSliceDoc, ratifiedHashScope, sliceDocBodyHash } from "../catalog/sliceDoc.js";
43
43
  import { applyDeferOrAppend, drainQueueInState, finishSignedDocEdit, isSignedDoc, readQueue, rewriteParkingLotFragments } from "./defer.js";
44
44
  import { runSliceSync } from "./sliceSync.js";
45
45
  import { runSliceIndex } from "./sliceIndex.js";
@@ -48,6 +48,8 @@ import { observeSigners } from "../roster/init.js";
48
48
  import { STATE_FILE_NAME } from "./stateFile.js";
49
49
  import { localIsoDate } from "../util/localDate.js";
50
50
  import { compile } from "../pipeline.js";
51
+ import { computeModelKey } from "../model/qualifiedRef.js";
52
+ import { contractPathFor, generateContract } from "./api.js";
51
53
  import { hasErrors } from "../model/validate.js";
52
54
  import { LEGACY_SYSTEM_MANIFEST_SCHEMA_VERSION, parseManifest } from "../system/manifest.js";
53
55
  import { planManifestMigration } from "../system/migrateManifest.js";
@@ -304,16 +306,19 @@ function ciBlockPlans(ctx) {
304
306
  const multi = args.models !== null;
305
307
  const files = ciBlockFiles(args, ctx);
306
308
  const ciStatus = planCiFile(ciPath, files.ci[0], files.ci[1], CI_WORKFLOW_MARKER, false, multi);
307
- const ciStale = ciStatus.kind === "stale";
309
+ // GH #346: the `on:` paths live outside the managed block; refresh them when still the pre-1.19 list.
310
+ // A workflow with its markers removed is the documented opt-out and stays untouched, filter included.
311
+ const pathsStale = (ciStatus.kind === "ok" || ciStatus.kind === "stale") && refreshCiPullRequestPaths(readFileSync(ciPath, "utf8")) !== null;
312
+ const ciStale = ciStatus.kind === "stale" || pathsStale;
308
313
  let conformStale = false;
309
314
  if (existsSync(conformPath)) {
310
315
  const conformStatus = planCiFile(conformPath, files.conform[0], files.conform[1], CONFORM_WORKFLOW_MARKER, false, multi);
311
316
  conformStale = conformStatus.kind === "stale";
312
317
  }
313
- return { ciPath, ciStale, conformPath, conformStale, args };
318
+ return { ciPath, ciStale, pathsOnly: pathsStale && ciStatus.kind === "ok", conformPath, conformStale, args };
314
319
  }
315
320
  function detectCiBlock(ctx) {
316
- const { ciPath, ciStale, conformStale, args, blocked } = ciBlockPlans(ctx);
321
+ const { ciPath, ciStale, pathsOnly, conformStale, args, blocked } = ciBlockPlans(ctx);
317
322
  if (!existsSync(ciPath))
318
323
  return { applicable: false, reason: "no .github/workflows/em-ci.yml — em upgrade never creates one" };
319
324
  if (!args && blocked)
@@ -322,6 +327,8 @@ function detectCiBlock(ctx) {
322
327
  return { applicable: false, reason: "can't determine the <model>/--tests arguments em-ci.yml was generated with — run `em ci init <model>` by hand if it needs refreshing" };
323
328
  if (!ciStale && !conformStale)
324
329
  return { applicable: false, reason: "generated CI block(s) already match the installed em" };
330
+ if (pathsOnly && !conformStale)
331
+ return { applicable: true, reason: "em-ci.yml pull_request paths filter is the pre-1.19.0 list" };
325
332
  return { applicable: true, reason: "generated CI block(s) are stale" };
326
333
  }
327
334
  function applyCiBlock(ctx) {
@@ -333,6 +340,7 @@ function applyCiBlock(ctx) {
333
340
  const changedFiles = [];
334
341
  if (ciStale) {
335
342
  applyCiFile(ciPath, planCiFile(ciPath, files.ci[0], files.ci[1], CI_WORKFLOW_MARKER, false, multi));
343
+ applyCiPullRequestPathsRefresh(ciPath);
336
344
  changedFiles.push(ciPath);
337
345
  }
338
346
  if (conformStale) {
@@ -341,6 +349,82 @@ function applyCiBlock(ctx) {
341
349
  }
342
350
  return { ok: true, changedFiles };
343
351
  }
352
+ /** Every model covered by an `api-check` job whose `contracts/<model key>.tsp` is absent. The job
353
+ * counts when it is in the on-disk em-ci.yml OR when `ci-block` is pending (a stale block is
354
+ * rewritten to the current preset, which carries `api-check` - so a pre-1.12 workflow is caught by
355
+ * the dry run, not only after `--apply`). Models that can't be read or compile cleanly are
356
+ * returned in `blocked` (never skipped silently). */
357
+ function contractPlan(ctx) {
358
+ const none = { missing: [], blocked: [] };
359
+ const ciPath = ciWorkflowPath(ctx.repoRoot);
360
+ if (!existsSync(ciPath))
361
+ return none;
362
+ const text = readFileSync(ciPath, "utf8");
363
+ const plans = ciBlockPlans(ctx);
364
+ const hasJob = /^ {2}api-check(-[\w-]+)?:/m.test(text) || plans.ciStale;
365
+ if (!hasJob || !plans.args)
366
+ return none;
367
+ const paths = plans.args.models ? plans.args.models.map((m) => m.path) : [plans.args.model];
368
+ const missing = [];
369
+ const blocked = [];
370
+ for (const rel of paths) {
371
+ const modelFile = resolve(ctx.repoRoot, rel);
372
+ let source;
373
+ try {
374
+ source = readFileSync(modelFile, "utf8");
375
+ }
376
+ catch {
377
+ blocked.push({ model: rel, why: "the model file can't be read" });
378
+ continue;
379
+ }
380
+ try {
381
+ const compiled = compile(source);
382
+ if (hasErrors(compiled.diagnostics)) {
383
+ // Only blocked when the contract is actually absent (a model mid-edit that already has one is fine).
384
+ if (!existsSync(contractPathFor(modelFile, computeModelKey(compiled.model, modelFile))))
385
+ blocked.push({ model: rel, why: "it has compile errors" });
386
+ continue;
387
+ }
388
+ const g = generateContract(modelFile, source, compiled);
389
+ if (!existsSync(g.contractPath))
390
+ missing.push({ modelFile, contractPath: g.contractPath, modelKey: g.modelKey });
391
+ }
392
+ catch (e) {
393
+ blocked.push({ model: rel, why: `it does not parse (${e.message})` });
394
+ }
395
+ }
396
+ return { missing, blocked };
397
+ }
398
+ function blockedText(blocked) {
399
+ return blocked.map((b) => `${b.model} (${b.why})`).join("; ");
400
+ }
401
+ function detectApiContractMissing(ctx) {
402
+ const { missing, blocked } = contractPlan(ctx);
403
+ if (missing.length === 0) {
404
+ if (blocked.length)
405
+ return { applicable: false, reason: `the api-check contract could not be generated for ${blockedText(blocked)} - see the api-contract-blocked item` };
406
+ return { applicable: false, reason: "no api-check job lacking its contracts/<model key>.tsp" };
407
+ }
408
+ const skipped = blocked.length ? `; not generated for ${blockedText(blocked)} (see the api-contract-blocked item)` : "";
409
+ return {
410
+ applicable: true,
411
+ reason: `the api-check job needs ${missing.map((m) => relative(ctx.repoRoot, m.contractPath)).join(", ")}, which ${missing.length === 1 ? "is" : "are"} missing - em upgrade will run em api generate${skipped}`,
412
+ };
413
+ }
414
+ function applyApiContractMissing(ctx) {
415
+ const { missing } = contractPlan(ctx);
416
+ if (missing.length === 0)
417
+ return { ok: false, message: "nothing to generate" };
418
+ const changedFiles = [];
419
+ for (const m of missing) {
420
+ const source = readFileSync(m.modelFile, "utf8");
421
+ const generated = generateContract(m.modelFile, source, compile(source));
422
+ mkdirSync(dirname(generated.contractPath), { recursive: true });
423
+ writeFileSync(generated.contractPath, generated.text);
424
+ changedFiles.push(generated.contractPath);
425
+ }
426
+ return { ok: true, changedFiles };
427
+ }
344
428
  // ---- Step 5: constitution — scaffold a draft when absent and no .specify/ owns the slot. ----
345
429
  function detectConstitution(ctx) {
346
430
  const existing = locateConstitution(ctx.baseDir);
@@ -478,29 +562,35 @@ function queuedLeftovers(ctx) {
478
562
  return out.sort((a, b) => a.key.localeCompare(b.key));
479
563
  }
480
564
  function detectRatifiedHashScope(ctx) {
481
- const legacy = hashScopeCandidates(ctx).filter((c) => c.scope === "legacy");
482
- const mismatch = hashScopeCandidates(ctx).filter((c) => c.scope === "mismatch");
565
+ const candidates = hashScopeCandidates(ctx);
566
+ const v117 = candidates.filter((c) => c.scope === "v1.17");
567
+ const legacy = candidates.filter((c) => c.scope === "legacy");
568
+ const mismatch = candidates.filter((c) => c.scope === "mismatch");
483
569
  const queued = queuedLeftovers(ctx);
484
- if (legacy.length === 0 && queued.length === 0) {
485
- return { applicable: false, reason: "every ratifiedHash is in the 1.17 scope and no question is queued in the state file" };
570
+ const left = mismatch.length > 0 ? `left alone: ${mismatch.map((c) => c.key).join(", ")} — body no longer matches ratifiedHash in any scope (revert, or em slice revise and re-ratify)` : null;
571
+ if (v117.length === 0 && legacy.length === 0 && queued.length === 0) {
572
+ return { applicable: false, reason: `every intact ratifiedHash is in the 1.19 scope and no question is queued in the state file${left ? `; ${left}` : ""}` };
486
573
  }
574
+ const restamp = "em upgrade will re-stamp them in the 1.19 scope (authored text only: generated regions and Open Questions deferrals excluded; same body, not a re-signing; ratifiedRef untouched)";
487
575
  const parts = [];
576
+ if (v117.length > 0)
577
+ parts.push(`${v117.length} doc(s) carry a 1.17–1.18 ratifiedHash (generated regions inside; body intact): ${v117.map((c) => c.key).join(", ")} — ${restamp}`);
488
578
  if (legacy.length > 0)
489
- parts.push(`${legacy.length} doc(s) carry a pre-1.17 whole-body ratifiedHash (body intact): ${legacy.map((c) => c.key).join(", ")} — em upgrade will re-stamp them in the 1.17 scope (Open Questions deferrals excluded; same body, not a re-signing)`);
579
+ parts.push(`${legacy.length} doc(s) carry a pre-1.17 whole-body ratifiedHash (body intact): ${legacy.map((c) => c.key).join(", ")} — ${restamp}`);
490
580
  if (queued.length > 0) {
491
581
  const n = queued.reduce((s, q) => s + q.items.length, 0);
492
582
  parts.push(`${n} question(s) queued in ${STATE_FILE_NAME} by 1.16's em slice defer (GH #236) for ${queued.map((q) => `${q.key}@v${q.version + 1}`).join(", ")} — em upgrade will write each into its doc's Open Questions as a deferred item and drain the queue`);
493
583
  }
494
- if (mismatch.length > 0)
495
- parts.push(`left alone: ${mismatch.map((c) => c.key).join(", ")} — body no longer matches ratifiedHash in either scope (revert, or em slice revise and re-ratify)`);
584
+ if (left)
585
+ parts.push(left);
496
586
  return { applicable: true, reason: parts.join("; ") };
497
587
  }
498
588
  function applyRatifiedHashScope(ctx) {
499
- const legacy = new Set(hashScopeCandidates(ctx).filter((c) => c.scope === "legacy").map((c) => c.path));
589
+ const oldScope = new Set(hashScopeCandidates(ctx).filter((c) => isOldHashScope(c.scope)).map((c) => c.path));
500
590
  const queued = queuedLeftovers(ctx);
501
591
  const touched = new Map(); // abs path -> next content
502
592
  const current = (path) => touched.get(path) ?? readFileSync(path, "utf8");
503
- // Queue first, on the doc as read: `finishSignedDocEdit` re-stamps a legacy hash itself.
593
+ // Queue first, on the doc as read: `finishSignedDocEdit` re-stamps an old-scope hash itself.
504
594
  for (const q of queued) {
505
595
  const file = join(ctx.baseDir, q.path);
506
596
  let content = current(file);
@@ -524,9 +614,9 @@ function applyRatifiedHashScope(ctx) {
524
614
  content = finished.content;
525
615
  }
526
616
  touched.set(file, content);
527
- legacy.delete(q.path);
617
+ oldScope.delete(q.path);
528
618
  }
529
- for (const path of legacy) {
619
+ for (const path of oldScope) {
530
620
  const file = join(ctx.baseDir, path);
531
621
  const raw = current(file);
532
622
  const next = replaceFieldValue(raw, "ratifiedHash", sliceDocBodyHash(raw));
@@ -668,7 +758,7 @@ function detectCollapseAlternativeEvents(ctx) {
668
758
  applicable: true,
669
759
  reason: `${plan.groups.length} run(s) of repeated-command slices collapse to alternative events: ${desc.join("; ")} — em upgrade will rewrite them (the first slice's key is kept; the others' docs move to ${ARCHIVE_DIR}/ with superseded-by and a SUPERSEDED banner, and the kept doc gains merged-from)` +
670
760
  (notes.length > 0 ? `; ${notes.join("; ")}` : "") +
671
- (stale.length > 0 ? `; signed doc(s) will not be synced and go stale: ${stale.join(", ")} — em slice revise <key>, then em slice sync` : ""),
761
+ (stale.length > 0 ? `; signed doc(s) whose generated regions go stale: ${stale.join(", ")} — em upgrade syncs the kept slice's doc, run em slice sync for any other (since 1.19.0 sync keeps the signature: ratifiedHash covers the authored text only)` : ""),
672
762
  };
673
763
  }
674
764
  /** Frontmatter-only edit: adds `key: value` before the closing fence (a no-op without frontmatter). */
@@ -708,15 +798,18 @@ function applyCollapseAlternativeEvents(ctx) {
708
798
  if (!changed.includes(abs))
709
799
  changed.push(abs);
710
800
  }
711
- // Regenerate the kept slice's event region (one `Records one of:` region now) unless its doc is signed
712
- // (then `slice-doc-stale-signed` is the human item that says what to do).
801
+ // Regenerate the kept slice's event region (one `Records one of:` region now). A signed doc is synced
802
+ // too and keeps its signature (GH #347: ratifiedHash covers the authored text only).
713
803
  const after = freshCompile(ctx);
714
804
  if (after) {
715
805
  for (const g of plan.groups) {
716
806
  const kept = retired.find((r) => r.keptKey === g.keys[0])?.keptDoc ?? `slices/${g.keys[0]}.md`;
717
807
  const doc = join(ctx.baseDir, kept);
718
- if (existsSync(doc) && !isSignedDoc(readFileSync(doc, "utf8"))) {
808
+ if (existsSync(doc)) {
719
809
  const r = runSliceSync(after.model, after.refs, ctx.baseDir, g.keys[0], false);
810
+ const bad = r.ok ? r.docs.find((d) => d.status === "malformed") : undefined;
811
+ if (bad)
812
+ return { ok: false, message: `${bad.path}: em slice sync could not rewrite it — ${bad.problems.join("; ")}` };
720
813
  if (r.ok && r.docs.some((d) => d.status === "synced") && !changed.includes(doc))
721
814
  changed.push(doc);
722
815
  }
@@ -750,7 +843,7 @@ function detectFoldContinuations(ctx) {
750
843
  const optIn = ctx.foldContinuations === true || ctx.toSugar === true;
751
844
  const prefix = optIn ? "" : `OPT-IN, not applied by a plain --apply (run \`${FOLD_FLAG}\`): `;
752
845
  const stale = signedDocsStaleAfter(ctx, f.plan.next);
753
- const staleNote = stale.length > 0 ? `; signed doc(s) whose generated regions (e.g. the view's \`via\` annotations) go stale and will not be synced: ${stale.join(", ")} — em slice revise <key>, then em slice sync` : "";
846
+ const staleNote = stale.length > 0 ? `; signed doc(s) whose generated regions (e.g. the view's \`via\` annotations) go stale: ${stale.join(", ")} — run em slice sync afterwards (since 1.19.0 it keeps the signature: ratifiedHash covers the authored text only)` : "";
754
847
  if (f.kind === "stack") {
755
848
  const p = f.plan;
756
849
  const desc = p.groups.map((g) => `${[g.kept, ...g.dropped].map((n) => `"${n}"`).join(" + ")} → one continuation slice "${g.kept}" (key ${g.keptKey} kept; dropped keys: ${g.droppedKeys.join(", ")})`);
@@ -770,36 +863,40 @@ function applyFoldContinuations(ctx) {
770
863
  return { ok: true, changedFiles: [ctx.modelFile] };
771
864
  }
772
865
  function staleViewDocs(ctx) {
773
- const open = [];
774
- const signed = [];
866
+ const out = [];
775
867
  const r = runSliceSync(ctx.model, ctx.refs, ctx.baseDir, undefined, true);
776
868
  if (!r.ok)
777
- return { open, signed };
869
+ return out;
778
870
  for (const d of r.docs) {
779
- if (d.status !== "stale" || !d.regions.some((g) => g.status === "stale" && g.name.startsWith("em-slice-view")))
871
+ if (d.status !== "stale" || !hasStaleViewRegion(d))
780
872
  continue;
781
- (isSignedDoc(readFileSync(join(ctx.baseDir, d.path), "utf8")) ? signed : open).push({ key: d.key, path: d.path });
873
+ out.push({ key: d.key, path: d.path });
782
874
  }
783
- return { open, signed };
875
+ return out;
876
+ }
877
+ function hasStaleViewRegion(d) {
878
+ return d.regions.some((g) => g.status === "stale" && g.name.startsWith("em-slice-view"));
784
879
  }
785
880
  function detectViewDocSourcesStale(ctx) {
786
- const { open, signed } = staleViewDocs(ctx);
787
- const left = signed.length > 0 ? `; left alone (ratified or shipped — em slice revise, then em slice sync): ${signed.map((d) => d.path).join(", ")}` : "";
881
+ const open = staleViewDocs(ctx);
788
882
  if (open.length === 0) {
789
- return { applicable: false, reason: `no bound slice doc's view region is stale against the model${left}` };
883
+ return { applicable: false, reason: "no bound slice doc's view region is stale against the model" };
790
884
  }
791
885
  return {
792
886
  applicable: true,
793
- reason: `em slice sync --check would change the view region of ${open.length} doc(s): ${open.map((d) => d.path).join(", ")} — em upgrade will run em slice sync on them${left}`,
887
+ reason: `em slice sync --check would change the view region of ${open.length} doc(s): ${open.map((d) => d.path).join(", ")} — em upgrade will run em slice sync on them (a ratified or shipped doc keeps its signature)`,
794
888
  };
795
889
  }
796
890
  function applyViewDocSourcesStale(ctx) {
797
- const { open } = staleViewDocs(ctx);
891
+ const open = staleViewDocs(ctx);
798
892
  const changedFiles = [];
799
893
  for (const d of open) {
800
894
  const r = runSliceSync(ctx.model, ctx.refs, ctx.baseDir, d.key, false);
801
895
  if (!r.ok)
802
896
  return { ok: false, message: r.message };
897
+ const bad = r.docs.find((x) => x.status === "malformed");
898
+ if (bad)
899
+ return { ok: false, message: `${bad.path}: em slice sync could not rewrite it — ${bad.problems.join("; ")}` };
803
900
  changedFiles.push(join(ctx.baseDir, d.path));
804
901
  }
805
902
  return { ok: true, changedFiles };
@@ -945,9 +1042,10 @@ export const UPGRADE_STEPS = [
945
1042
  { id: "reaction-shape", sinceVersion: "1.8.0", detect: detectReactionShape, apply: applyReactionShape },
946
1043
  { id: "state-file", sinceVersion: "1.13.0", detect: detectStateFile, apply: applyStateFile },
947
1044
  { id: "ci-block", sinceVersion: "1.9.0", detect: detectCiBlock, apply: applyCiBlock },
1045
+ { id: "api-contract-missing", sinceVersion: "1.18.1", detect: detectApiContractMissing, apply: applyApiContractMissing },
948
1046
  { id: "constitution", sinceVersion: "1.11.0", detect: detectConstitution, apply: applyConstitution },
949
1047
  { id: "ratified-signoff", sinceVersion: "1.14.0", detect: detectRatifiedSignoff, apply: applyRatifiedSignoff },
950
- { id: "ratified-hash-scope", sinceVersion: "1.17.0", detect: detectRatifiedHashScope, apply: applyRatifiedHashScope },
1048
+ { id: "ratified-hash-scope", sinceVersion: "1.19.0", detect: detectRatifiedHashScope, apply: applyRatifiedHashScope },
951
1049
  { id: "collapse-alternative-events", sinceVersion: "1.18.0", detect: detectCollapseAlternativeEvents, apply: applyCollapseAlternativeEvents },
952
1050
  { id: "fold-continuations", sinceVersion: "1.18.0", detect: detectFoldContinuations, apply: applyFoldContinuations, optedIn: (c) => c.foldContinuations === true || c.toSugar === true },
953
1051
  { id: "view-doc-sources-stale", sinceVersion: "1.18.0", detect: detectViewDocSourcesStale, apply: applyViewDocSourcesStale },
@@ -1085,19 +1183,23 @@ function detectPluginInstallLocally(ctx) {
1085
1183
  }
1086
1184
  return null;
1087
1185
  }
1088
- /** GH #290: a SIGNED doc (ratified or shipped) whose generated regions are stale against the model —
1089
- * `view-doc-sources-stale` and `collapse-alternative-events` leave it alone because syncing would move
1090
- * its `ratifiedHash`. Raised for any region (view, event, command, invariants). */
1186
+ /** GH #290 / GH #347: a SIGNED doc (ratified or shipped) whose generated regions are stale against the
1187
+ * model and that no step syncs (`view-doc-sources-stale` syncs a doc with a stale view region, signed or
1188
+ * not). Since 1.19.0 the fix is mechanical — `em slice sync` keeps the signature, because
1189
+ * `ratifiedHash` covers the authored text only; model-change discipline is the model's own pin
1190
+ * (`model-versions/v<N>.json`). Raised for the event, command and invariants regions. */
1091
1191
  function detectSliceDocStaleSigned(ctx) {
1092
1192
  const r = runSliceSync(ctx.model, ctx.refs, ctx.baseDir, undefined, true);
1093
1193
  if (!r.ok)
1094
1194
  return null;
1095
- const docs = r.docs.filter((d) => d.status === "stale" && isSignedDoc(readFileSync(join(ctx.baseDir, d.path), "utf8")));
1195
+ // A doc with a stale view region is `view-doc-sources-stale`'s — unless sync could not rewrite it
1196
+ // (`signatureRefused`), in which case that step fails on it and the human item must still name it.
1197
+ const docs = r.docs.filter((d) => d.status === "stale" && (!hasStaleViewRegion(d) || d.signatureRefused !== undefined) && isSignedDoc(readFileSync(join(ctx.baseDir, d.path), "utf8")));
1096
1198
  if (docs.length === 0)
1097
1199
  return null;
1098
1200
  return {
1099
1201
  id: "slice-doc-stale-signed",
1100
- reason: `${docs.map((d) => `${d.path} (${d.regions.filter((g) => g.status === "stale").map((g) => g.name).join(", ")})`).join("; ")} — ratified or shipped and stale against the model; run \`em slice revise ${docs.map((d) => d.key).join(" / ")}\`, then \`em slice sync\``,
1202
+ reason: `${docs.map((d) => `${d.path} (${d.regions.filter((g) => g.status === "stale").map((g) => g.name).join(", ")})`).join("; ")} — ratified or shipped and stale against the model; run \`em slice sync ${basename(ctx.modelFile)}\` (it rewrites the generated regions and keeps the signature: ratifiedHash covers the authored text only)`,
1101
1203
  };
1102
1204
  }
1103
1205
  /** GH #290 / #278: a retired doc under archive/slices/ names its successor in `superseded-by`, but the
@@ -1180,6 +1282,33 @@ function detectSharedEventNowLegal(ctx) {
1180
1282
  reason: `information only: ${[...shared].join(", ")} ${shared.size === 1 ? "is" : "are"} recorded by more than one command — legal since 1.18.0 (one logical event; duplicate-name no longer fires), no model change needed`,
1181
1283
  };
1182
1284
  }
1285
+ /** GH #331: the api-check job needs a contract the mechanical step could not generate. */
1286
+ function detectApiContractBlocked(ctx) {
1287
+ const { blocked } = contractPlan(ctx);
1288
+ if (blocked.length === 0)
1289
+ return null;
1290
+ return {
1291
+ id: "api-contract-blocked",
1292
+ reason: `the contract could not be generated for ${blockedText(blocked)}, so the api-check job will fail until it exists - run \`em validate <model>\` to see why, fix it, then \`em api generate <model>\``,
1293
+ };
1294
+ }
1295
+ /** GH #346: a hand-edited `pull_request.paths` filter that lacks entries of the current preset is
1296
+ * left alone, but the owner should know why upgrade PRs may get no CI run. */
1297
+ function detectCiPathsHandEdited(ctx) {
1298
+ const ciPath = ciWorkflowPath(ctx.repoRoot);
1299
+ if (!existsSync(ciPath))
1300
+ return null;
1301
+ const content = readFileSync(ciPath, "utf8");
1302
+ if (refreshCiPullRequestPaths(content) !== null)
1303
+ return null;
1304
+ const missing = missingCiPullRequestPaths(content);
1305
+ if (!missing || missing.length === 0)
1306
+ return null;
1307
+ return {
1308
+ id: "ci-paths-hand-edited",
1309
+ reason: `em-ci.yml's hand-edited pull_request paths filter lacks ${missing.map((p) => `"${p}"`).join(", ")}, so a PR touching only those files (an \`em upgrade\` PR, for one) runs no CI; em leaves a hand-edited filter alone - add them if you want those PRs checked`,
1310
+ };
1311
+ }
1183
1312
  const HUMAN_DETECTORS = [
1184
1313
  detectNoModelVersion,
1185
1314
  detectContinuationHasOwnDoc,
@@ -1194,6 +1323,8 @@ const HUMAN_DETECTORS = [
1194
1323
  detectFoldContinuationsOptIn,
1195
1324
  detectSharedEventNowLegal,
1196
1325
  detectRosterNotInitialised,
1326
+ detectApiContractBlocked,
1327
+ detectCiPathsHandEdited,
1197
1328
  ];
1198
1329
  export function detectHumanItems(ctx) {
1199
1330
  return HUMAN_DETECTORS.map((d) => d(ctx)).filter((x) => x !== null);