@smartmemory/compose 0.3.7 → 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 (215) hide show
  1. package/.compose-deps.json +1 -13
  2. package/README.md +72 -5
  3. package/bin/compose.js +470 -351
  4. package/bin/judgment-migrate.js +387 -0
  5. package/contracts/comp-obs-contract.schema.json +9 -3
  6. package/contracts/fluid-record.schema.json +209 -0
  7. package/contracts/lifecycle-backfill.schema.json +322 -0
  8. package/dist/assets/App-Z4MU-H_F.js +916 -0
  9. package/dist/assets/{_baseUniq-Bo837sRJ.js → _baseUniq-ClWoCPFl.js} +1 -1
  10. package/dist/assets/{arc-BafGpyqE.js → arc-DY26UIVo.js} +1 -1
  11. package/dist/assets/{architectureDiagram-Q4EWVU46-BOBfUsqL.js → architectureDiagram-Q4EWVU46-6Ggq4DqJ.js} +1 -1
  12. package/dist/assets/{blockDiagram-DXYQGD6D-Dwodev1a.js → blockDiagram-DXYQGD6D-CH3Ked0l.js} +1 -1
  13. package/dist/assets/{browser-1ntj1-x_.js → browser-BWkrenen.js} +1 -1
  14. package/dist/assets/{c4Diagram-AHTNJAMY-CU_bhYag.js → c4Diagram-AHTNJAMY-Bk8dYilu.js} +1 -1
  15. package/dist/assets/channel-SnZzzh7k.js +1 -0
  16. package/dist/assets/{chunk-4BX2VUAB-p8WsDwnO.js → chunk-4BX2VUAB-BMR0XaAQ.js} +1 -1
  17. package/dist/assets/{chunk-4TB4RGXK-B8h7-eR0.js → chunk-4TB4RGXK-JytR14a9.js} +1 -1
  18. package/dist/assets/{chunk-55IACEB6-DxeEr98s.js → chunk-55IACEB6-B4Q97BCP.js} +1 -1
  19. package/dist/assets/{chunk-EDXVE4YY-BYt8F151.js → chunk-EDXVE4YY-R_qarkSf.js} +1 -1
  20. package/dist/assets/{chunk-FMBD7UC4-DGSOVeie.js → chunk-FMBD7UC4-C9s7KR9m.js} +1 -1
  21. package/dist/assets/{chunk-OYMX7WX6-B-QdgYR2.js → chunk-OYMX7WX6-BySQzVxc.js} +1 -1
  22. package/dist/assets/{chunk-QZHKN3VN-Du5UAZLs.js → chunk-QZHKN3VN-DdpSYZsW.js} +1 -1
  23. package/dist/assets/{chunk-YZCP3GAM-C8JbNBSk.js → chunk-YZCP3GAM-iE_tzriw.js} +1 -1
  24. package/dist/assets/classDiagram-6PBFFD2Q-CBu92dSH.js +1 -0
  25. package/dist/assets/classDiagram-v2-HSJHXN6E-CBu92dSH.js +1 -0
  26. package/dist/assets/clone-DgklGjHm.js +1 -0
  27. package/dist/assets/{cose-bilkent-S5V4N54A-O1ESaqge.js → cose-bilkent-S5V4N54A-BdlU6ZX_.js} +1 -1
  28. package/dist/assets/{dagre-KV5264BT-CPTmFPHw.js → dagre-KV5264BT-Cp3F5KTn.js} +1 -1
  29. package/dist/assets/{diagram-5BDNPKRD-B3PNrWs5.js → diagram-5BDNPKRD-DiR6_2q_.js} +1 -1
  30. package/dist/assets/{diagram-G4DWMVQ6-Cscfr6vc.js → diagram-G4DWMVQ6-w0i-p5HX.js} +1 -1
  31. package/dist/assets/{diagram-MMDJMWI5-CSfqZ-TM.js → diagram-MMDJMWI5-tIHhwUv3.js} +1 -1
  32. package/dist/assets/{diagram-TYMM5635-Cg4aYS7W.js → diagram-TYMM5635-BAeY3B19.js} +1 -1
  33. package/dist/assets/{erDiagram-SMLLAGMA-_ZqwG5pl.js → erDiagram-SMLLAGMA-Ckx_Knko.js} +1 -1
  34. package/dist/assets/{flowDiagram-DWJPFMVM-C83boxFT.js → flowDiagram-DWJPFMVM-DeoNka6J.js} +1 -1
  35. package/dist/assets/{ganttDiagram-T4ZO3ILL-CWnIjuEi.js → ganttDiagram-T4ZO3ILL-BmGnFbEg.js} +1 -1
  36. package/dist/assets/{gitGraphDiagram-UUTBAWPF-DrMdxZfH.js → gitGraphDiagram-UUTBAWPF-Dk48IHsx.js} +1 -1
  37. package/dist/assets/{graph-RE4I7Ty7.js → graph-BNzKGvoy.js} +1 -1
  38. package/dist/assets/{graph-Bi99_6Yf.js → graph-CI_1htl0.js} +1 -1
  39. package/dist/assets/{index-Rm2RE-c0.js → index-BEfrNBp8.js} +3 -3
  40. package/dist/assets/index-yyrA5OZd.css +1 -0
  41. package/dist/assets/{infoDiagram-42DDH7IO-BLmP4Epr.js → infoDiagram-42DDH7IO-BRf827i0.js} +1 -1
  42. package/dist/assets/{ishikawaDiagram-UXIWVN3A-yuWWshKN.js → ishikawaDiagram-UXIWVN3A-0kCZaeCM.js} +1 -1
  43. package/dist/assets/{journeyDiagram-VCZTEJTY-BOfhaJov.js → journeyDiagram-VCZTEJTY-rvU7ayRt.js} +1 -1
  44. package/dist/assets/{kanban-definition-6JOO6SKY-Bbolde15.js → kanban-definition-6JOO6SKY-DpQwX1C5.js} +1 -1
  45. package/dist/assets/{layout-BSf33zm8.js → layout-BI8cXFPI.js} +1 -1
  46. package/dist/assets/{linear-AvSTWMqx.js → linear-a0glcDiw.js} +1 -1
  47. package/dist/assets/{min-QBM8H4xN.js → min-vPHfnXcC.js} +1 -1
  48. package/dist/assets/{mindmap-definition-QFDTVHPH-BuvgtqIc.js → mindmap-definition-QFDTVHPH-D14eF-7C.js} +1 -1
  49. package/dist/assets/mobile-B7m9EO9D.js +17 -0
  50. package/dist/assets/{pieDiagram-DEJITSTG-DIzF16vh.js → pieDiagram-DEJITSTG-Cno-gETh.js} +1 -1
  51. package/dist/assets/{quadrantDiagram-34T5L4WZ-D-mbUIjS.js → quadrantDiagram-34T5L4WZ-BUQM1Hfm.js} +1 -1
  52. package/dist/assets/{requirementDiagram-MS252O5E-CEs4kCLd.js → requirementDiagram-MS252O5E-pOXlN2-q.js} +1 -1
  53. package/dist/assets/{sankeyDiagram-XADWPNL6-DFsnCr9n.js → sankeyDiagram-XADWPNL6-Crynd3_b.js} +1 -1
  54. package/dist/assets/{sequenceDiagram-FGHM5R23-BEJYdTjQ.js → sequenceDiagram-FGHM5R23-D9fZdCM8.js} +1 -1
  55. package/dist/assets/{stateDiagram-FHFEXIEX-BBXs57uY.js → stateDiagram-FHFEXIEX-CW9qVec8.js} +1 -1
  56. package/dist/assets/stateDiagram-v2-QKLJ7IA2-DkVLzHbY.js +1 -0
  57. package/dist/assets/{timeline-definition-GMOUNBTQ-BGvLoVAY.js → timeline-definition-GMOUNBTQ-BcHzhm_8.js} +1 -1
  58. package/dist/assets/{vennDiagram-DHZGUBPP-9LaBTMe0.js → vennDiagram-DHZGUBPP-BfytJcWk.js} +1 -1
  59. package/dist/assets/{wardley-RL74JXVD-P4MEqMTP.js → wardley-RL74JXVD-DLj-IjyB.js} +1 -1
  60. package/dist/assets/{wardleyDiagram-NUSXRM2D-o-tmxnlC.js → wardleyDiagram-NUSXRM2D-Ds0Ue68c.js} +1 -1
  61. package/dist/assets/{xychartDiagram-5P7HB3ND-Dpn7V6qk.js → xychartDiagram-5P7HB3ND-vjWDXFL6.js} +1 -1
  62. package/dist/index.html +3 -3
  63. package/lib/agent-string.js +7 -5
  64. package/lib/append-integrity.js +81 -0
  65. package/lib/backfill-evidence.js +109 -0
  66. package/lib/bug-escalation.js +9 -0
  67. package/lib/build-stream-schema.js +3 -1
  68. package/lib/build-stream-writer.js +25 -0
  69. package/lib/build.js +874 -170
  70. package/lib/canon-guard.js +28 -6
  71. package/lib/canon-override.js +196 -0
  72. package/lib/canon-registry.js +104 -0
  73. package/lib/cli-commands.js +144 -0
  74. package/lib/codex-preflight.js +26 -13
  75. package/lib/colleague/context.js +215 -0
  76. package/lib/colleague/writeback.js +95 -0
  77. package/lib/completion-gate.js +1421 -0
  78. package/lib/completion-writer.js +47 -47
  79. package/lib/consumer-fanout.js +105 -11
  80. package/lib/coverage-gate.js +200 -0
  81. package/lib/dir-lock.js +170 -0
  82. package/lib/dispatch-ledger.js +3 -3
  83. package/lib/feature-json.js +1 -1
  84. package/lib/feature-reconciler.js +8 -0
  85. package/lib/feature-validator.js +64 -1
  86. package/lib/feature-writer.js +57 -2
  87. package/lib/fluid/factory.js +167 -0
  88. package/lib/fluid/ideabox-dates.js +73 -0
  89. package/lib/fluid/ideabox-migrate.js +154 -0
  90. package/lib/fluid/ideabox-ops.js +585 -0
  91. package/lib/fluid/ideabox-view.js +146 -0
  92. package/lib/fluid/import-ideabox.js +186 -0
  93. package/lib/fluid/local-provider.js +606 -0
  94. package/lib/fluid/provider.js +684 -0
  95. package/lib/fluid/record-shape.js +214 -0
  96. package/lib/fluid/record-store.js +328 -0
  97. package/lib/fluid/render-ideabox.js +261 -0
  98. package/lib/fluid/schema.js +40 -0
  99. package/lib/fluid/smartmemory-provider.js +1695 -0
  100. package/lib/gsd.js +63 -23
  101. package/lib/guard-cli.js +175 -0
  102. package/lib/guard-custody.js +141 -0
  103. package/lib/guard-descriptors.js +530 -0
  104. package/lib/guard-enrol.js +254 -0
  105. package/lib/health-score.js +1 -1
  106. package/lib/ideabox-cli.js +315 -0
  107. package/lib/ideabox.js +121 -21
  108. package/lib/judgment/store/index.js +9 -1
  109. package/lib/judgment/store/records.js +1 -1
  110. package/lib/judgment/trace.js +380 -0
  111. package/lib/judgment-decision-write.js +277 -0
  112. package/lib/judgment-decisions.js +466 -0
  113. package/lib/judgment-gen.js +5 -1
  114. package/lib/judgment-writer.js +56 -2
  115. package/lib/lifecycle-modes.js +4 -4
  116. package/lib/lineage.js +400 -0
  117. package/lib/local-claude-connector.js +52 -1
  118. package/lib/maya-client.js +302 -0
  119. package/lib/maya-config.js +53 -0
  120. package/lib/maya-identity.js +283 -0
  121. package/lib/migrate-anon.js +5 -0
  122. package/lib/migrate-roadmap.js +15 -0
  123. package/lib/new.js +13 -1
  124. package/lib/pipeline-compat.js +104 -0
  125. package/lib/policy-catalog.js +295 -0
  126. package/lib/policy-check.js +0 -0
  127. package/lib/process-termination.js +98 -0
  128. package/lib/resolve-workspace.js +5 -1
  129. package/lib/result-normalizer.js +396 -199
  130. package/lib/roadmap-errors.js +65 -0
  131. package/lib/roadmap-preservers.js +24 -4
  132. package/lib/roadmap-residue.js +299 -0
  133. package/lib/smartmemory-client.js +614 -78
  134. package/lib/smartmemory-config.js +54 -0
  135. package/lib/smartmemory-ingest.js +19 -2
  136. package/lib/step-prompt.js +7 -6
  137. package/lib/stratum-engine.js +53 -4
  138. package/lib/stratum-mcp-client.js +271 -36
  139. package/lib/test-bootstrap.js +31 -0
  140. package/lib/tool-inventory.js +122 -0
  141. package/lib/version-check.js +91 -19
  142. package/lib/vision-writer.js +88 -1
  143. package/package.json +7 -6
  144. package/pipelines/bug-fix.stratum.yaml +205 -211
  145. package/pipelines/build-quick.profiles.json +12 -0
  146. package/pipelines/build-quick.stratum.yaml +263 -350
  147. package/pipelines/content.stratum.yaml +81 -77
  148. package/pipelines/coverage-sweep.stratum.yaml +49 -30
  149. package/pipelines/plan.stratum.yaml +76 -86
  150. package/pipelines/refactor.stratum.yaml +125 -125
  151. package/pipelines/research.stratum.yaml +56 -58
  152. package/pipelines/review-fix.profiles.json +6 -0
  153. package/pipelines/review-fix.stratum.yaml +110 -83
  154. package/presets/team-feature.profiles.json +6 -0
  155. package/presets/team-feature.stratum.yaml +93 -66
  156. package/presets/team-research.profiles.json +6 -0
  157. package/presets/team-research.stratum.yaml +89 -80
  158. package/presets/team-review.profiles.json +8 -0
  159. package/presets/team-review.stratum.yaml +98 -80
  160. package/scripts/cost-census.mjs +70 -0
  161. package/scripts/guard-sign/compose-guard-sign.sh +62 -0
  162. package/server/agent-health.js +22 -0
  163. package/server/agent-hooks.js +14 -1
  164. package/server/agent-server.js +5 -248
  165. package/server/agent-spawn.js +3 -4
  166. package/server/agent-workspace.js +294 -0
  167. package/server/build-routes.js +6 -5
  168. package/server/build-stream-bridge.js +53 -0
  169. package/server/cc-session-watcher.js +4 -1
  170. package/server/coalescing-buffer.js +7 -1
  171. package/server/completion-projection.js +228 -0
  172. package/server/compose-mcp-tools.js +109 -23
  173. package/server/compose-mcp.js +88 -882
  174. package/server/decision-event-emit.js +41 -2
  175. package/server/decision-event-id.js +17 -0
  176. package/server/decision-events-snapshot.js +3 -0
  177. package/server/design-routes.js +14 -8
  178. package/server/feature-scan.js +76 -2
  179. package/server/file-watcher.js +170 -21
  180. package/server/ideabox-routes.js +166 -224
  181. package/server/index.js +70 -100
  182. package/server/lifecycle-guard.js +240 -10
  183. package/server/lifecycle-phase-history.js +276 -0
  184. package/server/maya-routes.js +507 -0
  185. package/server/mcp-tool-defs.js +940 -0
  186. package/server/mcp-tool-policy.js +34 -2
  187. package/server/model-tiers.js +22 -5
  188. package/server/pipeline-routes.js +21 -11
  189. package/server/project-root.js +58 -19
  190. package/server/remote-utils.js +3 -1
  191. package/server/schema-validator.js +7 -1
  192. package/server/session-manager.js +5 -6
  193. package/server/session-routes.js +3 -1
  194. package/server/stratum-client.js +57 -10
  195. package/server/stratum-sync.js +6 -3
  196. package/server/summarizer.js +3 -4
  197. package/server/supervisor.js +0 -1
  198. package/server/vision-routes.js +208 -98
  199. package/server/vision-server.js +86 -23
  200. package/server/vision-store.js +60 -6
  201. package/server/vision-utils.js +3 -4
  202. package/server/workspace-activity.js +18 -0
  203. package/server/workspace-middleware.js +2 -2
  204. package/server/workspace-runtime.js +243 -0
  205. package/server/worktree-gc.js +1 -0
  206. package/dist/assets/App-PkZzHeMj.js +0 -894
  207. package/dist/assets/channel-qVK_qn4E.js +0 -1
  208. package/dist/assets/classDiagram-6PBFFD2Q-B8UcfC1q.js +0 -1
  209. package/dist/assets/classDiagram-v2-HSJHXN6E-B8UcfC1q.js +0 -1
  210. package/dist/assets/clone-Pu3RyLUh.js +0 -1
  211. package/dist/assets/index-LIwREYgH.css +0 -1
  212. package/dist/assets/mobile-BnXEOE3U.js +0 -17
  213. package/dist/assets/stateDiagram-v2-QKLJ7IA2-BqKuX4rj.js +0 -1
  214. package/lib/staleness.js +0 -87
  215. package/server/ideabox-cache.js +0 -77
@@ -0,0 +1,940 @@
1
+ /**
2
+ * mcp-tool-defs.js — the compose MCP tool definitions.
3
+ *
4
+ * Extracted from server/compose-mcp.js (COMP-COVERAGE-GATE slice 1) so the
5
+ * declarations can be imported WITHOUT booting the stdio server: compose-mcp.js
6
+ * connects a StdioServerTransport at module load, so importing it for `TOOLS`
7
+ * hangs any consumer (lib/tool-inventory.js, its contract test, validate).
8
+ *
9
+ * Data only — no I/O, no imports. Each entry declares `effect` and, when
10
+ * mutating, `writes` (canon-registry entry ids). See lib/tool-inventory.js.
11
+ */
12
+ export const TOOLS = [
13
+ {
14
+ name: 'get_vision_items',
15
+ effect: 'read',
16
+ description: 'Query Compose tracker items. Filter by phase, status, type, or keyword. Returns id, title, type, phase, status, confidence, description.',
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: {
20
+ phase: {
21
+ type: 'string',
22
+ description: 'Filter by phase: vision, requirements, design, planning, implementation, verification, release',
23
+ },
24
+ status: {
25
+ type: 'string',
26
+ description: 'Filter by status (comma-separated for multiple): planned, in_progress, complete, blocked, parked, killed',
27
+ },
28
+ type: {
29
+ type: 'string',
30
+ description: 'Filter by type: task, decision, evaluation, idea, spec, thread, artifact, question, feature, track',
31
+ },
32
+ keyword: {
33
+ type: 'string',
34
+ description: 'Search keyword matched against title and description',
35
+ },
36
+ limit: {
37
+ type: 'number',
38
+ description: 'Max results to return (default 30)',
39
+ },
40
+ },
41
+ },
42
+ },
43
+ {
44
+ name: 'get_item_detail',
45
+ effect: 'read',
46
+ description: 'Get full detail for a single tracker item including all its connections.',
47
+ inputSchema: {
48
+ type: 'object',
49
+ properties: {
50
+ id: {
51
+ type: 'string',
52
+ description: 'Item ID (UUID) or semanticId/slug',
53
+ },
54
+ },
55
+ required: ['id'],
56
+ },
57
+ },
58
+ {
59
+ name: 'get_phase_summary',
60
+ effect: 'read',
61
+ description: 'Get status and type distribution for a phase (or all phases). Useful for understanding overall project health.',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ phase: {
66
+ type: 'string',
67
+ description: 'Phase to summarize: vision, requirements, design, planning, implementation, verification, release. Omit for all phases.',
68
+ },
69
+ },
70
+ },
71
+ },
72
+ {
73
+ name: 'get_blocked_items',
74
+ effect: 'read',
75
+ description: 'List all tracker items that are blocked by non-complete items.',
76
+ inputSchema: {
77
+ type: 'object',
78
+ properties: {},
79
+ },
80
+ },
81
+ {
82
+ name: 'get_current_session',
83
+ effect: 'setup',
84
+ description: 'Get the most recent session: tool count, items touched, error count, and recent Haiku summaries of what was accomplished.',
85
+ inputSchema: {
86
+ type: 'object',
87
+ properties: {
88
+ featureCode: { type: 'string', description: 'Optional: get context for a specific feature' },
89
+ },
90
+ },
91
+ },
92
+ {
93
+ name: 'bind_session',
94
+ effect: 'setup',
95
+ description: 'Bind the current agent session to a lifecycle feature. Call once per session after creating/identifying the feature. Binding is one-shot — calling again on a bound session returns already_bound.',
96
+ inputSchema: {
97
+ type: 'object',
98
+ properties: {
99
+ featureCode: { type: 'string', description: 'The feature code (e.g., "gate-ui")' },
100
+ },
101
+ required: ['featureCode'],
102
+ },
103
+ },
104
+ {
105
+ name: 'set_workspace',
106
+ effect: 'setup',
107
+ description: 'Bind this MCP session to a workspace. Required when cwd contains multiple workspaces. Lives in process memory; lost on MCP restart.',
108
+ inputSchema: {
109
+ type: 'object',
110
+ required: ['workspaceId'],
111
+ properties: { workspaceId: { type: 'string', description: 'Workspace ID (kebab-case)' } },
112
+ },
113
+ },
114
+ {
115
+ name: 'get_workspace',
116
+ effect: 'setup',
117
+ description: 'Get the current MCP workspace binding plus all candidates discovered from cwd.',
118
+ inputSchema: { type: 'object', properties: {} },
119
+ },
120
+ {
121
+ name: 'get_feature_lifecycle',
122
+ effect: 'read',
123
+ description: 'Get the lifecycle state of a feature: current phase, phase history, artifacts, warnings.',
124
+ inputSchema: {
125
+ type: 'object',
126
+ properties: {
127
+ id: { type: 'string', description: 'Item ID (UUID) or slug' },
128
+ },
129
+ required: ['id'],
130
+ },
131
+ },
132
+ {
133
+ name: 'kill_feature',
134
+ effect: 'mutating',
135
+ writes: ["feature-json"],
136
+ description: 'Kill a feature from any phase. Records reason and sets status to killed.',
137
+ inputSchema: {
138
+ type: 'object',
139
+ properties: {
140
+ id: { type: 'string', description: 'Item ID' },
141
+ reason: { type: 'string', description: 'Why the feature is being killed' },
142
+ },
143
+ required: ['id', 'reason'],
144
+ },
145
+ },
146
+ {
147
+ name: 'complete_feature',
148
+ effect: 'mutating',
149
+ writes: ["feature-json"],
150
+ description: 'Mark a feature as complete. Only callable from the ship phase. For a managed build-mode feature (one with a feature.json) this goes through the completion gate (COMP-COMPLETION-GATE): the commit is verified and tests attested when capabilities.guard is on, ONE guarded transition is ledgered, and the gate writes the completion record, feature.status COMPLETE, ROADMAP.md and the cockpit item. A refusal (bad evidence, KILLED feature, guard refused) returns 422 and writes nothing. commit_sha is required under the guard; with the guard off a commit-less completion records a no-commit completion. Fix/plan items and items with no feature.json transition as before.',
151
+ inputSchema: {
152
+ type: 'object',
153
+ properties: {
154
+ id: { type: 'string', description: 'Item ID' },
155
+ commit_sha: { type: 'string', description: 'Full 40-char commit SHA. Required to write a completion record.' },
156
+ tests_pass: { type: 'boolean', description: 'Defaults to true when commit_sha is provided.' },
157
+ files_changed: { type: 'array', items: { type: 'string' }, description: 'Repo-relative paths committed in the SHA.' },
158
+ notes: { type: 'string', description: 'One-line note for the completion record.' },
159
+ },
160
+ required: ['id'],
161
+ },
162
+ },
163
+ {
164
+ name: 'start_iteration_loop',
165
+ effect: 'mutating',
166
+ writes: [],
167
+ description: 'Start a review or coverage iteration loop on a feature. Returns loop state.',
168
+ inputSchema: {
169
+ type: 'object',
170
+ properties: {
171
+ id: { type: 'string', description: 'Item ID or semanticId' },
172
+ loopType: { type: 'string', enum: ['review', 'coverage'], description: 'Type of iteration loop' },
173
+ maxIterations: { type: 'number', description: 'Override max iterations (optional, defaults from settings)' },
174
+ },
175
+ required: ['id', 'loopType'],
176
+ },
177
+ },
178
+ {
179
+ name: 'report_iteration_result',
180
+ effect: 'mutating',
181
+ writes: [],
182
+ description: 'Report one iteration result. Compose evaluates exit criteria and returns whether to continue.',
183
+ inputSchema: {
184
+ type: 'object',
185
+ properties: {
186
+ id: { type: 'string', description: 'Item ID or semanticId' },
187
+ result: { type: 'object', description: 'Iteration result. Review: {clean: bool, findings: []}, Coverage: {passing: bool, failures: []}' },
188
+ },
189
+ required: ['id', 'result'],
190
+ },
191
+ },
192
+ {
193
+ name: 'abort_iteration_loop',
194
+ effect: 'mutating',
195
+ writes: [],
196
+ description: 'Abort the current iteration loop early.',
197
+ inputSchema: {
198
+ type: 'object',
199
+ properties: {
200
+ id: { type: 'string', description: 'Item ID or semanticId' },
201
+ reason: { type: 'string', description: 'Why the loop was aborted' },
202
+ },
203
+ required: ['id'],
204
+ },
205
+ },
206
+ {
207
+ name: 'assess_feature_artifacts',
208
+ effect: 'read',
209
+ description: 'Assess quality signals for all artifacts of a feature: section completeness, word count, last modified.',
210
+ inputSchema: {
211
+ type: 'object',
212
+ properties: {
213
+ featureCode: { type: 'string', description: 'Feature folder name (e.g. "artifact-awareness")' },
214
+ },
215
+ required: ['featureCode'],
216
+ },
217
+ },
218
+ {
219
+ name: 'scaffold_feature',
220
+ effect: 'mutating',
221
+ writes: [],
222
+ description: 'Create feature folder with template stubs for all phase artifacts. Existing files are never overwritten.',
223
+ inputSchema: {
224
+ type: 'object',
225
+ properties: {
226
+ featureCode: { type: 'string', description: 'Feature folder name' },
227
+ only: {
228
+ type: 'array',
229
+ items: { type: 'string' },
230
+ description: 'Limit to specific artifacts (e.g. ["design.md", "blueprint.md"]). Omit for all.',
231
+ },
232
+ },
233
+ required: ['featureCode'],
234
+ },
235
+ },
236
+ {
237
+ name: 'approve_gate',
238
+ effect: 'mutating',
239
+ writes: [],
240
+ description: 'Resolve a pending policy gate. Outcomes: approved (proceed), revised (stay in phase), killed (abandon feature).',
241
+ inputSchema: {
242
+ type: 'object',
243
+ properties: {
244
+ gateId: { type: 'string', description: 'Gate ID' },
245
+ outcome: { type: 'string', enum: ['approved', 'revised', 'killed'], description: 'Resolution outcome' },
246
+ comment: { type: 'string', description: 'Optional human feedback' },
247
+ },
248
+ required: ['gateId', 'outcome'],
249
+ },
250
+ },
251
+ {
252
+ name: 'get_pending_gates',
253
+ effect: 'read',
254
+ description: 'List pending policy gates. Optionally filter by item ID.',
255
+ inputSchema: {
256
+ type: 'object',
257
+ properties: {
258
+ itemId: { type: 'string', description: 'Filter to gates for a specific item (optional)' },
259
+ },
260
+ },
261
+ },
262
+ // `agent_run` tool removed 2026-04-18 (STRAT-DEDUP-AGENTRUN v1); LLM-facing
263
+ // dispatch goes through `mcp__stratum__stratum_agent_run`.
264
+
265
+ // -------------------------------------------------------------------------
266
+ // Roadmap writers — COMP-MCP-ROADMAP-WRITER
267
+ // -------------------------------------------------------------------------
268
+ {
269
+ name: 'add_roadmap_entry',
270
+ effect: 'mutating',
271
+ writes: ["feature-json", "roadmap"],
272
+ description: 'Register a new feature in the project. Writes feature.json and regenerates ROADMAP.md (audit-log append is best-effort). Use this instead of editing ROADMAP.md by hand.',
273
+ inputSchema: {
274
+ type: 'object',
275
+ required: ['code', 'description', 'phase'],
276
+ properties: {
277
+ code: { type: 'string', description: 'Unique feature code (e.g. "COMP-FOO-1"). Must be uppercase A-Z, digits, dashes; cannot start or end with a dash.' },
278
+ description: { type: 'string', description: 'One-line description for the ROADMAP cell' },
279
+ phase: { type: 'string', description: 'Phase heading (e.g. "Phase 6: MCP Writers"). Required.' },
280
+ complexity: { type: 'string', enum: ['S', 'M', 'L', 'XL'] },
281
+ status: { type: 'string', enum: ['PLANNED', 'IN_PROGRESS', 'PARTIAL', 'COMPLETE', 'BLOCKED', 'KILLED', 'PARKED', 'SUPERSEDED'], description: 'Initial status (default PLANNED)' },
282
+ position: { type: 'number', description: 'Sort order within phase' },
283
+ parent: { type: 'string', description: 'Parent feature code, for cross-references' },
284
+ tags: { type: 'array', items: { type: 'string' } },
285
+ profile: { type: 'object', description: 'Triage build profile (needs_prd/needs_architecture/etc.). Set by the plan lifecycle so build can skip fresh triage.' },
286
+ triageTimestamp: { type: 'string', description: 'Triage cache stamp (ISO 8601). Set AFTER design.md is written so build treats triage as a cached no-op.' },
287
+ plannedBy: { type: 'string', description: 'Originating plan session code (e.g. "PLAN-WIDGET"). Marks a plan-authored, build-ready feature.' },
288
+ impact: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Estimated impact, carried from the plan/ideabox estimation step.' },
289
+ force: { type: 'boolean', description: 'Bypass the pre-commit roundtrip guard (commit even if ROADMAP.md would not be a generation fixed point).' },
290
+ idempotency_key: { type: 'string', description: 'Optional caller-provided key. Same key replays return the cached result without re-mutating.' },
291
+ },
292
+ },
293
+ },
294
+ {
295
+ name: 'set_feature_status',
296
+ effect: 'mutating',
297
+ writes: ["feature-json", "roadmap"],
298
+ description: 'Flip a feature status. Updates feature.json and regenerates ROADMAP.md. Enforces a transition policy (use force: true to bypass). Appends an audit event (best-effort).',
299
+ inputSchema: {
300
+ type: 'object',
301
+ required: ['code', 'status'],
302
+ properties: {
303
+ code: { type: 'string' },
304
+ status: { type: 'string', enum: ['PLANNED', 'IN_PROGRESS', 'PARTIAL', 'COMPLETE', 'BLOCKED', 'KILLED', 'PARKED', 'SUPERSEDED'] },
305
+ reason: { type: 'string', description: 'Free-form reason persisted in the audit event' },
306
+ commit_sha: { type: 'string', description: 'Optional commit binding' },
307
+ force: { type: 'boolean', description: 'Bypass the transition policy AND the roundtrip fixed-point guard (commits even if ROADMAP.md would not be a generation fixed point). Recorded in audit.' },
308
+ idempotency_key: { type: 'string' },
309
+ },
310
+ },
311
+ },
312
+ {
313
+ name: 'roadmap_diff',
314
+ effect: 'read',
315
+ description: 'Read the feature-management audit log for a window. Returns events plus derived added[] and status_changed[] arrays.',
316
+ inputSchema: {
317
+ type: 'object',
318
+ properties: {
319
+ since: { type: 'string', description: 'Window: shorthand like "24h"/"7d"/"30m", or an ISO date. Default 24h.' },
320
+ feature_code: { type: 'string' },
321
+ tool: { type: 'string', description: 'Filter to one tool name, e.g. "set_feature_status"' },
322
+ },
323
+ },
324
+ },
325
+ {
326
+ name: 'get_roadmap',
327
+ effect: 'read',
328
+ description: 'Read the current roadmap rendered from canon (feature.json) WITHOUT writing. Returns a status summary, the active/blocked convenience lists, and a staleness flag vs on-disk ROADMAP.md. Pass a status/phase filter or a limit to also get a general `rows` list (e.g. {status:"PLANNED", limit:10} for "what to work on next") — structured rows so callers never re-parse the markdown. Narrative-owned workspaces return the hand-authored file verbatim. Read-only — prefer this over reading ROADMAP.md directly.',
329
+ inputSchema: {
330
+ type: 'object',
331
+ properties: {
332
+ status: { type: 'string', description: 'Filter active/blocked + rows by status (comma-separated): PLANNED, IN_PROGRESS, PARTIAL, BLOCKED, COMPLETE, …' },
333
+ phase: { type: 'string', description: 'Filter active/blocked + rows to a single phase (matched against phaseId)' },
334
+ format: { type: 'string', description: '"summary" (default — counts + lists, token-safe) or "markdown" (full rendered text)' },
335
+ check_drift: { type: 'boolean', description: 'Compare the render against on-disk ROADMAP.md and set stale/drift (default true)' },
336
+ limit: { type: 'integer', minimum: 0, description: 'Cap on the general `rows` list (default 50). Supplying status/phase/limit emits rows[]/rowsTotal/rowsTruncated; without any of them rows is omitted (token-safe summary). A finite value is floored and clamped to ≥ 0.' },
337
+ },
338
+ },
339
+ },
340
+ {
341
+ name: 'validate_feature',
342
+ effect: 'read',
343
+ description: 'Cross-check a single feature against ROADMAP, vision-state, feature.json, folder contents, linked artifacts, and cross-references. Returns structured findings with severity (error/warning/info). FEATURE_NOT_FOUND emitted as a finding (not thrown) when the code matches strict regex but exists in no source.',
344
+ inputSchema: {
345
+ type: 'object',
346
+ required: ['feature_code'],
347
+ properties: {
348
+ feature_code: { type: 'string', description: 'Strict feature code, e.g. "COMP-MCP-VALIDATE"' },
349
+ external_prefixes: { type: 'array', items: { type: 'string' }, description: 'Code prefixes (e.g. ["STRAT-"]) treated as external; downgrades ORPHAN_FOLDER to info' },
350
+ feature_json_mode: { type: 'boolean', description: 'Default true. Set false to skip feature.json comparisons in legacy projects.' },
351
+ },
352
+ },
353
+ },
354
+ {
355
+ name: 'validate_project',
356
+ effect: 'read',
357
+ description: 'Run validate_feature for every code in vision-state, ROADMAP, and folders, plus cross-cutting checks (orphan folders, dangling cross-refs, CHANGELOG references, journal index drift) and read-only external-reference staleness (kind:"external" links + xref: roadmap citations). external:true enables network resolution of github refs (off by default — github refs then emit XREF_RESOLUTION_SKIPPED). Returns the union of all findings.',
358
+ inputSchema: {
359
+ type: 'object',
360
+ properties: {
361
+ external_prefixes: { type: 'array', items: { type: 'string' } },
362
+ feature_json_mode: { type: 'boolean' },
363
+ external: { type: 'boolean', description: 'Resolve github external refs over the network (read-only). Default false: github refs degrade to XREF_RESOLUTION_SKIPPED.' },
364
+ fix: { type: 'boolean', description: 'COMP-MCP-VALIDATE-2: reconcile mechanical drift. Returns a fix plan under `reconcile` (dry-run unless apply:true). Local-provider only.' },
365
+ apply: { type: 'boolean', description: 'With fix:true, write the fixes and re-validate. Default false (dry-run plan only).' },
366
+ fix_classes: { type: 'array', items: { type: 'string' }, description: 'Override the enabled fix classes: dangling_link, invalid_link_kind, status_fj_vision, partial_age, roadmap_status_rewrite, invalid_link_kind_repair. Default: the non-destructive set.' },
367
+ },
368
+ },
369
+ },
370
+ {
371
+ name: 'roadmap_graph',
372
+ effect: 'read',
373
+ description: 'COMP-ROADMAP-GRAPH-1: generate a self-contained roadmap dependency-graph HTML from feature.json status/phase + per-feature deps.yaml edges + display frontmatter. Drops COMPLETE/SUPERSEDED/KILLED nodes; refuses (DANGLING_EDGE) when any edge points at an unknown feature. Deterministic/idempotent. Returns a small summary (path + counts + warnings), never the HTML body.',
374
+ inputSchema: {
375
+ type: 'object',
376
+ properties: {
377
+ project: { type: 'string', description: 'Project root path. Default: current workspace.' },
378
+ out: { type: 'string', description: 'Output HTML path (relative to project root unless absolute). Default: compose.json#roadmap_graph.out or roadmap-graph.html.' },
379
+ },
380
+ },
381
+ },
382
+ {
383
+ name: 'roadmap_graph_check',
384
+ effect: 'read',
385
+ description: 'COMP-ROADMAP-GRAPH-1: render the roadmap graph in-memory and diff against the on-disk HTML without writing. Returns { matches, exists, diffSummary, counts, warnings }. matches:false means the file is stale/missing — run roadmap_graph. Raises DANGLING_EDGE on a bad edge.',
386
+ inputSchema: {
387
+ type: 'object',
388
+ properties: {
389
+ project: { type: 'string', description: 'Project root path. Default: current workspace.' },
390
+ out: { type: 'string', description: 'Output HTML path to compare against. Default: compose.json#roadmap_graph.out or roadmap-graph.html.' },
391
+ },
392
+ },
393
+ },
394
+ {
395
+ name: 'roadmap_xref_push',
396
+ effect: 'mutating',
397
+ writes: [],
398
+ description: 'COMP-ROADMAP-XREF-PUSH: write external trackers to match feature.json `expect=`/`expect_labels` declared intent (the write-side counterpart to roadmap xref-sync Pull). DRY-RUN by default — returns {pushed, skipped, unchanged, scanned} describing what WOULD change. Only links with `push: true` are eligible. apply:true performs the writes: github issue open/closed + additive labels (never removes; PR-backed refs skipped), and local refs via the sibling repo\'s own setFeatureStatus. Degrades (skips, never guesses) on offline/no-token/404/non-2xx/disallowed-transition. Returns a small summary, never a large body.',
399
+ inputSchema: {
400
+ type: 'object',
401
+ properties: {
402
+ project: { type: 'string', description: 'Project root path. Default: current workspace.' },
403
+ apply: { type: 'boolean', description: 'Perform the writes. Default false (dry-run — report only).' },
404
+ },
405
+ },
406
+ },
407
+ {
408
+ name: 'propose_followup',
409
+ effect: 'mutating',
410
+ writes: ["feature-json", "roadmap"],
411
+ description: 'File a follow-up feature against a parent. Auto-numbers the next code in the parent\'s namespace (parent_code-N), adds the ROADMAP row, links surfaced_by from new → parent, and scaffolds design.md with a "## Why" rationale block. Idempotent on (parent_code, idempotency_key); resumes across partial failures via an inflight ledger.',
412
+ inputSchema: {
413
+ type: 'object',
414
+ required: ['parent_code', 'description', 'rationale'],
415
+ properties: {
416
+ parent_code: { type: 'string', description: 'Parent feature code (e.g. "COMP-MCP-MIGRATION"). Must exist; must not be KILLED/SUPERSEDED.' },
417
+ description: { type: 'string', description: 'One-line description for the ROADMAP cell.' },
418
+ rationale: { type: 'string', description: 'Why this follow-up exists. Persisted as a "## Why" block in the new design.md and in the audit event.' },
419
+ complexity: { type: 'string', enum: ['S', 'M', 'L', 'XL'] },
420
+ phase: { type: 'string', description: 'Phase heading. Defaults to the parent\'s phase if omitted.' },
421
+ status: { type: 'string', enum: ['PLANNED', 'IN_PROGRESS', 'PARTIAL', 'COMPLETE', 'BLOCKED', 'KILLED', 'PARKED', 'SUPERSEDED'] },
422
+ idempotency_key: { type: 'string', description: 'Optional retry-safety key. Without it, repeated calls allocate new codes.' },
423
+ },
424
+ },
425
+ },
426
+
427
+ // -------------------------------------------------------------------------
428
+ // Linker — COMP-MCP-ARTIFACT-LINKER
429
+ // -------------------------------------------------------------------------
430
+ {
431
+ name: 'link_artifact',
432
+ effect: 'mutating',
433
+ writes: ["feature-json"],
434
+ description: 'Register a non-canonical artifact (snapshot, journal entry, finding, etc.) on a feature. Canonical artifacts (design.md, plan.md, …) inside the feature folder are auto-discovered and rejected here. Stores in feature.json artifacts[]; dedups on (type, path); appends an audit event (best-effort).',
435
+ inputSchema: {
436
+ type: 'object',
437
+ required: ['feature_code', 'artifact_type', 'path'],
438
+ properties: {
439
+ feature_code: { type: 'string' },
440
+ artifact_type: { type: 'string', description: 'e.g. "journal", "snapshot", "finding", "report-supplement", "link", "external"' },
441
+ path: { type: 'string', description: 'Repo-relative path. Must exist; cannot contain ".." after normalization.' },
442
+ status: { type: 'string', enum: ['current', 'superseded', 'historical'] },
443
+ force: { type: 'boolean', description: 'Overwrite an existing entry with the same (type, path)' },
444
+ idempotency_key: { type: 'string' },
445
+ },
446
+ },
447
+ },
448
+ {
449
+ name: 'link_features',
450
+ effect: 'mutating',
451
+ writes: ["feature-json"],
452
+ description: 'Register a typed cross-feature relationship. Two shapes: (1) SAME-PROJECT — kind ∈ surfaced_by|blocks|depends_on|follow_up|supersedes|related, requires to_code; self-links rejected; dedups on (kind,to_code). (2) EXTERNAL (kind:"external") — a cross-project pointer, NOT a same-project link: requires provider; three resolvable sub-shapes — github (repo "owner/name" + integer issue), local (repo token + to_code), url (url); plus reserved url-class providers jira|linear|notion|obsidian (parse-valid, require url, NOT resolved in v1). External dedups on (kind=external, provider, repo, issue|to_code|url). Stores on the source feature; query inverse via get_feature_links(direction:"incoming").',
453
+ inputSchema: {
454
+ type: 'object',
455
+ required: ['from_code', 'kind'],
456
+ properties: {
457
+ from_code: { type: 'string' },
458
+ to_code: { type: 'string', description: 'Same-project: target feature code (required unless kind:"external"). External local: the cited feature code. Need not exist yet.' },
459
+ kind: { type: 'string', enum: ['surfaced_by', 'blocks', 'depends_on', 'follow_up', 'supersedes', 'related', 'external'] },
460
+ provider: { type: 'string', enum: ['github', 'local', 'url', 'jira', 'linear', 'notion', 'obsidian'], description: 'Required when kind:"external". Resolvable: github|local|url. Reserved url-class (require url, not resolved in v1): jira|linear|notion|obsidian.' },
461
+ repo: { type: 'string', description: 'External github: "owner/name". External local: workspace-relative repo token.' },
462
+ issue: { type: 'integer', minimum: 1, description: 'External github: issue/PR number.' },
463
+ url: { type: 'string', description: 'External url-class (url|jira|linear|notion|obsidian): the pointer URL.' },
464
+ expect: { type: 'string', description: 'Optional expected state. github: open|closed. local: a status token. url-class: recorded, never resolved.' },
465
+ note: { type: 'string' },
466
+ force: { type: 'boolean' },
467
+ idempotency_key: { type: 'string' },
468
+ },
469
+ },
470
+ },
471
+ {
472
+ name: 'get_feature_artifacts',
473
+ effect: 'read',
474
+ description: 'Read both canonical (auto-discovered: design.md, plan.md, …) and linked (snapshots, journals, findings) artifacts for a feature in one call. Each linked entry includes a current existence check.',
475
+ inputSchema: {
476
+ type: 'object',
477
+ required: ['feature_code'],
478
+ properties: {
479
+ feature_code: { type: 'string' },
480
+ },
481
+ },
482
+ },
483
+ {
484
+ name: 'get_feature_links',
485
+ effect: 'read',
486
+ description: 'Read outgoing and/or incoming feature links. Default returns both directions; filter by kind if needed.',
487
+ inputSchema: {
488
+ type: 'object',
489
+ required: ['feature_code'],
490
+ properties: {
491
+ feature_code: { type: 'string' },
492
+ direction: { type: 'string', enum: ['outgoing', 'incoming', 'both'] },
493
+ kind: { type: 'string' },
494
+ },
495
+ },
496
+ },
497
+
498
+ // -------------------------------------------------------------------------
499
+ // Changelog writer — COMP-MCP-CHANGELOG-WRITER
500
+ // -------------------------------------------------------------------------
501
+ {
502
+ name: 'add_changelog_entry',
503
+ effect: 'mutating',
504
+ writes: ["changelog"],
505
+ description: 'Insert (or replace, with force: true) a typed entry in compose/CHANGELOG.md. Idempotent on (date_or_version, code) at storage level; optional caller-supplied idempotency_key for retry safety. Audit-log append is best-effort. Use this instead of editing CHANGELOG.md by hand.',
506
+ inputSchema: {
507
+ type: 'object',
508
+ required: ['date_or_version', 'code', 'summary'],
509
+ properties: {
510
+ date_or_version: { type: 'string', description: 'ISO date "YYYY-MM-DD" or semver "vX.Y.Z"' },
511
+ code: { type: 'string', description: 'Feature code (e.g. "COMP-FOO-1"). Uppercase A-Z, digits, dashes; cannot start or end with a dash.' },
512
+ summary: { type: 'string', description: 'One-line summary; renders as the "— summary" tail of the entry header.' },
513
+ body: { type: 'string', description: 'Free paragraphs between header and labeled subsections.' },
514
+ sections: {
515
+ type: 'object',
516
+ description: 'Optional labeled subsections; emitted in fixed order Added → Changed → Fixed → Snapshot.',
517
+ properties: {
518
+ added: { type: 'array', items: { type: 'string' } },
519
+ changed: { type: 'array', items: { type: 'string' } },
520
+ fixed: { type: 'array', items: { type: 'string' } },
521
+ snapshot: { type: 'array', items: { type: 'string' } },
522
+ },
523
+ additionalProperties: false,
524
+ },
525
+ force: { type: 'boolean', description: 'If true and an entry with the same (date_or_version, code) exists, replace it in place.' },
526
+ idempotency_key: { type: 'string', description: 'Optional caller-supplied key. Same key replays return the cached result without re-mutating.' },
527
+ },
528
+ },
529
+ },
530
+ // -------------------------------------------------------------------------
531
+ // Canon override — COMP-CANON-OVERRIDE (COMP-CANON-GUARD Decision 4)
532
+ // -------------------------------------------------------------------------
533
+ {
534
+ name: 'canon_override_grant',
535
+ effect: 'mutating',
536
+ writes: ["override-ledger", "override-attest", "override-grants"],
537
+ description:
538
+ 'Mint a single-use, path-scoped grant permitting ONE direct write to a guarded canon path. '
539
+ + 'The bypass row is appended to .compose/canon-overrides.jsonl BEFORE the grant exists, so a grant '
540
+ + 'cannot be unrecorded. The token expires in 5 minutes and is burned by the first write. '
541
+ + 'Governance state (the bypass ledger, its baseline, the grant directory) is deliberately NOT grantable. '
542
+ + 'SCOPE: this is audit and careless-drift tooling for the Claude Write/Edit path — it is not enforcement. '
543
+ + 'Bash and Codex writes never reach the guard, and `operation` is a declared label recorded for later '
544
+ + 'analysis, never verified against the write that follows.',
545
+ inputSchema: {
546
+ type: 'object',
547
+ required: ['path', 'reason'],
548
+ properties: {
549
+ path: { type: 'string', description: 'Repo-relative path to grant one write for. Must be guarded at the write-time hook and override-eligible.' },
550
+ reason: { type: 'string', description: 'Why the bypass is justified. Empty or whitespace-only is rejected — the recorded reason is the point.' },
551
+ operation: { type: 'string', description: 'Caller-declared intent label (e.g. "repair-malformed-record"). Recorded for analysis; unverifiable by construction.' },
552
+ },
553
+ },
554
+ },
555
+ {
556
+ name: 'get_changelog_entries',
557
+ effect: 'read',
558
+ description: 'Read parsed entries from compose/CHANGELOG.md. Filter by code (exact) or since (shorthand "24h"/"7d"/"30m" or ISO date — date-only; version surfaces always pass through).',
559
+ inputSchema: {
560
+ type: 'object',
561
+ properties: {
562
+ since: { type: 'string', description: 'Window: shorthand like "24h"/"7d"/"30m" or ISO date. Date-only filter; version surfaces are always returned.' },
563
+ code: { type: 'string' },
564
+ limit: { type: 'number', description: 'Default 50; capped at 500.' },
565
+ },
566
+ },
567
+ },
568
+
569
+ // -------------------------------------------------------------------------
570
+ // Journal writer — COMP-MCP-JOURNAL-WRITER
571
+ // -------------------------------------------------------------------------
572
+ {
573
+ name: 'write_journal_entry',
574
+ effect: 'mutating',
575
+ writes: [],
576
+ description: 'Write a typed entry to compose/docs/journal/ with auto-numbered global session and inserted index row. Idempotent on (date, slug) at storage level; optional caller idempotency_key for retry safety. Audit-log append is best-effort.',
577
+ inputSchema: {
578
+ type: 'object',
579
+ required: ['date', 'slug', 'sections', 'summary_for_index'],
580
+ properties: {
581
+ date: { type: 'string', description: 'ISO date "YYYY-MM-DD".' },
582
+ slug: { type: 'string', description: 'Kebab-case slug for the filename, e.g. "mcp-journal-writer".' },
583
+ sections: {
584
+ type: 'object',
585
+ required: ['what_happened', 'what_we_built', 'what_we_learned', 'open_threads'],
586
+ properties: {
587
+ what_happened: { type: 'string' },
588
+ what_we_built: { type: 'string' },
589
+ what_we_learned: { type: 'string' },
590
+ open_threads: { type: 'string' },
591
+ },
592
+ additionalProperties: false,
593
+ },
594
+ summary_for_index: { type: 'string', description: 'Single-line summary for the README index row. No newlines, no "|".' },
595
+ feature_code: { type: 'string', description: 'Optional feature code stamped in entry frontmatter.' },
596
+ closing_line: { type: 'string', description: 'Optional final italicized one-liner.' },
597
+ force: { type: 'boolean', description: 'If true and an entry with the same (date, slug) exists, overwrite in place.' },
598
+ idempotency_key: { type: 'string', description: 'Optional caller-supplied key. Same key replays return the cached result without re-mutating.' },
599
+ },
600
+ },
601
+ },
602
+ {
603
+ name: 'get_journal_entries',
604
+ effect: 'read',
605
+ description: 'Read parsed entries from compose/docs/journal/. Filter by feature_code (exact), session (exact), or since (shorthand "24h"/"7d"/"30m" or ISO date).',
606
+ inputSchema: {
607
+ type: 'object',
608
+ properties: {
609
+ since: { type: 'string' },
610
+ feature_code: { type: 'string' },
611
+ session: { type: 'number' },
612
+ limit: { type: 'number', description: 'Default 50; capped at 500.' },
613
+ },
614
+ },
615
+ },
616
+ // -------------------------------------------------------------------------
617
+ // Completion writer — COMP-MCP-COMPLETION
618
+ // -------------------------------------------------------------------------
619
+ {
620
+ name: 'backfill_completion',
621
+ effect: 'mutating',
622
+ writes: ["feature-json"],
623
+ description: 'Reconstruct a completion through the running Compose server from historical evidence. With no configured guard.testCommand, tests_pass must be explicitly true. Each occurrence must cite either a verified commit SHA or a repo-relative file already inside the repository. This is HTTP delegation like complete_feature, so the Compose server must be running.',
624
+ inputSchema: {
625
+ type: 'object',
626
+ required: ['id', 'commit_sha', 'tests_pass', 'files_changed', 'reason'],
627
+ properties: {
628
+ id: { type: 'string' },
629
+ commit_sha: { type: 'string' },
630
+ tests_pass: { type: 'boolean' },
631
+ files_changed: { type: 'array', items: { type: 'string' } },
632
+ notes: { type: 'string' },
633
+ reason: { type: 'string' },
634
+ occurrences: { type: 'array', items: { type: 'object' } },
635
+ },
636
+ },
637
+ },
638
+ {
639
+ name: 'record_completion',
640
+ effect: 'mutating',
641
+ writes: ["feature-json"],
642
+ description: 'Record a completion bound to a commit SHA — THROUGH the completion gate (COMP-COMPLETION-GATE): under capabilities.guard the commit is server-verified and tests attested (no silent default), ONE guarded transition is ledgered, then the gate writes completions[], status COMPLETE, ROADMAP.md, the cockpit item and the audit event. A refusal (bad evidence, KILLED/SUPERSEDED feature, guard refused/unreachable) errors with COMPLETION_REFUSED and writes NOTHING. Projection failures (ROADMAP/vision) return success with partial:true + failures[]. set_status:false records evidence only (no status change, no guard transition). Idempotent on (feature_code, commit_sha).',
643
+ inputSchema: {
644
+ type: 'object',
645
+ required: ['feature_code', 'commit_sha', 'tests_pass', 'files_changed'],
646
+ properties: {
647
+ feature_code: { type: 'string' },
648
+ commit_sha: { type: 'string', description: 'Full 40-char hex SHA (Decision 9). Short prefixes are rejected on write. Stored verbatim; commit_sha_short is derived for display only.' },
649
+ tests_pass: { type: 'boolean' },
650
+ files_changed: { type: 'array', items: { type: 'string' } },
651
+ notes: { type: 'string' },
652
+ set_status: { type: 'boolean', description: 'Default true. When true, flips status to COMPLETE via set_feature_status.' },
653
+ force: { type: 'boolean', description: 'If true and a record with the same (feature_code, commit_sha) exists, replace it in place.' },
654
+ idempotency_key: { type: 'string' },
655
+ },
656
+ },
657
+ },
658
+ {
659
+ name: 'get_completions',
660
+ effect: 'read',
661
+ description: 'Read completion records from feature.json files. Filter by feature_code (exact), commit_sha (short or full prefix), or since (shorthand or ISO date).',
662
+ inputSchema: {
663
+ type: 'object',
664
+ properties: {
665
+ feature_code: { type: 'string' },
666
+ commit_sha: { type: 'string' },
667
+ since: { type: 'string' },
668
+ limit: { type: 'number', description: 'Default 50; capped at 500.' },
669
+ },
670
+ },
671
+ },
672
+ {
673
+ name: 'write_checkpoint',
674
+ effect: 'mutating',
675
+ writes: [],
676
+ description: 'COMP-RESUME: write a durable build checkpoint anchored to a deterministic environment fingerprint. Omit `soft` for a cheap anchor checkpoint — the response then includes a `scribePrompt` you can answer (returning {goal,nextStep,risks} anchored to the fingerprint) and re-submit as `soft` for a narrative checkpoint. Pass `soft` directly to skip that. Returns {checkpoint, scribePrompt}. Direct-to-disk (works when the server is down).',
677
+ inputSchema: {
678
+ type: 'object',
679
+ required: ['featureCode', 'trigger'],
680
+ properties: {
681
+ featureCode: { type: 'string' },
682
+ trigger: { type: 'string', enum: ['phase-transition', 'pre-risky-action', 'iteration-complete', 'gate-resolution', 'manual', 'resume-sync'] },
683
+ phase: { type: 'string', description: 'Lifecycle phase label; falls back to feature.json then "unknown".' },
684
+ soft: {
685
+ type: 'object',
686
+ description: 'Narrative intent; omit for an anchor checkpoint. Every factual claim should reference a fingerprint anchor.',
687
+ required: ['goal', 'nextStep'],
688
+ properties: {
689
+ goal: { type: 'string' },
690
+ nextStep: { type: 'string' },
691
+ risks: { type: 'array', items: { type: 'string' } },
692
+ },
693
+ },
694
+ flowId: { type: 'string', description: 'Stratum flow id, if any.' },
695
+ confidence: { type: 'number', description: 'Only on resume-sync checkpoints (0..1).' },
696
+ },
697
+ },
698
+ },
699
+ {
700
+ name: 'compose_resume',
701
+ effect: 'read',
702
+ description: 'COMP-RESUME: reconcile a build against ground-truth environment state and return the resume decision. Rebuilds derived state, classifies drift (clean/advanced/diverged), and returns action resume|needs-sync|gate. On needs-sync the caller runs the reconciliation agent with the returned prompt. Requires the Compose server (reconciles live lifecycle state).',
703
+ inputSchema: {
704
+ type: 'object',
705
+ required: ['featureCode'],
706
+ properties: {
707
+ featureCode: { type: 'string' },
708
+ },
709
+ },
710
+ },
711
+
712
+ // -------------------------------------------------------------------------
713
+ // Judgment writer — COMP-JUDGMENT-WRITER (the only legitimate write path to
714
+ // docs/judgment canon; records canonical, markdown generated). Terse
715
+ // schemas by design (token budget); the writer self-validates against
716
+ // contracts/judgment-record.schema.json — inputSchema is advisory.
717
+ // -------------------------------------------------------------------------
718
+ {
719
+ name: 'judgment_position_create',
720
+ effect: 'mutating',
721
+ writes: ["judgment"],
722
+ description: 'Create a judgment position revision (new chain, update, supersession via supersedes, or tombstone via retracted). ASSERT grounding requires an elicitation block. Provenance is writer-stamped.',
723
+ inputSchema: {
724
+ type: 'object',
725
+ required: ['slug', 'claims', 'conviction'],
726
+ properties: {
727
+ slug: { type: 'string' },
728
+ claims: { type: 'array', description: 'Claims: {id, text, grounding EXT|INT|ASSERT|DERIVED|AGENT, supports[], elicitation?}.' },
729
+ conviction: { type: 'object', description: '{level high|medium|low, source stated|inferred}' },
730
+ rejected_alternatives: { type: 'array', description: '{what, why}[]' },
731
+ supersedes: { type: 'string', description: '<slug>#r<N> being superseded.' },
732
+ retracted: { type: 'boolean', description: 'true → tombstone revision.' },
733
+ idempotency_key: { type: 'string' },
734
+ },
735
+ },
736
+ },
737
+ {
738
+ name: 'judgment_position_amend',
739
+ effect: 'mutating',
740
+ writes: ["judgment"],
741
+ description: 'Scoped amendment (P6): new revision changing ONE claim\'s grounding and/or the conviction block. Anything else is supersession via judgment_position_create.',
742
+ inputSchema: {
743
+ type: 'object',
744
+ required: ['slug'],
745
+ properties: {
746
+ slug: { type: 'string' },
747
+ claim_id: { type: 'string' },
748
+ grounding: { type: 'string', description: 'New grounding for claim_id.' },
749
+ elicitation: { type: 'object', description: 'Required if new grounding is ASSERT.' },
750
+ conviction: { type: 'object' },
751
+ idempotency_key: { type: 'string' },
752
+ },
753
+ },
754
+ },
755
+ {
756
+ name: 'judgment_joint_add',
757
+ effect: 'mutating',
758
+ writes: ["judgment"],
759
+ description: 'Add a judgment joint (born open). Both branches and a coarse cost bucket are required; EXT/STRADDLE method packages may be attached now or at dispose time.',
760
+ inputSchema: {
761
+ type: 'object',
762
+ required: ['slug', 'question', 'branch_true', 'branch_false', 'resolve_by', 'cost', 'rank'],
763
+ properties: {
764
+ slug: { type: 'string' },
765
+ question: { type: 'string' },
766
+ branch_true: { type: 'string' },
767
+ branch_false: { type: 'string' },
768
+ resolve_by: { type: 'string', description: 'EXT|INT|CONSTRUCT|ASSERT|STRADDLE' },
769
+ cost: { type: 'string', description: 'hours|days|weeks|months' },
770
+ rank: { type: 'string', description: 'high|medium' },
771
+ ext: { type: 'object', description: 'EXT package: {sharpened_question, bar, falsifier} or {judgment_dispatch: true, reason}.' },
772
+ straddle: { type: 'object', description: '{discriminating_signal, kill_criteria}' },
773
+ flags: { type: 'array' },
774
+ idempotency_key: { type: 'string' },
775
+ },
776
+ },
777
+ },
778
+ {
779
+ name: 'judgment_transition',
780
+ effect: 'mutating',
781
+ writes: ["judgment"],
782
+ description: 'Joint state machine (edge→artifact table enforced; Stratum-guarded where capabilities.guard). Provide the target state plus its required artifact (resolution/dissolution/reopen/redispose), and/or a rank change (atomic rank ledger event).',
783
+ inputSchema: {
784
+ type: 'object',
785
+ required: ['slug'],
786
+ properties: {
787
+ slug: { type: 'string' },
788
+ to: { type: 'string', description: 'open|under_test|resolved|inconclusive|superseded|dissolved' },
789
+ resolution: { type: 'object', description: '{outcome resolved|inconclusive|failed_to_run|superseded, …outcome-specific fields, ext_result?, elicitation?}' },
790
+ dissolution: { type: 'object', description: '{decomposed_into[]}' },
791
+ reopen: { type: 'object', description: '{shaken_evidence_ref} (P6)' },
792
+ redispose: { type: 'object', description: '{new_resolve_by, ext?, straddle?} (retry inconclusive with a different method)' },
793
+ ext: { type: 'object', description: 'EXT package attached at dispose time.' },
794
+ straddle: { type: 'object' },
795
+ rank: { type: 'object', description: '{to: high|medium}' },
796
+ idempotency_key: { type: 'string' },
797
+ },
798
+ },
799
+ },
800
+ {
801
+ name: 'judgment_ledger_append',
802
+ effect: 'mutating',
803
+ writes: ["judgment"],
804
+ description: 'Append a judgment ledger event (kind-specific required fields enforced: commit-decide needs trigger+open_joints+prediction; postmortem needs recall_verdict+attribution and grades prediction_ref; override needs reason). Commit/CONSTRUCT events spawn prediction records.',
805
+ inputSchema: {
806
+ type: 'object',
807
+ required: ['kind', 'title'],
808
+ properties: {
809
+ kind: { type: 'string', description: 'decide|kill|override|escalate|calibrate|postmortem|rank|note|correct|open' },
810
+ title: { type: 'string' },
811
+ body: { type: 'string' },
812
+ refs: { type: 'array' },
813
+ rejected: { type: 'array' },
814
+ conviction: { type: 'object' },
815
+ trigger: { type: 'string', description: 'On decide: earned|forced|exhausted (marks a commit-moment decide).' },
816
+ open_joints: { type: 'array' },
817
+ prediction: { type: 'object', description: '{text, outcome_criteria}' },
818
+ disposition: { type: 'string', description: 'Method disposed with; CONSTRUCT requires prediction.' },
819
+ recall_verdict: { type: 'string' },
820
+ attribution: { type: 'string' },
821
+ prediction_ref: { type: 'string' },
822
+ prediction_grade: { type: 'string', description: 'right|right-wrong-reason|wrong' },
823
+ reason: { type: 'string' },
824
+ rests_on: { type: 'array', description: 'On commit-moment decide: goal clause refs (goal:v<N>#c<N>) the commit rests on; resolved through the effective goal chain.' },
825
+ anchor: { type: 'string', description: 'On note: projection anchor (register-header, joint:<slug>, …).' },
826
+ idempotency_key: { type: 'string' },
827
+ },
828
+ },
829
+ },
830
+ {
831
+ name: 'judgment_person_write',
832
+ effect: 'mutating',
833
+ writes: ["judgment"],
834
+ description: 'Write person canon by op. create adds a person; open_field supports create, fill, and reopen; edge/load_link support create and remove; correct supports divergence pair/clear. Provenance is writer-stamped.',
835
+ inputSchema: {
836
+ type: 'object',
837
+ required: ['op'],
838
+ properties: {
839
+ op: { type: 'string', enum: ['create', 'add_fact', 'correct', 'open_field', 'edge', 'load_link'] },
840
+ slug: { type: 'string' },
841
+ display_name: { type: 'string' },
842
+ section: { type: 'string', description: 'role|life|stated|revealed' },
843
+ text: { type: 'string' },
844
+ channel: { type: 'string', description: 'said|observed|secondhand|inferred' },
845
+ via: { type: 'string', description: 'Source attribution for a secondhand fact; not provenance.' },
846
+ at: { type: 'string', description: 'ISO date (YYYY-MM-DD).' },
847
+ fact_id: { type: 'string' },
848
+ pair_with: { type: 'string', description: 'Pair stated↔revealed divergence on correct.' },
849
+ clear: { type: 'array', description: 'On correct: exactly ["diverges_with"].', items: { type: 'string' } },
850
+ name: { type: 'string', description: 'open_field create branch.' },
851
+ open_field_id: { type: 'string' },
852
+ filled_by: { type: 'string' },
853
+ reopen: { type: 'boolean' },
854
+ reason: { type: 'string' },
855
+ to: { type: 'string', description: 'edge create branch target person slug.' },
856
+ kind: { type: 'string', description: 'edge create branch kind.' },
857
+ edge_id: { type: 'string' },
858
+ remove: { type: 'boolean' },
859
+ fact: { type: 'string', description: 'load_link create branch fact ID.' },
860
+ carries: { type: 'string' },
861
+ load_link_id: { type: 'string' },
862
+ idempotency_key: { type: 'string' },
863
+ },
864
+ },
865
+ },
866
+ {
867
+ name: 'judgment_situation_write',
868
+ effect: 'mutating',
869
+ writes: ["judgment"],
870
+ description: 'Write situation-entity canon by op. create adds an entity; owed supports create, give, and reopen; load_link supports create and remove. Provenance is writer-stamped.',
871
+ inputSchema: {
872
+ type: 'object',
873
+ required: ['op'],
874
+ properties: {
875
+ op: { type: 'string', enum: ['create', 'add_fact', 'correct', 'owed', 'load_link'] },
876
+ slug: { type: 'string' },
877
+ display_name: { type: 'string' },
878
+ text: { type: 'string' },
879
+ channel: { type: 'string', description: 'said|observed|secondhand|inferred' },
880
+ via: { type: 'string', description: 'Source attribution for a secondhand fact; not provenance.' },
881
+ at: { type: 'string', description: 'ISO date (YYYY-MM-DD).' },
882
+ fact_id: { type: 'string' },
883
+ name: { type: 'string', description: 'owed create branch.' },
884
+ why_load_bearing: { type: 'string' },
885
+ owed_id: { type: 'string' },
886
+ filled_by: { type: 'string' },
887
+ reopen: { type: 'boolean' },
888
+ reason: { type: 'string' },
889
+ remove: { type: 'boolean' },
890
+ fact: { type: 'string', description: 'load_link create branch fact ID.' },
891
+ carries: { type: 'string' },
892
+ load_link_id: { type: 'string' },
893
+ idempotency_key: { type: 'string' },
894
+ },
895
+ },
896
+ },
897
+ {
898
+ name: 'judgment_goal_write',
899
+ effect: 'mutating',
900
+ writes: ["judgment"],
901
+ description: 'Write goal canon by op. Ordinary cuts require elicited clauses, provocation, and owner ratification. Every non-migrate op is migration-locked while a live legacy objective requires migration; migrate is the one-shot, fail-closed cutover that lifts the lock and takes no payload. joint_link/load_link support create and remove. Provenance and intents are writer-owned.',
902
+ inputSchema: {
903
+ type: 'object',
904
+ required: ['op'],
905
+ properties: {
906
+ op: { type: 'string', enum: ['cut', 'correct', 'joint_link', 'load_link', 'migrate'] },
907
+ clauses: { type: 'array', description: 'cut: {text, channel, elicitation, secondhand source attribution?}[]' },
908
+ provocation: { type: 'object', description: 'cut: {quote, at}' },
909
+ ratification: { type: 'object', description: 'cut: {asked, answered_at, answer_ref, quote}' },
910
+ diff_note: { type: 'string' },
911
+ clause_id: { type: 'string', description: 'correct branch clause ID.' },
912
+ text: { type: 'string', description: 'correct branch replacement wording.' },
913
+ joint: { type: 'string', description: 'joint_link create branch joint slug.' },
914
+ joint_link_id: { type: 'string' },
915
+ remove: { type: 'boolean' },
916
+ reason: { type: 'string' },
917
+ clause: { type: 'string', description: 'load_link create branch v<N>#c<N> reference.' },
918
+ carries: { type: 'string' },
919
+ load_link_id: { type: 'string' },
920
+ idempotency_key: { type: 'string' },
921
+ },
922
+ },
923
+ },
924
+ {
925
+ name: 'get_judgment_state',
926
+ effect: 'read',
927
+ description: 'Judgment canon snapshot: positions (derived status), joints, under-test, open predictions, recent ledger. Replays pending judgment intents first.',
928
+ inputSchema: { type: 'object', properties: {} },
929
+ },
930
+ {
931
+ name: 'get_judgment_trace',
932
+ effect: 'read',
933
+ description: 'Causal ancestry of one judgment position: every revision in order, what it superseded (walked recursively), and what superseded it. Read-only; answers "what did we believe when we made that call", which get_judgment_state cannot (it returns latest-only).',
934
+ inputSchema: {
935
+ type: 'object',
936
+ properties: { slug: { type: 'string', description: 'Position slug' } },
937
+ required: ['slug'],
938
+ },
939
+ },
940
+ ];