pi-outpost 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/dist/contract/README.md +190 -0
  2. package/dist/contract/conformance/README.md +54 -0
  3. package/dist/contract/conformance/index.json +165 -0
  4. package/dist/contract/conformance/invalid/change-without-reference.json +17 -0
  5. package/dist/contract/conformance/invalid/change-without-target.json +10 -0
  6. package/dist/contract/conformance/invalid/container-as-endpoint.json +26 -0
  7. package/dist/contract/conformance/invalid/container-duplicate-id.json +24 -0
  8. package/dist/contract/conformance/invalid/container-unknown-membership.json +20 -0
  9. package/dist/contract/conformance/invalid/containers-over-the-ceiling.json +220 -0
  10. package/dist/contract/conformance/invalid/duplicate-identifier.json +17 -0
  11. package/dist/contract/conformance/invalid/duplicate-reference-twice.json +24 -0
  12. package/dist/contract/conformance/invalid/duplicate-reference.json +23 -0
  13. package/dist/contract/conformance/invalid/empty-change.json +15 -0
  14. package/dist/contract/conformance/invalid/kind-data-mismatch.json +13 -0
  15. package/dist/contract/conformance/invalid/label-past-ceiling.json +13 -0
  16. package/dist/contract/conformance/invalid/new-element-without-label.json +12 -0
  17. package/dist/contract/conformance/invalid/new-relationship-without-kind.json +22 -0
  18. package/dist/contract/conformance/invalid/relationship-change-without-target.json +13 -0
  19. package/dist/contract/conformance/invalid/relationship-without-endpoints.json +19 -0
  20. package/dist/contract/conformance/invalid/removal-without-target.json +19 -0
  21. package/dist/contract/conformance/invalid/removal-without-type.json +19 -0
  22. package/dist/contract/conformance/invalid/row-column-mismatch.json +15 -0
  23. package/dist/contract/conformance/invalid/table-with-empty-removals.json +6 -0
  24. package/dist/contract/conformance/invalid/table-with-removal.json +20 -0
  25. package/dist/contract/conformance/invalid/table-with-target.json +15 -0
  26. package/dist/contract/conformance/invalid/too-many-element-kinds.json +340 -0
  27. package/dist/contract/conformance/invalid/too-many-relationship-kinds.json +665 -0
  28. package/dist/contract/conformance/invalid/undeclared-property.json +14 -0
  29. package/dist/contract/conformance/invalid/unknown-version.json +13 -0
  30. package/dist/contract/conformance/invalid/unresolved-endpoint.json +19 -0
  31. package/dist/contract/conformance/valid/graph-at-the-kind-ceiling.json +650 -0
  32. package/dist/contract/conformance/valid/graph-containers-at-the-ceiling.json +216 -0
  33. package/dist/contract/conformance/valid/graph-minimal.json +13 -0
  34. package/dist/contract/conformance/valid/graph-proposal-addition-only.json +14 -0
  35. package/dist/contract/conformance/valid/graph-proposal-context-element.json +24 -0
  36. package/dist/contract/conformance/valid/graph-proposal-declared-change.json +18 -0
  37. package/dist/contract/conformance/valid/graph-proposal-field-patch.json +17 -0
  38. package/dist/contract/conformance/valid/graph-proposal-moves-a-member.json +28 -0
  39. package/dist/contract/conformance/valid/graph-proposal-named-context.json +25 -0
  40. package/dist/contract/conformance/valid/graph-proposal-reattachment.json +30 -0
  41. package/dist/contract/conformance/valid/graph-with-containers.json +55 -0
  42. package/dist/contract/conformance/valid/graph-with-empty-container.json +24 -0
  43. package/dist/contract/conformance/valid/graph-with-typed-relationship.json +24 -0
  44. package/dist/contract/conformance/valid/sequence-minimal.json +13 -0
  45. package/dist/contract/conformance/valid/sequence-ordered.json +28 -0
  46. package/dist/contract/conformance/valid/sequence-with-containers.json +45 -0
  47. package/dist/contract/conformance/valid/table-minimal.json +20 -0
  48. package/dist/contract/schemas/structured-exchange-1.json +368 -0
  49. package/dist/contract/validate-structured-exchange.mjs +8670 -0
  50. package/dist/pi-outpost.mjs +13091 -3628
  51. package/dist/pi-outpost.sea.mjs +17422 -7961
  52. package/dist/sea-prep.blob +0 -0
  53. package/dist/skills/README.md +22 -0
  54. package/dist/skills/structured-exchange/SKILL.md +256 -0
  55. package/dist/skills/structured-exchange/structured-exchange-1.json +368 -0
  56. package/dist/web/assets/{PdfViewer-DqktVuQi.js → PdfViewer-CEAp6biY.js} +2 -2
  57. package/dist/web/assets/{abnfDiagram-N423BO3Z-DTFllthO.js → abnfDiagram-N423BO3Z-BE4AMXSH.js} +1 -1
  58. package/dist/web/assets/architecture-TIHT7OUA-DgTbwInB.js +1 -0
  59. package/dist/web/assets/{architectureDiagram-T3A2C74G-z58o8Htx.js → architectureDiagram-T3A2C74G-tjG-eidb.js} +1 -1
  60. package/dist/web/assets/{blockDiagram-VBNYF7ZC-DunlRCsF.js → blockDiagram-VBNYF7ZC-Bwzla1cD.js} +1 -1
  61. package/dist/web/assets/{c4Diagram-5PPSVZJV-BnXZSwmd.js → c4Diagram-5PPSVZJV-BbNH9mqH.js} +1 -1
  62. package/dist/web/assets/channel-CXbqHDJA.js +1 -0
  63. package/dist/web/assets/{chunk-2GRJ4B5K-DMD6wv-7.js → chunk-2GRJ4B5K-BTjPaaWT.js} +1 -1
  64. package/dist/web/assets/{chunk-3NCLNEKW-DcwPPr-p.js → chunk-3NCLNEKW-DfGGO5Az.js} +1 -1
  65. package/dist/web/assets/{chunk-4I5QYGJK-CpI4oXUq.js → chunk-4I5QYGJK-Bajr_p-3.js} +1 -1
  66. package/dist/web/assets/{chunk-5RXB4S5H-vLnKsYcD.js → chunk-5RXB4S5H-CbDXtSwX.js} +1 -1
  67. package/dist/web/assets/{chunk-6Q2QTUOP-C2P_VKtw.js → chunk-6Q2QTUOP-Cj5IB9-q.js} +1 -1
  68. package/dist/web/assets/{chunk-7Z6QIM7H-9Vj99wwd.js → chunk-7Z6QIM7H-BIsTfYEd.js} +1 -1
  69. package/dist/web/assets/{chunk-GF5L2VYU-Cna6JsDy.js → chunk-GF5L2VYU-CYLPBeKA.js} +1 -1
  70. package/dist/web/assets/{chunk-I66GZJ75-Crrt_0-l.js → chunk-I66GZJ75-BjZt7BZl.js} +3 -3
  71. package/dist/web/assets/{chunk-J7OUQ5F2-NnE94fab.js → chunk-J7OUQ5F2-B3gViRUp.js} +2 -2
  72. package/dist/web/assets/{chunk-JQJVKLGR-DV6jrsII.js → chunk-JQJVKLGR-Dg177seh.js} +1 -1
  73. package/dist/web/assets/{chunk-KBJHAD2P-96NXxGM6.js → chunk-KBJHAD2P-B_2jByMg.js} +1 -1
  74. package/dist/web/assets/{chunk-NSK5VX7P-D8DkQ2AO.js → chunk-NSK5VX7P-B5w-3_Ed.js} +1 -1
  75. package/dist/web/assets/{chunk-QR6OTTB3-BCLPnKlE.js → chunk-QR6OTTB3-DCJRxmQT.js} +1 -1
  76. package/dist/web/assets/{chunk-UBXNYLIW-DswC9lJa.js → chunk-UBXNYLIW-DBuIxFzr.js} +1 -1
  77. package/dist/web/assets/{chunk-W5SLKNZC-CPDEB8Hp.js → chunk-W5SLKNZC-B-cGIOQF.js} +1 -1
  78. package/dist/web/assets/{chunk-WRU74C26-DCZH8Rwq.js → chunk-WRU74C26-CLOxZV1w.js} +1 -1
  79. package/dist/web/assets/classDiagram-JCYQIIEL-lv93WkUc.js +1 -0
  80. package/dist/web/assets/classDiagram-v2-OCEON4UE-lv93WkUc.js +1 -0
  81. package/dist/web/assets/{cynefin-VYW2F7L2-cIE_7dpt.js → cynefin-VYW2F7L2-Bk96SN7L.js} +1 -1
  82. package/dist/web/assets/{cynefinDiagram-MW4NZA55-D2MrYar5.js → cynefinDiagram-MW4NZA55-ooeswKGp.js} +1 -1
  83. package/dist/web/assets/{dagre-VZM6K2ZE-BagJQZYc.js → dagre-VZM6K2ZE-CSwH_rTA.js} +1 -1
  84. package/dist/web/assets/{diagram-7IWD3JNH-BwKHjjQ-.js → diagram-7IWD3JNH-DVtrh_uV.js} +1 -1
  85. package/dist/web/assets/{diagram-B4RE2ZJO-BJmMpny4.js → diagram-B4RE2ZJO-CYE5VmiD.js} +1 -1
  86. package/dist/web/assets/{diagram-LBJQPF4R-D3PE9vwZ.js → diagram-LBJQPF4R-BYOlHlmg.js} +1 -1
  87. package/dist/web/assets/{diagram-Q27KOJAE-oiQW2yMD.js → diagram-Q27KOJAE-DJWxpvu9.js} +1 -1
  88. package/dist/web/assets/{diagram-UB23O5K3-lKRQ-OEd.js → diagram-UB23O5K3-B1g-vwj0.js} +1 -1
  89. package/dist/web/assets/{ebnfDiagram-BXEA7PRR-B7T7YLs8.js → ebnfDiagram-BXEA7PRR-BX8Jv2Vd.js} +1 -1
  90. package/dist/web/assets/{erDiagram-JOGREHBK-BB-xsqVL.js → erDiagram-JOGREHBK-9hxklGu2.js} +1 -1
  91. package/dist/web/assets/eventmodeling-45OFAUF4-CSvMsqcO.js +1 -0
  92. package/dist/web/assets/flowDiagram-UKHOOZJN-CPmAOaGQ.js +1 -0
  93. package/dist/web/assets/{ganttDiagram-PKOTCBZU-BnmC1zyp.js → ganttDiagram-PKOTCBZU-D0kAnvoM.js} +1 -1
  94. package/dist/web/assets/{gitGraph-TEB2WS4Q-DFCnvCQm.js → gitGraph-TEB2WS4Q-C9UMPCgj.js} +1 -1
  95. package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-CVU7rVHy.js → gitGraphDiagram-DS77QQ5N-6tPgqwE6.js} +1 -1
  96. package/dist/web/assets/index-CZwzQTfU.css +2 -0
  97. package/dist/web/assets/index-De86t3J_.js +323 -0
  98. package/dist/web/assets/{info-DKCQHKI2-C8DZu4NS.js → info-DKCQHKI2-BBV4qqmE.js} +1 -1
  99. package/dist/web/assets/{infoDiagram-6WML65LV-BSYvkU5b.js → infoDiagram-6WML65LV-Bmhc86CN.js} +1 -1
  100. package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-B6uWyt8M.js → ishikawaDiagram-WSZJBQD7-5wn4Yv2C.js} +1 -1
  101. package/dist/web/assets/{journeyDiagram-NVQOT4AX-CTrfVU7d.js → journeyDiagram-NVQOT4AX-DJLLpQlS.js} +1 -1
  102. package/dist/web/assets/{kanban-definition-27J2QSJJ-DsBfco54.js → kanban-definition-27J2QSJJ-7h9EMbUW.js} +1 -1
  103. package/dist/web/assets/{line-C6m1STwY.js → line-Uu7s3CmK.js} +1 -1
  104. package/dist/web/assets/{mermaid-parser.core-DbS7gJe7.js → mermaid-parser.core-DwpLoctN.js} +3 -3
  105. package/dist/web/assets/{mermaid.core-Blq4BYkb.js → mermaid.core-CRSZr8Y7.js} +3 -3
  106. package/dist/web/assets/{mindmap-definition-FAOFIHXS-BWL0h6Uq.js → mindmap-definition-FAOFIHXS-DPic1KLK.js} +1 -1
  107. package/dist/web/assets/{packet-7NZHBO7P-CryH8SNn.js → packet-7NZHBO7P-CMRjleUu.js} +1 -1
  108. package/dist/web/assets/{pdf-BxEVw97_.js → pdf-DGJrgWix.js} +1 -1
  109. package/dist/web/assets/{pegDiagram-VL7TDLO6-CWrJoBL8.js → pegDiagram-VL7TDLO6-CdECC4cs.js} +1 -1
  110. package/dist/web/assets/{pie-RZYD4A2V-DsuD2v_v.js → pie-RZYD4A2V-C5X4oGn0.js} +1 -1
  111. package/dist/web/assets/{pieDiagram-7S7Q4E2Y-D6-xXQ5p.js → pieDiagram-7S7Q4E2Y-Bvs4Q2O-.js} +1 -1
  112. package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-CMlYXuHz.js → quadrantDiagram-CIZ2JOQS-FsUrYDg7.js} +1 -1
  113. package/dist/web/assets/{radar-I7S5WNFK-CjUoPns1.js → radar-I7S5WNFK-tvx7HrZO.js} +1 -1
  114. package/dist/web/assets/{railroad-3IZDKUUU-Dv7YQd2U.js → railroad-3IZDKUUU-DBks1QD-.js} +1 -1
  115. package/dist/web/assets/railroad-abnf-AHOZXSZD-BnvB29Nv.js +1 -0
  116. package/dist/web/assets/railroad-ebnf-EBAXGLYW-BUSDQ0vn.js +1 -0
  117. package/dist/web/assets/railroad-peg-LSFZ7HO6-CZV0givf.js +1 -0
  118. package/dist/web/assets/{railroadDiagram-AXF67PYL-bzfj9r2a.js → railroadDiagram-AXF67PYL-CBvFYCmN.js} +1 -1
  119. package/dist/web/assets/{requirementDiagram-LRYGKXZP-B7PaiV8r.js → requirementDiagram-LRYGKXZP-C-rumttI.js} +1 -1
  120. package/dist/web/assets/{sankeyDiagram-W5VNT64P-DEKRi7gf.js → sankeyDiagram-W5VNT64P-ClOK2cdj.js} +1 -1
  121. package/dist/web/assets/{sequenceDiagram-SI44F4Z6-BB2FYR31.js → sequenceDiagram-SI44F4Z6-B0pKllVj.js} +1 -1
  122. package/dist/web/assets/{stateDiagram-OKZ733FA-BnQwJgUG.js → stateDiagram-OKZ733FA-BoAWywzk.js} +1 -1
  123. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CHrsco-w.js +1 -0
  124. package/dist/web/assets/{swimlanes-SLNWSIFB-omoGGO13.js → swimlanes-SLNWSIFB-cdgjeXSf.js} +1 -1
  125. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DPjAUQdL.js +8 -0
  126. package/dist/web/assets/{timeline-definition-Z64GVDOM-D0C35XAV.js → timeline-definition-Z64GVDOM-ByMPGIEh.js} +1 -1
  127. package/dist/web/assets/{treeView-QDETBFTQ-D21D9o06.js → treeView-QDETBFTQ-zBqwQF47.js} +1 -1
  128. package/dist/web/assets/{treemap-6X3UGDF4-DsNn3HqU.js → treemap-6X3UGDF4-C2U9S2di.js} +1 -1
  129. package/dist/web/assets/{vennDiagram-T6HMQDX7-Cg1oGxJi.js → vennDiagram-T6HMQDX7-CyJfOcB0.js} +1 -1
  130. package/dist/web/assets/{wardley-OPB4EBWU-Df-Ugyh4.js → wardley-OPB4EBWU-CDxzQuvF.js} +1 -1
  131. package/dist/web/assets/{wardleyDiagram-T6FBY63Y-DKiQg0PU.js → wardleyDiagram-T6FBY63Y-IO8_CDTG.js} +1 -1
  132. package/dist/web/assets/{xychartDiagram-ELKLHX3M-DecoU60Y.js → xychartDiagram-ELKLHX3M-UUfxFEDc.js} +1 -1
  133. package/dist/web/index.html +2 -2
  134. package/package.json +2 -2
  135. package/dist/web/assets/architecture-TIHT7OUA-CfWIx3bb.js +0 -1
  136. package/dist/web/assets/channel-By9fNdgM.js +0 -1
  137. package/dist/web/assets/classDiagram-JCYQIIEL-CbRWDDDS.js +0 -1
  138. package/dist/web/assets/classDiagram-v2-OCEON4UE-CbRWDDDS.js +0 -1
  139. package/dist/web/assets/eventmodeling-45OFAUF4-CIh5g1Zv.js +0 -1
  140. package/dist/web/assets/flowDiagram-UKHOOZJN-DWTzsyQN.js +0 -1
  141. package/dist/web/assets/index-Ct33KYcg.js +0 -321
  142. package/dist/web/assets/index-DFy_CqqQ.css +0 -2
  143. package/dist/web/assets/railroad-abnf-AHOZXSZD-Cr90hvNI.js +0 -1
  144. package/dist/web/assets/railroad-ebnf-EBAXGLYW-BhLKG67T.js +0 -1
  145. package/dist/web/assets/railroad-peg-LSFZ7HO6-Ct2bnVSV.js +0 -1
  146. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CsdKarH7.js +0 -1
  147. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-KAs6WWZH.js +0 -8
@@ -0,0 +1,190 @@
1
+ # Structured exchange — for producers
2
+
3
+ A tool can return structured data alongside its text, and this application will render
4
+ it natively: a graph, a sequence, or a table, drawn from the data rather than from
5
+ anything the tool wrote for display. When the document names a target it is read as a
6
+ *proposal* to change something an external authority holds, and its rendering becomes
7
+ the approval gate before that change is applied.
8
+
9
+ This page is for whoever writes such a producer. The normative contract is
10
+ [`shared/schemas/structured-exchange-1.json`](../shared/schemas/structured-exchange-1.json).
11
+
12
+ ## The two payloads, and why you owe both
13
+
14
+ A tool result carries two things, and they go to different readers:
15
+
16
+ | Channel | Reaches | Carries |
17
+ |---|---|---|
18
+ | the result's text content | **the model** | what the agent will reason about later |
19
+ | `details` | **the interface only** | the structured document |
20
+
21
+ The SDK defines `details` as metadata the LLM does not see. That is what makes it the
22
+ right home for a 500-element graph — the model pays nothing for it — and it is exactly
23
+ why the text half is not optional.
24
+
25
+ **A producer that emits only the structured document leaves the agent with nothing to
26
+ reason about.** It will render beautifully and be useless on the next turn, when the
27
+ agent is asked a follow-up question about a structure it cannot see. Summarise the
28
+ structure in the text: what it contains, what changed, what matters.
29
+
30
+ The reverse also holds: text alone gets you today's behaviour, a wall of prose.
31
+
32
+ ## Describing is not changing
33
+
34
+ On anything carrying a `ref`, the fields you declare beside it **describe what the
35
+ authority already holds**. They are how a reader recognises the thing. They are not
36
+ applied. An intended change goes in `set`:
37
+
38
+ ```json
39
+ { "id": "ledger", "ref": "EL-7", "label": "Ledger",
40
+ "set": { "label": "General Ledger" } }
41
+ ```
42
+
43
+ Two consequences worth stating plainly.
44
+
45
+ **Include as much context as the reader needs.** Elements carried purely so the
46
+ proposal can be situated cost nothing and change nothing. A proposal nobody can place
47
+ is a proposal nobody should approve.
48
+
49
+ **The default runs this way round because producers forget.** Were a declared field
50
+ taken as an intended change, a producer including twenty elements for context would be
51
+ proposing twenty renames to the names those elements already have — and the reader,
52
+ seeing them marked as changes, could approve them. This way a forgotten `set` changes
53
+ nothing and somebody says "it didn't work". A generative producer's mistakes have to
54
+ fail inert.
55
+
56
+ A `set` on something with no `ref` is refused: there is nothing there to change, and
57
+ its fields are already its values.
58
+
59
+ ## Emitting a document
60
+
61
+ Put the envelope in your tool result's `details`:
62
+
63
+ ```js
64
+ return {
65
+ content: [{ type: "text", text: "Billing now calls Ledger. 12 elements, 1 added." }],
66
+ details: {
67
+ schema: "urn:structured-exchange:1",
68
+ kind: "graph",
69
+ data: {
70
+ nodes: [{ id: "billing", label: "Billing" }, { id: "ledger", label: "Ledger" }],
71
+ edges: [{ from: "billing", to: "ledger", kind: "calls" }],
72
+ },
73
+ },
74
+ };
75
+ ```
76
+
77
+ The server forwards anything whose `schema` starts with `urn:structured-exchange:`
78
+ and validates nothing — validation happens where the rendering decision is made.
79
+
80
+ ## The agent as a producer
81
+
82
+ The agent can author these too, guided by [`skills/structured-exchange`](../skills/structured-exchange/SKILL.md).
83
+ It presents one through the `present_structure` tool, which validates before showing
84
+ anything and hands back the diagnostics when it refuses, so a document can be corrected
85
+ without leaving the exchange.
86
+
87
+ ## Validating before you emit
88
+
89
+ ```
90
+ node contract/validate-structured-exchange.mjs document.json
91
+ cat document.json | node contract/validate-structured-exchange.mjs
92
+ ```
93
+
94
+ One file, no install, no checkout: the schema and the rules are inside it. In this
95
+ repository, build it with `npm run build:validator` and find it at
96
+ `shared/dist/validate-structured-exchange.mjs`.
97
+
98
+ | Exit | Meaning |
99
+ |------|---------|
100
+ | `0` | the document conforms |
101
+ | `1` | the document was read and parsed, and does not conform |
102
+ | `2` | the input could not be read at all |
103
+ | `3` | the input was read and is not JSON |
104
+
105
+ The last three are separated on purpose. A missing file, a truncated write and a
106
+ document that says the wrong thing send you looking in three different places, and a
107
+ build that collapses them into "invalid" sends you to the schema for a problem that
108
+ is not there.
109
+
110
+ Diagnostics name the rule and point at the value:
111
+
112
+ ```json
113
+ {"valid": false, "issues": [
114
+ {"rule": "unresolved-endpoint", "path": "/data/edges/0/to",
115
+ "message": "\"ledgr\" is not an identifier declared in /data/nodes"}
116
+ ]}
117
+ ```
118
+
119
+ Every broken rule is reported, not just the first.
120
+
121
+ ## Getting a diagram into a document
122
+
123
+ Use **download SVG**, then insert the file as a picture. Word does not accept an SVG
124
+ pasted from the clipboard — it wants a file. **copy markup** is there for the places
125
+ that do take it directly: an editor, a wiki, a repository.
126
+
127
+ The markup stands on its own. Boxes are `rect` and `text` with colours as attributes
128
+ and an explicit white ground, so what lands in the document is what was on screen. An
129
+ earlier version drew them as HTML inside `foreignObject`, which looks identical in the
130
+ browser and loses everything the moment it is serialized.
131
+
132
+ ## If you are not building in this repository
133
+
134
+ You do not need our command-line interface, and you do not need this repository. The
135
+ contract ships with the package, under `contract/`:
136
+
137
+ ```
138
+ node_modules/pi-outpost/dist/contract/
139
+ schemas/structured-exchange-1.json the normative schema — any validator runs it
140
+ conformance/ documents and the verdict each should get
141
+ validate-structured-exchange.mjs the reference validator, self-contained
142
+ README.md this page
143
+ ```
144
+
145
+ - The **schema** is what the application validates against, byte for byte: it is the
146
+ same file, copied at build time rather than restated.
147
+ - The **conformance suite** covers the relational rules JSON Schema cannot express.
148
+ Run your implementation against it; if it agrees on every case, it conforms.
149
+ - The **validator** is the reference implementation of both, bundled with everything
150
+ it needs. Use it as a check on your own, or as the check itself.
151
+
152
+ In this repository the same two live at `shared/schemas/` and `shared/conformance/`.
153
+
154
+ ## Where the document has to be put
155
+
156
+ On `details` of a tool result. That is the whole channel, and it is deliberate:
157
+ `details` is filled by a tool's implementation and never by the model, so a proposal
158
+ shown as an approval gate was produced by code rather than written by the thing whose
159
+ work is being reviewed.
160
+
161
+ ```js
162
+ return {
163
+ content: [{ type: "text", text: "Billing now calls Ledger. 12 elements, 1 added." }],
164
+ details: envelope,
165
+ };
166
+ ```
167
+
168
+ **There is no MCP path.** The agent SDK underneath has no MCP client — it says so
169
+ outright and points anyone who wants one at writing an extension. So if you are
170
+ bridging a model-context server into this, the bridge is yours to write, and it meets
171
+ this contract by returning the envelope in `details` of its own tool result. Nothing
172
+ here reads MCP's `structuredContent`, because nothing in this process produces it.
173
+
174
+ Relay it unchanged. A bridge that reshapes what it passes through is a second
175
+ producer, and the reader would be approving its work rather than the original.
176
+
177
+ ## What is deliberately not here
178
+
179
+ **Delivery.** How an approved proposal reaches the authority that applies it, and what
180
+ that authority reports back, is a separate contract. What this one guarantees is that
181
+ an approved proposal survives unaltered and can be recovered exactly as it was
182
+ validated — the precondition any delivery mechanism needs.
183
+
184
+ **Concurrency.** A document names *which* artifact it targets, not which revision. A
185
+ proposal built from a stale export and applied late is the receiving authority's to
186
+ detect; this contract does not carry what it would need to do so.
187
+
188
+ **A vocabulary.** Relationship kinds are opaque strings. What `calls`, `composition`,
189
+ or anything else means belongs to your domain, and enumerating it here would make a
190
+ provider-neutral contract into somebody's particular one.
@@ -0,0 +1,54 @@
1
+ # Structured-exchange conformance suite
2
+
3
+ The executable half of the contract. `../schemas/structured-exchange-1.json` says what
4
+ the shape is; these cases say what a correct implementation *does* with documents that
5
+ sit on either side of the line — including the ones JSON Schema alone cannot judge.
6
+
7
+ Any producer, in any language, can run this: the cases are plain JSON and
8
+ `index.json` states the expected verdict for each. Nothing here depends on this
9
+ repository, which is the point — a producer that has to read our TypeScript to know
10
+ whether it conforms does not have a contract, it has a dependency.
11
+
12
+ ## Layout
13
+
14
+ - `valid/` — documents a conforming implementation accepts.
15
+ - `invalid/` — documents it refuses. `index.json` names the rule each one breaks.
16
+ - `index.json` — the manifest, with `expectedRule` for every invalid case.
17
+
18
+ ## Rules that are not in the schema
19
+
20
+ JSON Schema decides shape. These are the relational rules that follow it, and the
21
+ `expectedRule` values that name them:
22
+
23
+ | Rule | What it refuses |
24
+ |---|---|
25
+ | `duplicate-identifier` | two elements sharing an envelope-scoped `id` |
26
+ | `unresolved-endpoint` | a relationship endpoint that no declared element carries |
27
+ | `kind-data-mismatch` | a declared `kind` that disagrees with the data variant present |
28
+ | `kind-not-proposable` | a `table` carrying a `target` or a `removal` |
29
+ | `removal-without-target` | a removal in a document that targets nothing |
30
+ | `duplicate-reference` | the same reference addressed twice — changed twice, or changed and removed |
31
+ | `row-column-mismatch` | a row whose length differs from the declared columns |
32
+
33
+ Rules prefixed `schema/` come from the JSON Schema itself; the suffix is the keyword
34
+ that refused it.
35
+
36
+ ## Running it here
37
+
38
+ ```
39
+ node --import tsx/esm shared/bin/validate-structured-exchange.mjs shared/conformance/valid/graph-minimal.json
40
+ ```
41
+
42
+ Exits 0 for a valid document, 1 for a refused one, 2 when the input could not be read
43
+ at all — which is not the same thing as invalid, and a producer's build should not
44
+ treat it as if it were.
45
+
46
+ ## Two things a conforming implementation must not do
47
+
48
+ **Repair.** No case here is close enough to valid to be worth guessing at, and that is
49
+ deliberate: `unresolved-endpoint` names an endpoint one character from a declared
50
+ identifier. Correcting it would produce a document the producer never wrote.
51
+
52
+ **Report only the first problem.** A document breaking several rules is reported
53
+ against all of them. A producer that has to fix one thing, be refused, fix the next,
54
+ and be refused again is a producer that stops using the format.
@@ -0,0 +1,165 @@
1
+ {
2
+ "valid": [
3
+ {
4
+ "file": "valid/graph-minimal.json"
5
+ },
6
+ {
7
+ "file": "valid/graph-with-typed-relationship.json"
8
+ },
9
+ {
10
+ "file": "valid/graph-proposal-addition-only.json"
11
+ },
12
+ {
13
+ "file": "valid/graph-proposal-field-patch.json"
14
+ },
15
+ {
16
+ "file": "valid/graph-proposal-context-element.json"
17
+ },
18
+ {
19
+ "file": "valid/graph-proposal-reattachment.json"
20
+ },
21
+ {
22
+ "file": "valid/sequence-minimal.json"
23
+ },
24
+ {
25
+ "file": "valid/sequence-ordered.json"
26
+ },
27
+ {
28
+ "file": "valid/table-minimal.json"
29
+ },
30
+ {
31
+ "file": "valid/graph-proposal-named-context.json"
32
+ },
33
+ {
34
+ "file": "valid/graph-proposal-declared-change.json"
35
+ },
36
+ {
37
+ "file": "valid/graph-at-the-kind-ceiling.json"
38
+ },
39
+ {
40
+ "file": "valid/graph-with-containers.json"
41
+ },
42
+ {
43
+ "file": "valid/sequence-with-containers.json"
44
+ },
45
+ {
46
+ "file": "valid/graph-with-empty-container.json"
47
+ },
48
+ {
49
+ "file": "valid/graph-proposal-moves-a-member.json"
50
+ },
51
+ {
52
+ "file": "valid/graph-containers-at-the-ceiling.json"
53
+ }
54
+ ],
55
+ "invalid": [
56
+ {
57
+ "file": "invalid/unknown-version.json",
58
+ "expectedRule": "schema/const"
59
+ },
60
+ {
61
+ "file": "invalid/undeclared-property.json",
62
+ "expectedRule": "schema/additionalProperties"
63
+ },
64
+ {
65
+ "file": "invalid/new-element-without-label.json",
66
+ "expectedRule": "schema/required"
67
+ },
68
+ {
69
+ "file": "invalid/new-relationship-without-kind.json",
70
+ "expectedRule": "schema/required"
71
+ },
72
+ {
73
+ "file": "invalid/relationship-without-endpoints.json",
74
+ "expectedRule": "schema/required"
75
+ },
76
+ {
77
+ "file": "invalid/removal-without-type.json",
78
+ "expectedRule": "schema/required"
79
+ },
80
+ {
81
+ "file": "invalid/duplicate-identifier.json",
82
+ "expectedRule": "duplicate-identifier"
83
+ },
84
+ {
85
+ "file": "invalid/unresolved-endpoint.json",
86
+ "expectedRule": "unresolved-endpoint"
87
+ },
88
+ {
89
+ "file": "invalid/kind-data-mismatch.json",
90
+ "expectedRule": "kind-data-mismatch"
91
+ },
92
+ {
93
+ "file": "invalid/table-with-target.json",
94
+ "expectedRule": "kind-not-proposable"
95
+ },
96
+ {
97
+ "file": "invalid/table-with-removal.json",
98
+ "expectedRule": "kind-not-proposable"
99
+ },
100
+ {
101
+ "file": "invalid/removal-without-target.json",
102
+ "expectedRule": "removal-without-target"
103
+ },
104
+ {
105
+ "file": "invalid/duplicate-reference.json",
106
+ "expectedRule": "duplicate-reference"
107
+ },
108
+ {
109
+ "file": "invalid/row-column-mismatch.json",
110
+ "expectedRule": "row-column-mismatch"
111
+ },
112
+ {
113
+ "file": "invalid/label-past-ceiling.json",
114
+ "expectedRule": "schema/maxLength"
115
+ },
116
+ {
117
+ "file": "invalid/change-without-reference.json",
118
+ "expectedRule": "change-without-reference"
119
+ },
120
+ {
121
+ "file": "invalid/empty-change.json",
122
+ "expectedRule": "schema/minProperties"
123
+ },
124
+ {
125
+ "file": "invalid/duplicate-reference-twice.json",
126
+ "expectedRule": "duplicate-reference"
127
+ },
128
+ {
129
+ "file": "invalid/change-without-target.json",
130
+ "expectedRule": "change-without-target"
131
+ },
132
+ {
133
+ "file": "invalid/relationship-change-without-target.json",
134
+ "expectedRule": "change-without-target"
135
+ },
136
+ {
137
+ "file": "invalid/table-with-empty-removals.json",
138
+ "expectedRule": "kind-not-proposable"
139
+ },
140
+ {
141
+ "file": "invalid/too-many-element-kinds.json",
142
+ "expectedRule": "too-many-kinds"
143
+ },
144
+ {
145
+ "file": "invalid/too-many-relationship-kinds.json",
146
+ "expectedRule": "too-many-kinds"
147
+ },
148
+ {
149
+ "file": "invalid/container-unknown-membership.json",
150
+ "expectedRule": "unresolved-container"
151
+ },
152
+ {
153
+ "file": "invalid/container-duplicate-id.json",
154
+ "expectedRule": "duplicate-container-identifier"
155
+ },
156
+ {
157
+ "file": "invalid/container-as-endpoint.json",
158
+ "expectedRule": "unresolved-endpoint"
159
+ },
160
+ {
161
+ "file": "invalid/containers-over-the-ceiling.json",
162
+ "expectedRule": "schema/maxItems"
163
+ }
164
+ ]
165
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "schema": "urn:structured-exchange:1",
3
+ "kind": "graph",
4
+ "target": "T",
5
+ "data": {
6
+ "nodes": [
7
+ {
8
+ "id": "a",
9
+ "label": "A",
10
+ "set": {
11
+ "label": "B"
12
+ }
13
+ }
14
+ ],
15
+ "edges": []
16
+ }
17
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "schema": "urn:structured-exchange:1",
3
+ "kind": "graph",
4
+ "data": {
5
+ "nodes": [
6
+ { "id": "a", "ref": "EL-1", "label": "Old name", "set": { "label": "New name" } }
7
+ ],
8
+ "edges": []
9
+ }
10
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "schema": "urn:structured-exchange:1",
3
+ "kind": "graph",
4
+ "data": {
5
+ "containers": [
6
+ {
7
+ "id": "electrical",
8
+ "label": "Electrical system"
9
+ }
10
+ ],
11
+ "nodes": [
12
+ {
13
+ "id": "battery",
14
+ "label": "Battery",
15
+ "container": "electrical"
16
+ }
17
+ ],
18
+ "edges": [
19
+ {
20
+ "from": "battery",
21
+ "to": "electrical",
22
+ "kind": "feeds"
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "schema": "urn:structured-exchange:1",
3
+ "kind": "graph",
4
+ "data": {
5
+ "containers": [
6
+ {
7
+ "id": "electrical",
8
+ "label": "Electrical system"
9
+ },
10
+ {
11
+ "id": "electrical",
12
+ "label": "Electrical system again"
13
+ }
14
+ ],
15
+ "nodes": [
16
+ {
17
+ "id": "battery",
18
+ "label": "Battery",
19
+ "container": "electrical"
20
+ }
21
+ ],
22
+ "edges": []
23
+ }
24
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "schema": "urn:structured-exchange:1",
3
+ "kind": "graph",
4
+ "data": {
5
+ "containers": [
6
+ {
7
+ "id": "electrical",
8
+ "label": "Electrical system"
9
+ }
10
+ ],
11
+ "nodes": [
12
+ {
13
+ "id": "battery",
14
+ "label": "Battery",
15
+ "container": "hydraulic"
16
+ }
17
+ ],
18
+ "edges": []
19
+ }
20
+ }