@smartmemory/compose 0.3.6-beta → 0.3.8

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 (269) hide show
  1. package/.claude/skills/compose/SKILL.md +42 -88
  2. package/.compose-deps.json +1 -13
  3. package/README.md +72 -5
  4. package/bin/compose.js +754 -347
  5. package/bin/git-hooks/pre-push.template +29 -0
  6. package/bin/judgment-import.js +7 -0
  7. package/bin/judgment-migrate.js +387 -0
  8. package/contracts/comp-obs-contract.schema.json +9 -3
  9. package/contracts/feature-json.schema.json +5 -0
  10. package/contracts/fluid-record.schema.json +209 -0
  11. package/contracts/judgment-record.schema.json +425 -4
  12. package/contracts/lifecycle-backfill.schema.json +322 -0
  13. package/dist/assets/App-Z4MU-H_F.js +916 -0
  14. package/dist/assets/_baseUniq-ClWoCPFl.js +1 -0
  15. package/dist/assets/arc-DY26UIVo.js +1 -0
  16. package/dist/assets/architectureDiagram-Q4EWVU46-6Ggq4DqJ.js +36 -0
  17. package/dist/assets/blockDiagram-DXYQGD6D-CH3Ked0l.js +132 -0
  18. package/dist/assets/{browser-BSM23If2.js → browser-BWkrenen.js} +6 -6
  19. package/dist/assets/{c4Diagram-LMCZKHZV-DZf45Fbz.js → c4Diagram-AHTNJAMY-Bk8dYilu.js} +1 -1
  20. package/dist/assets/channel-SnZzzh7k.js +1 -0
  21. package/dist/assets/{chunk-JWPE2WC7-_7ujgd_Q.js → chunk-4BX2VUAB-BMR0XaAQ.js} +1 -1
  22. package/dist/assets/chunk-4TB4RGXK-JytR14a9.js +206 -0
  23. package/dist/assets/{chunk-XXDRQBXY-DfdVhbmA.js → chunk-55IACEB6-B4Q97BCP.js} +1 -1
  24. package/dist/assets/{chunk-VR4S4FIN-Dt9NZ67m.js → chunk-EDXVE4YY-R_qarkSf.js} +1 -1
  25. package/dist/assets/{chunk-5VM5RSS4-BY4_PV5H.js → chunk-FMBD7UC4-C9s7KR9m.js} +1 -1
  26. package/dist/assets/chunk-OYMX7WX6-BySQzVxc.js +231 -0
  27. package/dist/assets/{chunk-2Q5K7J3B-Dn1spZYu.js → chunk-QZHKN3VN-DdpSYZsW.js} +1 -1
  28. package/dist/assets/{chunk-32BRIVSS-pURGrJDk.js → chunk-YZCP3GAM-iE_tzriw.js} +1 -1
  29. package/dist/assets/classDiagram-6PBFFD2Q-CBu92dSH.js +1 -0
  30. package/dist/assets/classDiagram-v2-HSJHXN6E-CBu92dSH.js +1 -0
  31. package/dist/assets/clone-DgklGjHm.js +1 -0
  32. package/dist/assets/{cose-bilkent-JH36ORCC-BieYif4o.js → cose-bilkent-S5V4N54A-BdlU6ZX_.js} +1 -1
  33. package/dist/assets/dagre-KV5264BT-Cp3F5KTn.js +4 -0
  34. package/dist/assets/diagram-5BDNPKRD-DiR6_2q_.js +10 -0
  35. package/dist/assets/diagram-G4DWMVQ6-w0i-p5HX.js +24 -0
  36. package/dist/assets/diagram-MMDJMWI5-tIHhwUv3.js +43 -0
  37. package/dist/assets/diagram-TYMM5635-BAeY3B19.js +24 -0
  38. package/dist/assets/erDiagram-SMLLAGMA-Ckx_Knko.js +85 -0
  39. package/dist/assets/flowDiagram-DWJPFMVM-DeoNka6J.js +162 -0
  40. package/dist/assets/ganttDiagram-T4ZO3ILL-BmGnFbEg.js +292 -0
  41. package/dist/assets/gitGraphDiagram-UUTBAWPF-Dk48IHsx.js +106 -0
  42. package/dist/assets/graph-BNzKGvoy.js +1 -0
  43. package/dist/assets/graph-CI_1htl0.js +331 -0
  44. package/dist/assets/index-BEfrNBp8.js +123 -0
  45. package/dist/assets/index-yyrA5OZd.css +1 -0
  46. package/dist/assets/infoDiagram-42DDH7IO-BRf827i0.js +2 -0
  47. package/dist/assets/{ishikawaDiagram-FXEZZL3T-CzEB9fQS.js → ishikawaDiagram-UXIWVN3A-0kCZaeCM.js} +5 -5
  48. package/dist/assets/{journeyDiagram-5HDEW3XC-Bz8TCdz2.js → journeyDiagram-VCZTEJTY-rvU7ayRt.js} +1 -1
  49. package/dist/assets/{kanban-definition-HUTT4EX6-tozrMoV_.js → kanban-definition-6JOO6SKY-DpQwX1C5.js} +7 -7
  50. package/dist/assets/katex-DkKDou_j.js +257 -0
  51. package/dist/assets/layout-BI8cXFPI.js +1 -0
  52. package/dist/assets/{linear-Ck7gpa5N.js → linear-a0glcDiw.js} +1 -1
  53. package/dist/assets/min-vPHfnXcC.js +1 -0
  54. package/dist/assets/{mindmap-definition-LN4V7U3C-DTcHO0DJ.js → mindmap-definition-QFDTVHPH-D14eF-7C.js} +7 -7
  55. package/dist/assets/mobile-B7m9EO9D.js +17 -0
  56. package/dist/assets/pieDiagram-DEJITSTG-Cno-gETh.js +30 -0
  57. package/dist/assets/quadrantDiagram-34T5L4WZ-BUQM1Hfm.js +7 -0
  58. package/dist/assets/{requirementDiagram-TGXJPOKE-bnI2zJeT.js → requirementDiagram-MS252O5E-pOXlN2-q.js} +3 -3
  59. package/dist/assets/sankeyDiagram-XADWPNL6-Crynd3_b.js +10 -0
  60. package/dist/assets/sequenceDiagram-FGHM5R23-D9fZdCM8.js +157 -0
  61. package/dist/assets/stateDiagram-FHFEXIEX-CW9qVec8.js +1 -0
  62. package/dist/assets/stateDiagram-v2-QKLJ7IA2-DkVLzHbY.js +1 -0
  63. package/dist/assets/{timeline-definition-FHXFAJF6-D267GQFF.js → timeline-definition-GMOUNBTQ-BcHzhm_8.js} +3 -3
  64. package/dist/assets/vennDiagram-DHZGUBPP-BfytJcWk.js +34 -0
  65. package/dist/assets/wardley-RL74JXVD-DLj-IjyB.js +162 -0
  66. package/dist/assets/wardleyDiagram-NUSXRM2D-Ds0Ue68c.js +20 -0
  67. package/dist/assets/xychartDiagram-5P7HB3ND-vjWDXFL6.js +7 -0
  68. package/dist/index.html +3 -3
  69. package/lib/agent-string.js +7 -5
  70. package/lib/append-integrity.js +81 -0
  71. package/lib/backfill-evidence.js +109 -0
  72. package/lib/bug-escalation.js +39 -4
  73. package/lib/build-stream-schema.js +3 -1
  74. package/lib/build-stream-writer.js +25 -0
  75. package/lib/build.js +1624 -195
  76. package/lib/canon-guard.js +245 -0
  77. package/lib/canon-override.js +196 -0
  78. package/lib/canon-registry.js +291 -0
  79. package/lib/cli-commands.js +144 -0
  80. package/lib/codex-preflight.js +50 -15
  81. package/lib/colleague/context.js +215 -0
  82. package/lib/colleague/writeback.js +95 -0
  83. package/lib/completion-gate.js +1421 -0
  84. package/lib/completion-writer.js +47 -47
  85. package/lib/consumer-fanout.js +105 -11
  86. package/lib/coverage-gate.js +200 -0
  87. package/lib/dir-lock.js +170 -0
  88. package/lib/dispatch-ledger.js +301 -0
  89. package/lib/dispatch-metrics.js +236 -0
  90. package/lib/experiment-judge.js +6 -1
  91. package/lib/feature-json.js +1 -1
  92. package/lib/feature-reconciler.js +8 -0
  93. package/lib/feature-validator.js +64 -1
  94. package/lib/feature-writer.js +66 -2
  95. package/lib/fluid/factory.js +167 -0
  96. package/lib/fluid/ideabox-dates.js +73 -0
  97. package/lib/fluid/ideabox-migrate.js +154 -0
  98. package/lib/fluid/ideabox-ops.js +585 -0
  99. package/lib/fluid/ideabox-view.js +146 -0
  100. package/lib/fluid/import-ideabox.js +186 -0
  101. package/lib/fluid/local-provider.js +606 -0
  102. package/lib/fluid/provider.js +684 -0
  103. package/lib/fluid/record-shape.js +214 -0
  104. package/lib/fluid/record-store.js +328 -0
  105. package/lib/fluid/render-ideabox.js +261 -0
  106. package/lib/fluid/schema.js +40 -0
  107. package/lib/fluid/smartmemory-provider.js +1695 -0
  108. package/lib/gsd.js +63 -14
  109. package/lib/guard-cli.js +175 -0
  110. package/lib/guard-custody.js +141 -0
  111. package/lib/guard-descriptors.js +530 -0
  112. package/lib/guard-enrol.js +254 -0
  113. package/lib/health-score.js +1 -1
  114. package/lib/hooks-status.js +32 -3
  115. package/lib/ideabox-cli.js +315 -0
  116. package/lib/ideabox.js +121 -21
  117. package/lib/judgment/store/index.js +166 -0
  118. package/lib/judgment/store/records.js +184 -25
  119. package/lib/judgment/trace.js +380 -0
  120. package/lib/judgment-attest.js +259 -0
  121. package/lib/judgment-decision-write.js +277 -0
  122. package/lib/judgment-decisions.js +466 -0
  123. package/lib/judgment-gen.js +375 -22
  124. package/lib/judgment-verify.js +153 -0
  125. package/lib/judgment-writer.js +2842 -262
  126. package/lib/lane-gate.js +2 -0
  127. package/lib/lifecycle-modes.js +4 -4
  128. package/lib/lineage.js +400 -0
  129. package/lib/local-claude-connector.js +250 -54
  130. package/lib/maya-client.js +302 -0
  131. package/lib/maya-config.js +53 -0
  132. package/lib/maya-identity.js +283 -0
  133. package/lib/mcp-enforcement.js +21 -35
  134. package/lib/migrate-anon.js +5 -0
  135. package/lib/migrate-roadmap.js +15 -0
  136. package/lib/new.js +13 -1
  137. package/lib/pipeline-compat.js +104 -0
  138. package/lib/policy-catalog.js +295 -0
  139. package/lib/policy-check.js +0 -0
  140. package/lib/process-termination.js +98 -0
  141. package/lib/resolve-workspace.js +5 -1
  142. package/lib/result-normalizer.js +428 -153
  143. package/lib/review-normalize.js +4 -0
  144. package/lib/roadmap-errors.js +65 -0
  145. package/lib/roadmap-preservers.js +24 -4
  146. package/lib/roadmap-residue.js +299 -0
  147. package/lib/smartmemory-client.js +614 -78
  148. package/lib/smartmemory-config.js +54 -0
  149. package/lib/smartmemory-ingest.js +19 -2
  150. package/lib/step-prompt.js +7 -6
  151. package/lib/stratum-engine.js +53 -4
  152. package/lib/stratum-mcp-client.js +391 -31
  153. package/lib/test-bootstrap.js +31 -0
  154. package/lib/tool-inventory.js +122 -0
  155. package/lib/version-check.js +91 -19
  156. package/lib/vision-writer.js +88 -1
  157. package/package.json +7 -6
  158. package/pipelines/bug-fix.stratum.yaml +205 -211
  159. package/pipelines/build-quick.profiles.json +12 -0
  160. package/pipelines/build-quick.stratum.yaml +263 -350
  161. package/pipelines/content.stratum.yaml +81 -77
  162. package/pipelines/coverage-sweep.stratum.yaml +49 -30
  163. package/pipelines/plan.stratum.yaml +76 -86
  164. package/pipelines/refactor.stratum.yaml +125 -125
  165. package/pipelines/research.stratum.yaml +56 -58
  166. package/pipelines/review-fix.profiles.json +6 -0
  167. package/pipelines/review-fix.stratum.yaml +110 -83
  168. package/presets/team-feature.profiles.json +6 -0
  169. package/presets/team-feature.stratum.yaml +93 -66
  170. package/presets/team-research.profiles.json +6 -0
  171. package/presets/team-research.stratum.yaml +89 -80
  172. package/presets/team-review.profiles.json +8 -0
  173. package/presets/team-review.stratum.yaml +98 -80
  174. package/scripts/cost-census.mjs +70 -0
  175. package/scripts/guard-sign/compose-guard-sign.sh +62 -0
  176. package/server/agent-health.js +22 -0
  177. package/server/agent-hooks.js +14 -1
  178. package/server/agent-server.js +5 -248
  179. package/server/agent-spawn.js +3 -4
  180. package/server/agent-workspace.js +294 -0
  181. package/server/build-routes.js +6 -5
  182. package/server/build-stream-bridge.js +53 -0
  183. package/server/cc-session-watcher.js +4 -1
  184. package/server/coalescing-buffer.js +7 -1
  185. package/server/completion-projection.js +228 -0
  186. package/server/compose-mcp-tools.js +124 -24
  187. package/server/compose-mcp.js +91 -790
  188. package/server/decision-event-emit.js +41 -2
  189. package/server/decision-event-id.js +17 -0
  190. package/server/decision-events-snapshot.js +3 -0
  191. package/server/design-routes.js +14 -8
  192. package/server/feature-scan.js +76 -2
  193. package/server/file-watcher.js +170 -21
  194. package/server/ideabox-routes.js +166 -224
  195. package/server/index.js +70 -100
  196. package/server/lifecycle-guard.js +240 -10
  197. package/server/lifecycle-phase-history.js +276 -0
  198. package/server/maya-routes.js +507 -0
  199. package/server/mcp-tool-defs.js +940 -0
  200. package/server/mcp-tool-policy.js +35 -3
  201. package/server/model-tiers.js +22 -5
  202. package/server/pipeline-routes.js +21 -11
  203. package/server/project-root.js +58 -19
  204. package/server/remote-utils.js +3 -1
  205. package/server/schema-validator.js +7 -1
  206. package/server/session-manager.js +5 -6
  207. package/server/session-routes.js +3 -1
  208. package/server/stratum-client.js +57 -10
  209. package/server/stratum-sync.js +6 -3
  210. package/server/summarizer.js +3 -4
  211. package/server/supervisor.js +0 -1
  212. package/server/vision-routes.js +208 -98
  213. package/server/vision-server.js +86 -23
  214. package/server/vision-store.js +60 -6
  215. package/server/vision-utils.js +3 -4
  216. package/server/workspace-activity.js +18 -0
  217. package/server/workspace-middleware.js +2 -2
  218. package/server/workspace-runtime.js +243 -0
  219. package/server/worktree-gc.js +1 -0
  220. package/dist/assets/App-BG3ngu8H.js +0 -896
  221. package/dist/assets/abnfDiagram-VRR7QNED-CjB_sD3D.js +0 -1
  222. package/dist/assets/arc-_v4hR_uD.js +0 -1
  223. package/dist/assets/architectureDiagram-ZJ3FMSHR-DreJmzXQ.js +0 -36
  224. package/dist/assets/blockDiagram-677ZJIJ3-BG9-c0O1.js +0 -132
  225. package/dist/assets/channel-B3U5wFAT.js +0 -1
  226. package/dist/assets/chunk-EX3LRPZG-DdELs1qP.js +0 -231
  227. package/dist/assets/chunk-MOJQB5TN-D-ky35G-.js +0 -88
  228. package/dist/assets/chunk-RYQCIY6F-Dag_kVlO.js +0 -1
  229. package/dist/assets/chunk-V7JOEXUC-BtewURat.js +0 -206
  230. package/dist/assets/classDiagram-OUVF2IWQ-B6fCN-ht.js +0 -1
  231. package/dist/assets/classDiagram-v2-EOCWNBFH-B6fCN-ht.js +0 -1
  232. package/dist/assets/cynefin-VYW2F7L2-CT2BA6KE.js +0 -178
  233. package/dist/assets/cynefinDiagram-TSTJHNR4-Bh6exbyg.js +0 -62
  234. package/dist/assets/dagre-VKFMJZFB-aXMLSmQL.js +0 -4
  235. package/dist/assets/diagram-FQU43EPY-Dr7JAOuQ.js +0 -3
  236. package/dist/assets/diagram-G47NLZAW-DUvA3FQK.js +0 -24
  237. package/dist/assets/diagram-NH7WQ7WH-BQUARqcu.js +0 -24
  238. package/dist/assets/diagram-OA4YK3LP-dDUc1zHi.js +0 -30
  239. package/dist/assets/diagram-WEI45ONY-B2h5Qlb1.js +0 -41
  240. package/dist/assets/ebnfDiagram-CCIWWBDH-DThRGupB.js +0 -1
  241. package/dist/assets/erDiagram-Q63AITRT-BUCsprO2.js +0 -85
  242. package/dist/assets/flowDiagram-23GEKE2U-DXtNNi6r.js +0 -156
  243. package/dist/assets/ganttDiagram-NO4QXBWP-D4zbBHh_.js +0 -292
  244. package/dist/assets/gitGraphDiagram-IHSO6WYX-DpoQws0W.js +0 -106
  245. package/dist/assets/graph-BXPQrYYB.js +0 -331
  246. package/dist/assets/graph-C9eacEi8.js +0 -1
  247. package/dist/assets/index-3ZH5eMcZ.js +0 -119
  248. package/dist/assets/index-LIwREYgH.css +0 -1
  249. package/dist/assets/infoDiagram-FWYZ7A6U-Bbas2GAo.js +0 -2
  250. package/dist/assets/katex-C5jXJg4s.js +0 -257
  251. package/dist/assets/layout-DEXfKzaS.js +0 -1
  252. package/dist/assets/map-Czzmt4hB.js +0 -1
  253. package/dist/assets/mobile-CaoXUwAr.js +0 -17
  254. package/dist/assets/pegDiagram-2B236MQR-CHiINrNy.js +0 -1
  255. package/dist/assets/pieDiagram-ENE6RG2P-CfS4YFlR.js +0 -39
  256. package/dist/assets/quadrantDiagram-ABIIQ3AL-CadesS9w.js +0 -7
  257. package/dist/assets/railroadDiagram-RFXS5EU6-CgWEspBN.js +0 -1
  258. package/dist/assets/sankeyDiagram-HTMAVEWB-YWKFgOGw.js +0 -40
  259. package/dist/assets/sequenceDiagram-DBY2YBRQ-BvkNOyF9.js +0 -162
  260. package/dist/assets/sizeCapture-X5ZJPWSS-DlFPA2yO.js +0 -1
  261. package/dist/assets/stateDiagram-2N3HPSRC-h8NIx0kQ.js +0 -1
  262. package/dist/assets/stateDiagram-v2-6OUMAXLB-DjPgZtJ9.js +0 -1
  263. package/dist/assets/swimlanes-5IMT3BWC-CT5n22kG.js +0 -2
  264. package/dist/assets/swimlanesDiagram-G3AALYLV-Dn318Bhq.js +0 -8
  265. package/dist/assets/vennDiagram-L72KCM5P-Dj-wWLYG.js +0 -34
  266. package/dist/assets/wardleyDiagram-EHGQE667-BxCeYxkG.js +0 -78
  267. package/dist/assets/xychartDiagram-FW5EYKEG-DMFqWn7z.js +0 -7
  268. package/lib/staleness.js +0 -87
  269. package/server/ideabox-cache.js +0 -77
@@ -48,3 +48,57 @@ export function resolveProjectTag(cwd) {
48
48
  export function sourcePathFor(projectTag, repoRel) {
49
49
  return `compose/${projectTag}/${repoRel}`;
50
50
  }
51
+
52
+ /**
53
+ * GOV-COMPOSE-SEAM-1 step 0 (`plumbing`).
54
+ *
55
+ * Translate the `smartmemory` config block into the three env vars Stratum's
56
+ * policy client reads, so Compose's ingest events and Stratum's enforcement
57
+ * events land in ONE workspace instead of two (or, today, one and nowhere).
58
+ *
59
+ * Two traps this function exists to close:
60
+ *
61
+ * 1. `workspaceId` is two different things. Top-level `compose.json#workspaceId`
62
+ * is a Compose PROJECT SLUG (`^[a-z][a-z0-9-]{1,63}$`, see
63
+ * discover-workspaces.js) used for provenance tagging. The SmartMemory
64
+ * workspace id lives at `smartmemory.workspaceId` and looks like
65
+ * `team_26f0bbe60a4c`. Sending the slug as `X-Workspace-Id` scopes every
66
+ * event to a workspace that does not exist, and the API answers 200, so the
67
+ * failure is silent. This reads the block, never the top level.
68
+ * 2. Stratum's `SmartMemoryClient` reads `process.env` ONCE at construction
69
+ * (policy/smartmemory_client.ts). Setting these after the MCP subprocess
70
+ * starts is a no-op — they must be in the spawn env, which is why this
71
+ * returns a plain object for `connect()` to merge rather than mutating
72
+ * `process.env`.
73
+ *
74
+ * Fail-quiet by design, matching the existing fail-open ingest contract: an
75
+ * unconfigured, disabled, or half-configured workspace returns `{}` and Stratum
76
+ * simply runs without policy delivery (it warns once on its own). A partial env
77
+ * would be worse than none — it produces events addressed to nowhere.
78
+ *
79
+ * @param {string} cwd
80
+ * @returns {{SMARTMEMORY_API_URL?: string, SMARTMEMORY_API_KEY?: string, SMARTMEMORY_WORKSPACE_ID?: string}}
81
+ */
82
+ export function resolveStratumPolicyEnv(cwd) {
83
+ let cfg;
84
+ try {
85
+ cfg = getSmartmemoryConfig(cwd);
86
+ } catch {
87
+ return {};
88
+ }
89
+ if (cfg.enabled !== true) return {};
90
+
91
+ const baseUrl = typeof cfg.baseUrl === 'string' ? cfg.baseUrl.trim() : '';
92
+ // NEVER cfg.workspaceId's top-level namesake — see trap 1 above.
93
+ const workspaceId = typeof cfg.workspaceId === 'string' ? cfg.workspaceId.trim() : '';
94
+ const apiKey = cfg.apiKeyEnv ? process.env[cfg.apiKeyEnv] : undefined;
95
+
96
+ // All three or nothing.
97
+ if (!baseUrl || !workspaceId || !apiKey) return {};
98
+
99
+ return {
100
+ SMARTMEMORY_API_URL: baseUrl,
101
+ SMARTMEMORY_API_KEY: apiKey,
102
+ SMARTMEMORY_WORKSPACE_ID: workspaceId,
103
+ };
104
+ }
@@ -26,9 +26,23 @@ export function renderFeatureEventContent(row, projectTag) {
26
26
  return `[compose:${projectTag}] ${row.ts} ${row.tool} ${row.code ?? row.feature_code ?? '-'} by ${row.actor ?? 'mcp:agent'}`;
27
27
  }
28
28
 
29
- /** PURE. One deterministic line per gate-log entry. Shared with sync. */
29
+ /** PURE. One deterministic line per gate-log entry. Shared with sync.
30
+ *
31
+ * GOV-COMPOSE-SEAM-1 step 0 (F2): this is a **Vision UI product gate** — a
32
+ * human approving a feature in the Compose UI, keyed by the UI gate id and a
33
+ * feature code. It is NOT Stratum's `gate_resolution`, which is a flow-step
34
+ * gate keyed by `run_id`/`step_id` and emitted by the engine when a run carries
35
+ * a policy bundle. The design assumed these were two records of one event and
36
+ * called for deduping them by `run_id`; they are two events, and no Stratum
37
+ * run id exists at this call site (see gate-log-store.js). Deduping them would
38
+ * have destroyed a real record.
39
+ *
40
+ * So they are disambiguated instead of merged: this line is tagged
41
+ * `vision-gate` rather than the bare `gate`, so a workspace query can separate
42
+ * product approvals from enforcement gates without guessing from shape.
43
+ */
30
44
  export function renderGateLogContent(entry, projectTag) {
31
- return `[compose:${projectTag}] ${entry.timestamp} gate:${entry.decision} ${entry.feature_code ?? '-'} ${entry.id}`;
45
+ return `[compose:${projectTag}] ${entry.timestamp} vision-gate:${entry.decision} ${entry.feature_code ?? '-'} ${entry.id}`;
32
46
  }
33
47
 
34
48
  /** PURE. context for an event item (cli:compose regime — content-hash dedupe). */
@@ -45,6 +59,9 @@ export function buildGateLogContext(projectTag, entry) {
45
59
  return {
46
60
  origin: 'cli:compose',
47
61
  project: projectTag,
62
+ // Explicit record kind so a Compose product-gate approval is never mistaken
63
+ // for a Stratum enforcement `gate_resolution`. See renderGateLogContent.
64
+ record_kind: 'compose_vision_gate',
48
65
  source_path: sourcePathFor(projectTag, '.compose/data/gate-log.jsonl'),
49
66
  event: entry,
50
67
  };
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { readdirSync, readFileSync, existsSync } from 'node:fs';
6
6
  import { join } from 'node:path';
7
- import { checkStaleness } from './staleness.js';
7
+ import { findStaleArtifacts } from './lineage.js';
8
8
  import { readHypotheses, formatRejectedHypotheses } from './bug-ledger.js';
9
9
 
10
10
  // ---------------------------------------------------------------------------
@@ -242,13 +242,14 @@ export function buildGateContext(gateDispatch, context, gateExtras) {
242
242
  sections.push(`## Files Changed by This Feature\n${context.filesChanged.map(f => '- ' + f).join('\n')}`);
243
243
  }
244
244
 
245
- // Staleness warnings — flag artifacts that belong to an earlier phase
246
- if (context.featureDir && gateExtras?.toPhase) {
247
- const staleArtifacts = checkStaleness(context.featureDir, gateExtras.toPhase);
248
- const stale = staleArtifacts.filter(a => a.stale);
245
+ // Staleness warnings — derivation-based (COMP-PROV-LINEAGE): flag artifacts
246
+ // whose upstream (wasDerivedFrom) changed more recently than they did. Phase-
247
+ // independent, so it does not need gateExtras.toPhase.
248
+ if (context.featureDir) {
249
+ const stale = findStaleArtifacts(context.featureDir).filter(a => a.stale);
249
250
  if (stale.length > 0) {
250
251
  const lines = stale.map(a =>
251
- `- **${a.file}** was written in phase \`${a.writtenPhase}\` but feature is now in \`${a.currentPhase}\``
252
+ `- **${a.file}** may be outdated an upstream it was derived from (${a.staleAgainst.join(', ')}) changed more recently`
252
253
  );
253
254
  sections.push(`## Stale Artifacts\nThe following artifacts may be outdated:\n${lines.join('\n')}`);
254
255
  }
@@ -137,6 +137,46 @@ const BIN_CONFIG = {
137
137
  },
138
138
  };
139
139
 
140
+ // Sibling checkout's package.json — used only to tell the user when the
141
+ // installed dependency is shadowing a checkout at a different version.
142
+ export const LIVE_STRATUM_TS_PACKAGE_JSON = resolve(
143
+ __dirname, '..', '..', 'stratum', 'ts', 'package.json',
144
+ );
145
+
146
+ function readPackageVersion(packageJsonPath) {
147
+ try {
148
+ const version = JSON.parse(readFileSync(packageJsonPath, 'utf8'))?.version;
149
+ return typeof version === 'string' ? version : undefined;
150
+ } catch {
151
+ return undefined;
152
+ }
153
+ }
154
+
155
+ // One warning per (kind, paths, versions) per process: the resolver runs on every connect.
156
+ const shadowWarned = new Set();
157
+
158
+ /**
159
+ * The installed dependency wins over the sibling checkout by design (CI has no
160
+ * sibling). In the monorepo that silently runs a STALE stratum after a local
161
+ * rebuild — found 2026-08-30 when the STRAT-LEARN-COST census ran against
162
+ * 0.3.3 (no `stratum_usage_report`) while `stratum/ts` was at 0.3.4. Say so
163
+ * once, with the escape hatch, whenever the two versions differ.
164
+ */
165
+ function warnIfSiblingShadowed(kind, envVar, installedPackageJsonPath, siblingPackageJsonPath, warn) {
166
+ if (!siblingPackageJsonPath || !existsSync(siblingPackageJsonPath)) return;
167
+ const installedVersion = readPackageVersion(installedPackageJsonPath);
168
+ const siblingVersion = readPackageVersion(siblingPackageJsonPath);
169
+ if (!installedVersion || !siblingVersion || installedVersion === siblingVersion) return;
170
+ const key = `${kind}|${installedPackageJsonPath}|${siblingPackageJsonPath}|${installedVersion}|${siblingVersion}`;
171
+ if (shadowWarned.has(key)) return;
172
+ shadowWarned.add(key);
173
+ warn(
174
+ `[stratum-engine] using installed @smartmemory/stratum ${installedVersion}; `
175
+ + `the sibling checkout at ${dirname(siblingPackageJsonPath)} is ${siblingVersion} and is NOT what compose runs. `
176
+ + `Set ${envVar} to run a rebuilt checkout (${kind === 'mcp' ? 'dist/mcp/main.js' : 'dist/cli/stratum.js'}).`,
177
+ );
178
+ }
179
+
140
180
  /** Resolve a bin from an installed @smartmemory/stratum package, if present. */
141
181
  function resolveInstalledBin(packageBin, requireResolve) {
142
182
  let packageJsonPath;
@@ -156,7 +196,7 @@ function resolveInstalledBin(packageBin, requireResolve) {
156
196
  ? packageJson.bin?.[packageBin]
157
197
  : undefined;
158
198
  if (typeof relativeBin !== 'string') return undefined;
159
- return resolve(dirname(packageJsonPath), relativeBin);
199
+ return { bin: resolve(dirname(packageJsonPath), relativeBin), packageJsonPath };
160
200
  } catch {
161
201
  return undefined;
162
202
  }
@@ -168,7 +208,7 @@ function resolveInstalledBin(packageBin, requireResolve) {
168
208
  *
169
209
  * @param {'mcp'|'cli'} kind
170
210
  * @param {string} [cwd]
171
- * @param {{env?: object, requireResolve?: Function, siblingBins?: object}} [deps]
211
+ * @param {{env?: object, requireResolve?: Function, siblingBins?: object, siblingPackageJson?: string, warn?: Function}} [deps]
172
212
  */
173
213
  export function resolveStratumBin(kind, cwd = process.cwd(), deps = {}) {
174
214
  const config = BIN_CONFIG[kind];
@@ -180,11 +220,20 @@ export function resolveStratumBin(kind, cwd = process.cwd(), deps = {}) {
180
220
  || (kind === 'cli' ? env.COMPOSE_STRATUM_TS_BIN : undefined);
181
221
  if (envCandidate && existsSync(envCandidate)) return envCandidate;
182
222
 
183
- const installedCandidate = resolveInstalledBin(
223
+ const installed = resolveInstalledBin(
184
224
  config.packageBin,
185
225
  deps.requireResolve ?? require.resolve.bind(require),
186
226
  );
187
- if (installedCandidate && existsSync(installedCandidate)) return installedCandidate;
227
+ if (installed && existsSync(installed.bin)) {
228
+ warnIfSiblingShadowed(
229
+ kind,
230
+ config.env,
231
+ installed.packageJsonPath,
232
+ deps.siblingPackageJson ?? LIVE_STRATUM_TS_PACKAGE_JSON,
233
+ deps.warn ?? ((message) => console.warn(message)),
234
+ );
235
+ return installed.bin;
236
+ }
188
237
 
189
238
  const siblingCandidate = deps.siblingBins?.[kind] ?? config.sibling;
190
239
  if (siblingCandidate && existsSync(siblingCandidate)) return siblingCandidate;