@smartmemory/compose 0.3.6-beta → 0.3.7

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 (134) hide show
  1. package/.claude/skills/compose/SKILL.md +42 -88
  2. package/bin/compose.js +288 -0
  3. package/bin/git-hooks/pre-push.template +29 -0
  4. package/bin/judgment-import.js +7 -0
  5. package/contracts/feature-json.schema.json +5 -0
  6. package/contracts/judgment-record.schema.json +425 -4
  7. package/dist/assets/App-PkZzHeMj.js +894 -0
  8. package/dist/assets/_baseUniq-Bo837sRJ.js +1 -0
  9. package/dist/assets/arc-BafGpyqE.js +1 -0
  10. package/dist/assets/architectureDiagram-Q4EWVU46-BOBfUsqL.js +36 -0
  11. package/dist/assets/blockDiagram-DXYQGD6D-Dwodev1a.js +132 -0
  12. package/dist/assets/{browser-BSM23If2.js → browser-1ntj1-x_.js} +6 -6
  13. package/dist/assets/{c4Diagram-LMCZKHZV-DZf45Fbz.js → c4Diagram-AHTNJAMY-CU_bhYag.js} +1 -1
  14. package/dist/assets/channel-qVK_qn4E.js +1 -0
  15. package/dist/assets/{chunk-JWPE2WC7-_7ujgd_Q.js → chunk-4BX2VUAB-p8WsDwnO.js} +1 -1
  16. package/dist/assets/chunk-4TB4RGXK-B8h7-eR0.js +206 -0
  17. package/dist/assets/{chunk-XXDRQBXY-DfdVhbmA.js → chunk-55IACEB6-DxeEr98s.js} +1 -1
  18. package/dist/assets/{chunk-VR4S4FIN-Dt9NZ67m.js → chunk-EDXVE4YY-BYt8F151.js} +1 -1
  19. package/dist/assets/{chunk-5VM5RSS4-BY4_PV5H.js → chunk-FMBD7UC4-DGSOVeie.js} +1 -1
  20. package/dist/assets/chunk-OYMX7WX6-B-QdgYR2.js +231 -0
  21. package/dist/assets/{chunk-2Q5K7J3B-Dn1spZYu.js → chunk-QZHKN3VN-Du5UAZLs.js} +1 -1
  22. package/dist/assets/{chunk-32BRIVSS-pURGrJDk.js → chunk-YZCP3GAM-C8JbNBSk.js} +1 -1
  23. package/dist/assets/classDiagram-6PBFFD2Q-B8UcfC1q.js +1 -0
  24. package/dist/assets/classDiagram-v2-HSJHXN6E-B8UcfC1q.js +1 -0
  25. package/dist/assets/clone-Pu3RyLUh.js +1 -0
  26. package/dist/assets/{cose-bilkent-JH36ORCC-BieYif4o.js → cose-bilkent-S5V4N54A-O1ESaqge.js} +1 -1
  27. package/dist/assets/dagre-KV5264BT-CPTmFPHw.js +4 -0
  28. package/dist/assets/diagram-5BDNPKRD-B3PNrWs5.js +10 -0
  29. package/dist/assets/diagram-G4DWMVQ6-Cscfr6vc.js +24 -0
  30. package/dist/assets/diagram-MMDJMWI5-CSfqZ-TM.js +43 -0
  31. package/dist/assets/diagram-TYMM5635-Cg4aYS7W.js +24 -0
  32. package/dist/assets/erDiagram-SMLLAGMA-_ZqwG5pl.js +85 -0
  33. package/dist/assets/flowDiagram-DWJPFMVM-C83boxFT.js +162 -0
  34. package/dist/assets/ganttDiagram-T4ZO3ILL-CWnIjuEi.js +292 -0
  35. package/dist/assets/gitGraphDiagram-UUTBAWPF-DrMdxZfH.js +106 -0
  36. package/dist/assets/graph-Bi99_6Yf.js +331 -0
  37. package/dist/assets/graph-RE4I7Ty7.js +1 -0
  38. package/dist/assets/index-Rm2RE-c0.js +123 -0
  39. package/dist/assets/infoDiagram-42DDH7IO-BLmP4Epr.js +2 -0
  40. package/dist/assets/{ishikawaDiagram-FXEZZL3T-CzEB9fQS.js → ishikawaDiagram-UXIWVN3A-yuWWshKN.js} +5 -5
  41. package/dist/assets/{journeyDiagram-5HDEW3XC-Bz8TCdz2.js → journeyDiagram-VCZTEJTY-BOfhaJov.js} +1 -1
  42. package/dist/assets/{kanban-definition-HUTT4EX6-tozrMoV_.js → kanban-definition-6JOO6SKY-Bbolde15.js} +7 -7
  43. package/dist/assets/katex-DkKDou_j.js +257 -0
  44. package/dist/assets/layout-BSf33zm8.js +1 -0
  45. package/dist/assets/{linear-Ck7gpa5N.js → linear-AvSTWMqx.js} +1 -1
  46. package/dist/assets/min-QBM8H4xN.js +1 -0
  47. package/dist/assets/{mindmap-definition-LN4V7U3C-DTcHO0DJ.js → mindmap-definition-QFDTVHPH-BuvgtqIc.js} +7 -7
  48. package/dist/assets/{mobile-CaoXUwAr.js → mobile-BnXEOE3U.js} +2 -2
  49. package/dist/assets/pieDiagram-DEJITSTG-DIzF16vh.js +30 -0
  50. package/dist/assets/quadrantDiagram-34T5L4WZ-D-mbUIjS.js +7 -0
  51. package/dist/assets/{requirementDiagram-TGXJPOKE-bnI2zJeT.js → requirementDiagram-MS252O5E-CEs4kCLd.js} +3 -3
  52. package/dist/assets/sankeyDiagram-XADWPNL6-DFsnCr9n.js +10 -0
  53. package/dist/assets/sequenceDiagram-FGHM5R23-BEJYdTjQ.js +157 -0
  54. package/dist/assets/stateDiagram-FHFEXIEX-BBXs57uY.js +1 -0
  55. package/dist/assets/stateDiagram-v2-QKLJ7IA2-BqKuX4rj.js +1 -0
  56. package/dist/assets/{timeline-definition-FHXFAJF6-D267GQFF.js → timeline-definition-GMOUNBTQ-BGvLoVAY.js} +3 -3
  57. package/dist/assets/vennDiagram-DHZGUBPP-9LaBTMe0.js +34 -0
  58. package/dist/assets/wardley-RL74JXVD-P4MEqMTP.js +162 -0
  59. package/dist/assets/wardleyDiagram-NUSXRM2D-o-tmxnlC.js +20 -0
  60. package/dist/assets/xychartDiagram-5P7HB3ND-Dpn7V6qk.js +7 -0
  61. package/dist/index.html +2 -2
  62. package/lib/bug-escalation.js +30 -4
  63. package/lib/build.js +777 -52
  64. package/lib/canon-guard.js +223 -0
  65. package/lib/canon-registry.js +187 -0
  66. package/lib/codex-preflight.js +26 -4
  67. package/lib/dispatch-ledger.js +301 -0
  68. package/lib/dispatch-metrics.js +236 -0
  69. package/lib/experiment-judge.js +6 -1
  70. package/lib/feature-writer.js +9 -0
  71. package/lib/gsd.js +11 -2
  72. package/lib/hooks-status.js +32 -3
  73. package/lib/judgment/store/index.js +158 -0
  74. package/lib/judgment/store/records.js +183 -24
  75. package/lib/judgment-attest.js +259 -0
  76. package/lib/judgment-gen.js +370 -21
  77. package/lib/judgment-verify.js +153 -0
  78. package/lib/judgment-writer.js +2803 -277
  79. package/lib/lane-gate.js +2 -0
  80. package/lib/local-claude-connector.js +199 -54
  81. package/lib/mcp-enforcement.js +21 -35
  82. package/lib/result-normalizer.js +93 -15
  83. package/lib/review-normalize.js +4 -0
  84. package/lib/stratum-mcp-client.js +131 -6
  85. package/package.json +2 -2
  86. package/server/compose-mcp-tools.js +15 -1
  87. package/server/compose-mcp.js +96 -1
  88. package/server/mcp-tool-policy.js +1 -1
  89. package/dist/assets/App-BG3ngu8H.js +0 -896
  90. package/dist/assets/abnfDiagram-VRR7QNED-CjB_sD3D.js +0 -1
  91. package/dist/assets/arc-_v4hR_uD.js +0 -1
  92. package/dist/assets/architectureDiagram-ZJ3FMSHR-DreJmzXQ.js +0 -36
  93. package/dist/assets/blockDiagram-677ZJIJ3-BG9-c0O1.js +0 -132
  94. package/dist/assets/channel-B3U5wFAT.js +0 -1
  95. package/dist/assets/chunk-EX3LRPZG-DdELs1qP.js +0 -231
  96. package/dist/assets/chunk-MOJQB5TN-D-ky35G-.js +0 -88
  97. package/dist/assets/chunk-RYQCIY6F-Dag_kVlO.js +0 -1
  98. package/dist/assets/chunk-V7JOEXUC-BtewURat.js +0 -206
  99. package/dist/assets/classDiagram-OUVF2IWQ-B6fCN-ht.js +0 -1
  100. package/dist/assets/classDiagram-v2-EOCWNBFH-B6fCN-ht.js +0 -1
  101. package/dist/assets/cynefin-VYW2F7L2-CT2BA6KE.js +0 -178
  102. package/dist/assets/cynefinDiagram-TSTJHNR4-Bh6exbyg.js +0 -62
  103. package/dist/assets/dagre-VKFMJZFB-aXMLSmQL.js +0 -4
  104. package/dist/assets/diagram-FQU43EPY-Dr7JAOuQ.js +0 -3
  105. package/dist/assets/diagram-G47NLZAW-DUvA3FQK.js +0 -24
  106. package/dist/assets/diagram-NH7WQ7WH-BQUARqcu.js +0 -24
  107. package/dist/assets/diagram-OA4YK3LP-dDUc1zHi.js +0 -30
  108. package/dist/assets/diagram-WEI45ONY-B2h5Qlb1.js +0 -41
  109. package/dist/assets/ebnfDiagram-CCIWWBDH-DThRGupB.js +0 -1
  110. package/dist/assets/erDiagram-Q63AITRT-BUCsprO2.js +0 -85
  111. package/dist/assets/flowDiagram-23GEKE2U-DXtNNi6r.js +0 -156
  112. package/dist/assets/ganttDiagram-NO4QXBWP-D4zbBHh_.js +0 -292
  113. package/dist/assets/gitGraphDiagram-IHSO6WYX-DpoQws0W.js +0 -106
  114. package/dist/assets/graph-BXPQrYYB.js +0 -331
  115. package/dist/assets/graph-C9eacEi8.js +0 -1
  116. package/dist/assets/index-3ZH5eMcZ.js +0 -119
  117. package/dist/assets/infoDiagram-FWYZ7A6U-Bbas2GAo.js +0 -2
  118. package/dist/assets/katex-C5jXJg4s.js +0 -257
  119. package/dist/assets/layout-DEXfKzaS.js +0 -1
  120. package/dist/assets/map-Czzmt4hB.js +0 -1
  121. package/dist/assets/pegDiagram-2B236MQR-CHiINrNy.js +0 -1
  122. package/dist/assets/pieDiagram-ENE6RG2P-CfS4YFlR.js +0 -39
  123. package/dist/assets/quadrantDiagram-ABIIQ3AL-CadesS9w.js +0 -7
  124. package/dist/assets/railroadDiagram-RFXS5EU6-CgWEspBN.js +0 -1
  125. package/dist/assets/sankeyDiagram-HTMAVEWB-YWKFgOGw.js +0 -40
  126. package/dist/assets/sequenceDiagram-DBY2YBRQ-BvkNOyF9.js +0 -162
  127. package/dist/assets/sizeCapture-X5ZJPWSS-DlFPA2yO.js +0 -1
  128. package/dist/assets/stateDiagram-2N3HPSRC-h8NIx0kQ.js +0 -1
  129. package/dist/assets/stateDiagram-v2-6OUMAXLB-DjPgZtJ9.js +0 -1
  130. package/dist/assets/swimlanes-5IMT3BWC-CT5n22kG.js +0 -2
  131. package/dist/assets/swimlanesDiagram-G3AALYLV-Dn318Bhq.js +0 -8
  132. package/dist/assets/vennDiagram-L72KCM5P-Dj-wWLYG.js +0 -34
  133. package/dist/assets/wardleyDiagram-EHGQE667-BxCeYxkG.js +0 -78
  134. package/dist/assets/xychartDiagram-FW5EYKEG-DMFqWn7z.js +0 -7
@@ -19,10 +19,49 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
19
19
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
20
20
  import YAML from 'yaml';
21
21
  import { validateBuildStreamEvent } from './build-stream-schema.js';
22
+ import { appendEvent, resolveDispatchLedgerCwd } from './dispatch-ledger.js';
22
23
  import { resolveStratumMcpConnection } from './stratum-engine.js';
23
24
 
24
25
  const RUNTIME_INPUT_REF = /^\$\.input\.([A-Za-z_][A-Za-z0-9_]*)$/;
25
26
 
27
+ function finiteOrNull(value) {
28
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
29
+ }
30
+
31
+ function nonemptyStringOrNull(value) {
32
+ return typeof value === 'string' && value.length > 0 ? value : null;
33
+ }
34
+
35
+ function isBlockedDispatch(value) {
36
+ const candidates = [
37
+ value?.status,
38
+ value?.outcome,
39
+ value?.code,
40
+ value?.error?.status,
41
+ value?.error?.code,
42
+ ];
43
+ return candidates.some((candidate) => {
44
+ const normalized = typeof candidate === 'string' ? candidate.toLowerCase() : '';
45
+ return normalized === 'blocked'
46
+ || normalized === 'budget_exhausted'
47
+ || normalized === 'budget-exhausted';
48
+ });
49
+ }
50
+
51
+ function attachDispatchId(value, dispatchId) {
52
+ if ((typeof value !== 'object' && typeof value !== 'function') || value === null) return;
53
+ try {
54
+ Object.defineProperty(value, 'dispatchId', {
55
+ configurable: true,
56
+ enumerable: false,
57
+ value: dispatchId,
58
+ });
59
+ } catch {
60
+ // A frozen third-party value cannot carry metadata. Dispatch behavior still
61
+ // wins over observability.
62
+ }
63
+ }
64
+
26
65
  /**
27
66
  * Resolve producer-owned authoring values that TS v1 deliberately requires to
28
67
  * be literals. Compose knows these values at plan time, so mutate a cloned spec
@@ -147,6 +186,86 @@ export class StratumMcpClient {
147
186
  // STRAT-PAR-STREAM: subscribers keyed by `${flowId}::${stepId}` → Set<handler>
148
187
  #eventSubs = new Map();
149
188
 
189
+ #recordAgentDispatch(dispatchId, agentType, opts, resultOrError, outcome, elapsedMs) {
190
+ try {
191
+ const context = opts.telemetry && typeof opts.telemetry === 'object'
192
+ ? opts.telemetry
193
+ : {};
194
+ const returnedTelemetry = resultOrError?.telemetry
195
+ && typeof resultOrError.telemetry === 'object'
196
+ ? resultOrError.telemetry
197
+ : {};
198
+ const usage = resultOrError?.usage && typeof resultOrError.usage === 'object'
199
+ ? resultOrError.usage
200
+ : {};
201
+ const effortIntended = Object.hasOwn(context, 'effort_intended')
202
+ ? nonemptyStringOrNull(context.effort_intended)
203
+ : nonemptyStringOrNull(opts.effort);
204
+ const event = {
205
+ kind: 'dispatch',
206
+ dispatch_id: dispatchId,
207
+ site: nonemptyStringOrNull(context.site) ?? 'unattributed',
208
+ agent: String(agentType ?? 'claude').split(':', 1)[0] || 'claude',
209
+ outcome,
210
+ model: nonemptyStringOrNull(returnedTelemetry.model)
211
+ ?? nonemptyStringOrNull(usage.model),
212
+ effort_intended: effortIntended,
213
+ effort_executed: nonemptyStringOrNull(returnedTelemetry.effort),
214
+ tokens_in: null,
215
+ tokens_out: null,
216
+ tokens_total: finiteOrNull(usage.tokens),
217
+ usd: finiteOrNull(usage.usd),
218
+ duration_ms: finiteOrNull(usage.ms)
219
+ ?? finiteOrNull(returnedTelemetry.durationMs)
220
+ ?? finiteOrNull(elapsedMs),
221
+ };
222
+ for (const [field, value] of [
223
+ ['build_id', context.build_id],
224
+ ['feature_code', context.feature_code],
225
+ ['step_id', context.step_id],
226
+ ]) {
227
+ if (nonemptyStringOrNull(value) !== null) event[field] = value;
228
+ }
229
+ if (finiteOrNull(context.attempt) !== null) event.attempt = context.attempt;
230
+ appendEvent(resolveDispatchLedgerCwd(context.project_cwd), event);
231
+ } catch {
232
+ // Dispatch capture is fail-open by contract.
233
+ }
234
+ }
235
+
236
+ async #dispatchAgentRun(agentType, prompt, opts, callOpts) {
237
+ const dispatchId = randomUUID();
238
+ const startedAt = Date.now();
239
+ try {
240
+ const result = await this.#callTool(
241
+ 'stratum_agent_run',
242
+ buildAgentRunRequest(agentType, prompt, opts),
243
+ callOpts,
244
+ );
245
+ this.#recordAgentDispatch(
246
+ dispatchId,
247
+ agentType,
248
+ opts,
249
+ result,
250
+ isBlockedDispatch(result) ? 'blocked' : 'ok',
251
+ Date.now() - startedAt,
252
+ );
253
+ attachDispatchId(result, dispatchId);
254
+ return result;
255
+ } catch (error) {
256
+ this.#recordAgentDispatch(
257
+ dispatchId,
258
+ agentType,
259
+ opts,
260
+ error,
261
+ isBlockedDispatch(error) ? 'blocked' : 'error',
262
+ Date.now() - startedAt,
263
+ );
264
+ attachDispatchId(error, dispatchId);
265
+ throw error;
266
+ }
267
+ }
268
+
150
269
  /**
151
270
  * Subscribe to BuildStreamEvent push notifications scoped to a (flowId, stepId).
152
271
  * Handler receives a parsed BuildStreamEvent envelope. Returns an unsubscribe fn.
@@ -515,9 +634,12 @@ export class StratumMcpClient {
515
634
  // those knobs are resolved compose-side (resolveAgentConfig) and applied at
516
635
  // this seam — the engine's connector honors model + sandboxMode. `cwd` is
517
636
  // required, so default to the process cwd when the caller omits it.
518
- return this.#callTool('stratum_agent_run',
519
- buildAgentRunRequest(agentType, prompt, opts),
520
- { subscribeProgress: true, correlationId });
637
+ return this.#dispatchAgentRun(
638
+ agentType,
639
+ prompt,
640
+ opts,
641
+ { subscribeProgress: true, correlationId },
642
+ );
521
643
  }
522
644
 
523
645
  /**
@@ -531,9 +653,12 @@ export class StratumMcpClient {
531
653
  * @returns {Promise<string>}
532
654
  */
533
655
  async runAgentText(agentType, prompt, opts = {}) {
534
- const result = await this.#callTool('stratum_agent_run',
535
- buildAgentRunRequest(agentType, prompt, opts),
536
- { subscribeProgress: false });
656
+ const result = await this.#dispatchAgentRun(
657
+ agentType,
658
+ prompt,
659
+ opts,
660
+ { subscribeProgress: false },
661
+ );
537
662
  return result?.text ?? '';
538
663
  }
539
664
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartmemory/compose",
3
- "version": "0.3.6-beta",
3
+ "version": "0.3.7",
4
4
  "description": "Structured AI dev pipeline — goal-to-product orchestration with gates, iteration loops, and feature lifecycle management.",
5
5
  "author": "SmartMemory",
6
6
  "license": "MIT",
@@ -85,7 +85,7 @@
85
85
  "@radix-ui/react-toggle": "^1.1.10",
86
86
  "@radix-ui/react-toggle-group": "^1.1.11",
87
87
  "@radix-ui/react-tooltip": "^1.2.8",
88
- "@smartmemory/stratum": "0.3.2",
88
+ "@smartmemory/stratum": "^0.3.3",
89
89
  "@tanstack/react-virtual": "^3.13.23",
90
90
  "ajv": "^8.18.0",
91
91
  "ajv-formats": "^3.0.1",
@@ -835,6 +835,21 @@ export async function toolJudgmentLedgerAppend(args) {
835
835
  return judgmentLedgerAppend(getTargetRoot(), args);
836
836
  }
837
837
 
838
+ export async function toolJudgmentPersonWrite(args) {
839
+ const { judgmentPersonWrite } = await import('../lib/judgment-writer.js');
840
+ return judgmentPersonWrite(getTargetRoot(), args);
841
+ }
842
+
843
+ export async function toolJudgmentSituationWrite(args) {
844
+ const { judgmentSituationWrite } = await import('../lib/judgment-writer.js');
845
+ return judgmentSituationWrite(getTargetRoot(), args);
846
+ }
847
+
848
+ export async function toolJudgmentGoalWrite(args) {
849
+ const { judgmentGoalWrite } = await import('../lib/judgment-writer.js');
850
+ return judgmentGoalWrite(getTargetRoot(), args);
851
+ }
852
+
838
853
  export async function toolGetJudgmentState() {
839
854
  const { getJudgmentState } = await import('../lib/judgment-writer.js');
840
855
  return getJudgmentState(getTargetRoot());
@@ -931,4 +946,3 @@ export function assertToolPhaseAllowed(tool, args = {}, _testCtx) {
931
946
  throw e;
932
947
  }
933
948
  }
934
-
@@ -73,6 +73,9 @@ import {
73
73
  toolJudgmentJointAdd,
74
74
  toolJudgmentTransition,
75
75
  toolJudgmentLedgerAppend,
76
+ toolJudgmentPersonWrite,
77
+ toolJudgmentSituationWrite,
78
+ toolJudgmentGoalWrite,
76
79
  toolGetJudgmentState,
77
80
  _getBinding,
78
81
  assertToolPhaseAllowed,
@@ -790,14 +793,103 @@ const TOOLS = [
790
793
  prediction_ref: { type: 'string' },
791
794
  prediction_grade: { type: 'string', description: 'right|right-wrong-reason|wrong' },
792
795
  reason: { type: 'string' },
796
+ 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.' },
793
797
  anchor: { type: 'string', description: 'On note: projection anchor (register-header, joint:<slug>, …).' },
794
798
  idempotency_key: { type: 'string' },
795
799
  },
796
800
  },
797
801
  },
802
+ {
803
+ name: 'judgment_person_write',
804
+ 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.',
805
+ inputSchema: {
806
+ type: 'object',
807
+ required: ['op'],
808
+ properties: {
809
+ op: { type: 'string', enum: ['create', 'add_fact', 'correct', 'open_field', 'edge', 'load_link'] },
810
+ slug: { type: 'string' },
811
+ display_name: { type: 'string' },
812
+ section: { type: 'string', description: 'role|life|stated|revealed' },
813
+ text: { type: 'string' },
814
+ channel: { type: 'string', description: 'said|observed|secondhand|inferred' },
815
+ via: { type: 'string', description: 'Source attribution for a secondhand fact; not provenance.' },
816
+ at: { type: 'string', description: 'ISO date (YYYY-MM-DD).' },
817
+ fact_id: { type: 'string' },
818
+ pair_with: { type: 'string', description: 'Pair stated↔revealed divergence on correct.' },
819
+ clear: { type: 'array', description: 'On correct: exactly ["diverges_with"].', items: { type: 'string' } },
820
+ name: { type: 'string', description: 'open_field create branch.' },
821
+ open_field_id: { type: 'string' },
822
+ filled_by: { type: 'string' },
823
+ reopen: { type: 'boolean' },
824
+ reason: { type: 'string' },
825
+ to: { type: 'string', description: 'edge create branch target person slug.' },
826
+ kind: { type: 'string', description: 'edge create branch kind.' },
827
+ edge_id: { type: 'string' },
828
+ remove: { type: 'boolean' },
829
+ fact: { type: 'string', description: 'load_link create branch fact ID.' },
830
+ carries: { type: 'string' },
831
+ load_link_id: { type: 'string' },
832
+ idempotency_key: { type: 'string' },
833
+ },
834
+ },
835
+ },
836
+ {
837
+ name: 'judgment_situation_write',
838
+ 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.',
839
+ inputSchema: {
840
+ type: 'object',
841
+ required: ['op'],
842
+ properties: {
843
+ op: { type: 'string', enum: ['create', 'add_fact', 'correct', 'owed', 'load_link'] },
844
+ slug: { type: 'string' },
845
+ display_name: { type: 'string' },
846
+ text: { type: 'string' },
847
+ channel: { type: 'string', description: 'said|observed|secondhand|inferred' },
848
+ via: { type: 'string', description: 'Source attribution for a secondhand fact; not provenance.' },
849
+ at: { type: 'string', description: 'ISO date (YYYY-MM-DD).' },
850
+ fact_id: { type: 'string' },
851
+ name: { type: 'string', description: 'owed create branch.' },
852
+ why_load_bearing: { type: 'string' },
853
+ owed_id: { type: 'string' },
854
+ filled_by: { type: 'string' },
855
+ reopen: { type: 'boolean' },
856
+ reason: { type: 'string' },
857
+ remove: { type: 'boolean' },
858
+ fact: { type: 'string', description: 'load_link create branch fact ID.' },
859
+ carries: { type: 'string' },
860
+ load_link_id: { type: 'string' },
861
+ idempotency_key: { type: 'string' },
862
+ },
863
+ },
864
+ },
865
+ {
866
+ name: 'judgment_goal_write',
867
+ 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.',
868
+ inputSchema: {
869
+ type: 'object',
870
+ required: ['op'],
871
+ properties: {
872
+ op: { type: 'string', enum: ['cut', 'correct', 'joint_link', 'load_link', 'migrate'] },
873
+ clauses: { type: 'array', description: 'cut: {text, channel, elicitation, secondhand source attribution?}[]' },
874
+ provocation: { type: 'object', description: 'cut: {quote, at}' },
875
+ ratification: { type: 'object', description: 'cut: {asked, answered_at, answer_ref, quote}' },
876
+ diff_note: { type: 'string' },
877
+ clause_id: { type: 'string', description: 'correct branch clause ID.' },
878
+ text: { type: 'string', description: 'correct branch replacement wording.' },
879
+ joint: { type: 'string', description: 'joint_link create branch joint slug.' },
880
+ joint_link_id: { type: 'string' },
881
+ remove: { type: 'boolean' },
882
+ reason: { type: 'string' },
883
+ clause: { type: 'string', description: 'load_link create branch v<N>#c<N> reference.' },
884
+ carries: { type: 'string' },
885
+ load_link_id: { type: 'string' },
886
+ idempotency_key: { type: 'string' },
887
+ },
888
+ },
889
+ },
798
890
  {
799
891
  name: 'get_judgment_state',
800
- description: 'Judgment canon snapshot: positions (derived status), joints, under-test, open predictions, recent ledger. Replays pending transition intents first.',
892
+ description: 'Judgment canon snapshot: positions (derived status), joints, under-test, open predictions, recent ledger. Replays pending judgment intents first.',
801
893
  inputSchema: { type: 'object', properties: {} },
802
894
  },
803
895
  ];
@@ -898,6 +990,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
898
990
  case 'judgment_joint_add': result = await toolJudgmentJointAdd(args); break;
899
991
  case 'judgment_transition': result = await toolJudgmentTransition(args); break;
900
992
  case 'judgment_ledger_append': result = await toolJudgmentLedgerAppend(args); break;
993
+ case 'judgment_person_write': result = await toolJudgmentPersonWrite(args); break;
994
+ case 'judgment_situation_write': result = await toolJudgmentSituationWrite(args); break;
995
+ case 'judgment_goal_write': result = await toolJudgmentGoalWrite(args); break;
901
996
  case 'get_judgment_state': result = await toolGetJudgmentState(args); break;
902
997
  // agent_run removed — STRAT-DEDUP-AGENTRUN v1. Use mcp__stratum__stratum_agent_run.
903
998
  default:
@@ -36,7 +36,7 @@ const REVIEWER_ALLOW = [
36
36
  'get_pending_gates', 'get_changelog_entries', 'get_journal_entries', 'get_completions',
37
37
  'validate_feature', 'validate_project', 'roadmap_diff', 'get_roadmap', 'roadmap_graph_check', 'assess_feature_artifacts',
38
38
  'set_workspace', 'get_workspace', 'bind_session',
39
- 'get_judgment_state', // COMP-JUDGMENT-WRITER: read-only; the five judgment write tools stay reviewer-denied
39
+ 'get_judgment_state', // COMP-JUDGMENT-WRITER: read-only; the eight judgment write tools stay reviewer-denied
40
40
  ];
41
41
 
42
42
  export const PROFILE_POLICY = {