agentic-mermaid 0.1.1 → 0.2.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 (340) hide show
  1. package/AGENT_NATIVE.md +155 -91
  2. package/CHANGELOG.md +163 -4
  3. package/Instructions_for_agents.md +10 -10
  4. package/LICENSES/Apache-2.0.txt +202 -0
  5. package/README.md +31 -38
  6. package/THIRD_PARTY_NOTICES.md +29 -0
  7. package/assets/hero.png +0 -0
  8. package/dist/agent-core.d.ts +463 -0
  9. package/dist/agent-core.js +397 -0
  10. package/dist/agent.d.ts +73 -1868
  11. package/dist/agent.js +336 -37325
  12. package/dist/agentic-mermaid-mcp.js +7 -38399
  13. package/dist/am.js +33626 -40446
  14. package/dist/chunk-2P5FKUQC.js +3485 -0
  15. package/dist/chunk-2S3VSS6I.js +149 -0
  16. package/dist/chunk-2SILJDW2.js +37 -0
  17. package/dist/chunk-BJYMBT4I.js +61963 -0
  18. package/dist/chunk-FDM43XU7.js +615 -0
  19. package/dist/chunk-MYPDVZCO.js +4702 -0
  20. package/dist/index-DWPa2MJs.d.ts +6780 -0
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +310 -33014
  23. package/dist/mcp-cli-5J47WEY3.js +17 -0
  24. package/docs/README.md +39 -31
  25. package/docs/agent-api-cookbook.md +44 -35
  26. package/docs/agent-mutation-policy.md +1 -1
  27. package/docs/agent-workflow-examples.md +2 -2
  28. package/docs/api.md +284 -77
  29. package/docs/ascii.md +26 -16
  30. package/docs/code-mode-impact.md +214 -0
  31. package/docs/comparison.md +23 -11
  32. package/docs/computational-aesthetics-prototype-plan.md +252 -0
  33. package/docs/config.md +26 -10
  34. package/docs/custom-fonts.md +265 -0
  35. package/docs/custom-style-cookbook.md +130 -9
  36. package/docs/diagram-families.md +48 -23
  37. package/docs/features.md +59 -52
  38. package/docs/fork-differences.md +6 -3
  39. package/docs/getting-started.md +23 -16
  40. package/docs/issue-derived-test-cases.md +29 -22
  41. package/docs/mcp-code-mode-rationale.md +13 -6
  42. package/docs/mcp-http-transport.md +20 -13
  43. package/docs/mermaid-layout-complaints.md +66 -52
  44. package/docs/mutation-testing.md +145 -66
  45. package/docs/quality.md +23 -14
  46. package/docs/react.md +2 -2
  47. package/docs/schemas/style-spec.schema.json +1221 -30
  48. package/docs/style-authoring.md +241 -38
  49. package/docs/svg-semantic-contract.md +49 -0
  50. package/docs/testing-strategy.md +166 -58
  51. package/docs/theming.md +19 -32
  52. package/examples/styles/catalog.json +54 -0
  53. package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
  54. package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
  55. package/examples/styles/cupertino-prototype.mmd +10 -0
  56. package/examples/styles/cupertino-prototype.style.json +63 -0
  57. package/examples/styles/mid-century-report.style.json +2 -2
  58. package/examples/styles/star-chart-atlas.style.json +2 -1
  59. package/examples/styles/transit-route-map.style.json +2 -2
  60. package/examples/styles/vercel-inspired-prototype.mmd +6 -0
  61. package/examples/styles/vercel-inspired-prototype.style.json +87 -0
  62. package/package.json +85 -45
  63. package/server.json +2 -2
  64. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
  65. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
  66. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
  67. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
  68. package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
  69. package/TODO.md +0 -684
  70. package/dist/index-wjRjbsP-.d.ts +0 -1102
  71. package/docs/assets/improvements/box-start-connector.png +0 -0
  72. package/docs/assets/improvements/comment-dropped-lint.png +0 -0
  73. package/docs/assets/improvements/er-content-loss.png +0 -0
  74. package/docs/assets/improvements/fan-in-trunks.png +0 -0
  75. package/docs/assets/improvements/gantt-family.png +0 -0
  76. package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
  77. package/docs/assets/improvements/gantt-verify-seam.png +0 -0
  78. package/docs/assets/improvements/subgraph-container-edges.png +0 -0
  79. package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
  80. package/docs/assets/improvements/wrapper-init-directive.png +0 -0
  81. package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
  82. package/docs/assets/style-cookbook/mid-century-report.png +0 -0
  83. package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
  84. package/docs/assets/style-cookbook/transit-route-map.png +0 -0
  85. package/docs/contributing/adding-diagram-types.md +0 -144
  86. package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
  87. package/docs/contributing/diagram-family-citizenship.md +0 -82
  88. package/docs/contributing/harvesting-upstream-tests.md +0 -126
  89. package/docs/contributing/lessons-learned.md +0 -276
  90. package/docs/contributing/releasing.md +0 -82
  91. package/docs/contributing/visual-review-evidence.md +0 -53
  92. package/docs/design/families/README.md +0 -26
  93. package/docs/design/families/architecture-beta.md +0 -48
  94. package/docs/design/families/flowchart-parser-conformance.md +0 -22
  95. package/docs/design/families/gantt-research.md +0 -137
  96. package/docs/design/families/gantt.md +0 -329
  97. package/docs/design/families/journey-agentic-cards-before.png +0 -0
  98. package/docs/design/families/journey-experience-curve-implementation.png +0 -0
  99. package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
  100. package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
  101. package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
  102. package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
  103. package/docs/design/families/journey-migration-parity.md +0 -439
  104. package/docs/design/families/journey-section-overlap-after.png +0 -0
  105. package/docs/design/families/journey-section-overlap-before.png +0 -0
  106. package/docs/design/families/journey-usage-research.md +0 -423
  107. package/docs/design/families/journey.md +0 -153
  108. package/docs/design/families/xychart.md +0 -85
  109. package/docs/design/system/README.md +0 -45
  110. package/docs/design/system/abstraction-audit.md +0 -224
  111. package/docs/design/system/abstraction-recommendations.md +0 -655
  112. package/docs/design/system/architecture.mmd +0 -29
  113. package/docs/design/system/architecture.svg +0 -137
  114. package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
  115. package/docs/design/system/layout-rubric.md +0 -138
  116. package/docs/design/system/route-contracts.md +0 -848
  117. package/docs/design/system/source-preservation-ladder.md +0 -52
  118. package/docs/design/system/styles-rollout.md +0 -216
  119. package/docs/design/system/ugly-layouts.md +0 -119
  120. package/docs/layout-characterization/README.md +0 -245
  121. package/docs/layout-characterization/contact-sheet-families.md +0 -498
  122. package/docs/layout-characterization/contact-sheet.md +0 -638
  123. package/docs/layout-characterization/properties.md +0 -281
  124. package/docs/layout-characterization/visual-quality.md +0 -160
  125. package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
  126. package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
  127. package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
  128. package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
  129. package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
  130. package/docs/layout-characterization/visual-snapshots/journey.svg +0 -98
  131. package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
  132. package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
  133. package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
  134. package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
  135. package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
  136. package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
  137. package/docs/project/divergences.md +0 -98
  138. package/docs/project/dogfooding-docs-strategy.md +0 -66
  139. package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
  140. package/docs/project/lessons-learned.md +0 -1019
  141. package/docs/project/remove-role-styling-plan.md +0 -176
  142. package/examples/agent-improve-auth-flow.ts +0 -206
  143. package/examples/agent-loop.ts +0 -53
  144. package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
  145. package/llms.txt +0 -104
  146. package/skill-evals/README.md +0 -77
  147. package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
  148. package/skill-evals/fixtures/class-payment/input.mmd +0 -4
  149. package/skill-evals/fixtures/er-orders/input.mmd +0 -10
  150. package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
  151. package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
  152. package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
  153. package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
  154. package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
  155. package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
  156. package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
  157. package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
  158. package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
  159. package/skill-evals/shared-benchmark.json +0 -510
  160. package/src/agent/analyze.ts +0 -157
  161. package/src/agent/apply.ts +0 -146
  162. package/src/agent/architecture-body.ts +0 -458
  163. package/src/agent/certificates.ts +0 -110
  164. package/src/agent/class-body.ts +0 -319
  165. package/src/agent/core.ts +0 -207
  166. package/src/agent/create.ts +0 -106
  167. package/src/agent/describe.ts +0 -368
  168. package/src/agent/er-body.ts +0 -224
  169. package/src/agent/facts.ts +0 -360
  170. package/src/agent/families-builtin.ts +0 -920
  171. package/src/agent/families.ts +0 -232
  172. package/src/agent/family-layouts.ts +0 -832
  173. package/src/agent/flowchart-body.ts +0 -372
  174. package/src/agent/flowchart-unsupported.ts +0 -243
  175. package/src/agent/font-coverage.ts +0 -288
  176. package/src/agent/gantt-body.ts +0 -495
  177. package/src/agent/index.ts +0 -10
  178. package/src/agent/journey-body.ts +0 -374
  179. package/src/agent/label-metrics.ts +0 -28
  180. package/src/agent/layout-to-rendered.ts +0 -63
  181. package/src/agent/mutate.ts +0 -97
  182. package/src/agent/mutation-ops.ts +0 -51
  183. package/src/agent/op-schema.ts +0 -443
  184. package/src/agent/parse.ts +0 -223
  185. package/src/agent/pie-body.ts +0 -270
  186. package/src/agent/png.ts +0 -171
  187. package/src/agent/quadrant-body.ts +0 -336
  188. package/src/agent/quality.ts +0 -363
  189. package/src/agent/readability-audit.ts +0 -92
  190. package/src/agent/rendered-route-audit.ts +0 -61
  191. package/src/agent/sequence-body.ts +0 -345
  192. package/src/agent/serialize.ts +0 -214
  193. package/src/agent/state-body.ts +0 -589
  194. package/src/agent/structural-count.ts +0 -150
  195. package/src/agent/timeline-body.ts +0 -280
  196. package/src/agent/trace-log.ts +0 -43
  197. package/src/agent/types.ts +0 -995
  198. package/src/agent/verify.ts +0 -603
  199. package/src/agent/xychart-body.ts +0 -472
  200. package/src/architecture/config.ts +0 -282
  201. package/src/architecture/layout.ts +0 -496
  202. package/src/architecture/parser.ts +0 -375
  203. package/src/architecture/renderer.ts +0 -611
  204. package/src/architecture/types.ts +0 -113
  205. package/src/ascii/ansi.ts +0 -423
  206. package/src/ascii/architecture.ts +0 -210
  207. package/src/ascii/canvas.ts +0 -443
  208. package/src/ascii/class-diagram.ts +0 -697
  209. package/src/ascii/converter.ts +0 -475
  210. package/src/ascii/draw.ts +0 -1693
  211. package/src/ascii/edge-bundling.ts +0 -328
  212. package/src/ascii/edge-routing.ts +0 -330
  213. package/src/ascii/er-diagram.ts +0 -439
  214. package/src/ascii/gantt.ts +0 -276
  215. package/src/ascii/grid.ts +0 -747
  216. package/src/ascii/index.ts +0 -196
  217. package/src/ascii/journey.ts +0 -169
  218. package/src/ascii/meta.ts +0 -244
  219. package/src/ascii/multiline-utils.ts +0 -80
  220. package/src/ascii/pathfinder.ts +0 -301
  221. package/src/ascii/pie.ts +0 -67
  222. package/src/ascii/quadrant.ts +0 -158
  223. package/src/ascii/reverse.ts +0 -216
  224. package/src/ascii/sequence.ts +0 -499
  225. package/src/ascii/shapes/circle.ts +0 -27
  226. package/src/ascii/shapes/corners.ts +0 -143
  227. package/src/ascii/shapes/diamond.ts +0 -27
  228. package/src/ascii/shapes/hexagon.ts +0 -27
  229. package/src/ascii/shapes/index.ts +0 -106
  230. package/src/ascii/shapes/rectangle.ts +0 -168
  231. package/src/ascii/shapes/rounded.ts +0 -27
  232. package/src/ascii/shapes/special.ts +0 -332
  233. package/src/ascii/shapes/stadium.ts +0 -110
  234. package/src/ascii/shapes/state.ts +0 -192
  235. package/src/ascii/shapes/types.ts +0 -73
  236. package/src/ascii/timeline.ts +0 -141
  237. package/src/ascii/types.ts +0 -312
  238. package/src/ascii/validate.ts +0 -120
  239. package/src/ascii/width.ts +0 -81
  240. package/src/ascii/wrap.ts +0 -131
  241. package/src/ascii/xychart.ts +0 -919
  242. package/src/browser.ts +0 -29
  243. package/src/class/layout.ts +0 -240
  244. package/src/class/parser.ts +0 -332
  245. package/src/class/renderer.ts +0 -638
  246. package/src/class/types.ts +0 -129
  247. package/src/cli/agent-instructions.ts +0 -1
  248. package/src/cli/am-bin.ts +0 -6
  249. package/src/cli/exit-codes.ts +0 -18
  250. package/src/cli/index.ts +0 -1132
  251. package/src/cli/init-agent.ts +0 -147
  252. package/src/cli/run-entrypoint.ts +0 -10
  253. package/src/color-resolver.ts +0 -152
  254. package/src/elk-instance.ts +0 -114
  255. package/src/er/layout.ts +0 -187
  256. package/src/er/parser.ts +0 -226
  257. package/src/er/renderer.ts +0 -792
  258. package/src/er/types.ts +0 -99
  259. package/src/family-rubric.ts +0 -425
  260. package/src/font-manifest.ts +0 -31
  261. package/src/gantt/layout.ts +0 -365
  262. package/src/gantt/parser.ts +0 -282
  263. package/src/gantt/pipeline.ts +0 -27
  264. package/src/gantt/renderer.ts +0 -414
  265. package/src/gantt/schedule.ts +0 -513
  266. package/src/gantt/types.ts +0 -255
  267. package/src/index.ts +0 -368
  268. package/src/journey/layout.ts +0 -554
  269. package/src/journey/parse-core.ts +0 -251
  270. package/src/journey/parser.ts +0 -101
  271. package/src/journey/renderer.ts +0 -917
  272. package/src/journey/types.ts +0 -144
  273. package/src/layout/geometry.ts +0 -182
  274. package/src/layout/pass.ts +0 -142
  275. package/src/layout/passes/index.ts +0 -3916
  276. package/src/layout-engine.ts +0 -1565
  277. package/src/layout-rubric.ts +0 -659
  278. package/src/mcp/artifacts.ts +0 -137
  279. package/src/mcp/dynamic-harness.ts +0 -31
  280. package/src/mcp/dynamic-modules.d.ts +0 -7
  281. package/src/mcp/facade.ts +0 -608
  282. package/src/mcp/harness-runtime.ts +0 -203
  283. package/src/mcp/hosted-server.ts +0 -468
  284. package/src/mcp/mcp-bin.ts +0 -3
  285. package/src/mcp/mcp-cli.ts +0 -82
  286. package/src/mcp/protocol.ts +0 -13
  287. package/src/mcp/sandbox.ts +0 -195
  288. package/src/mcp/sdk-decl.ts +0 -391
  289. package/src/mcp/server.ts +0 -362
  290. package/src/mcp/tool-surface.ts +0 -146
  291. package/src/mermaid-source.ts +0 -655
  292. package/src/multiline-utils.ts +0 -228
  293. package/src/parser.ts +0 -1031
  294. package/src/pie/layout.ts +0 -168
  295. package/src/pie/parser.ts +0 -136
  296. package/src/pie/renderer.ts +0 -234
  297. package/src/pie/types.ts +0 -82
  298. package/src/quadrant/layout.ts +0 -191
  299. package/src/quadrant/parser.ts +0 -183
  300. package/src/quadrant/renderer.ts +0 -322
  301. package/src/quadrant/types.ts +0 -113
  302. package/src/render-family-hooks.ts +0 -244
  303. package/src/renderer.ts +0 -1099
  304. package/src/route-contracts.ts +0 -2723
  305. package/src/scene/backend.ts +0 -76
  306. package/src/scene/fidelity.ts +0 -183
  307. package/src/scene/hybrid-backend.ts +0 -174
  308. package/src/scene/ir.ts +0 -181
  309. package/src/scene/marks.ts +0 -127
  310. package/src/scene/rough-backend.ts +0 -378
  311. package/src/scene/seed.ts +0 -46
  312. package/src/scene/style-registry.ts +0 -556
  313. package/src/sequence/layout.ts +0 -456
  314. package/src/sequence/parser.ts +0 -255
  315. package/src/sequence/renderer.ts +0 -604
  316. package/src/sequence/types.ts +0 -154
  317. package/src/shape-clipping.ts +0 -351
  318. package/src/shared/accessibility-directives.ts +0 -22
  319. package/src/shared/batched.ts +0 -61
  320. package/src/shared/color-math.ts +0 -93
  321. package/src/shared/svg-a11y.ts +0 -29
  322. package/src/shared/syntax-error.ts +0 -17
  323. package/src/shared/unicode-ranges.ts +0 -135
  324. package/src/styles.ts +0 -318
  325. package/src/svg-paths.ts +0 -28
  326. package/src/text-metrics.ts +0 -248
  327. package/src/theme.ts +0 -710
  328. package/src/timeline/layout.ts +0 -381
  329. package/src/timeline/parser.ts +0 -215
  330. package/src/timeline/renderer.ts +0 -538
  331. package/src/timeline/types.ts +0 -105
  332. package/src/types.ts +0 -462
  333. package/src/website-worker-modules.d.ts +0 -18
  334. package/src/xychart/axis-utils.ts +0 -109
  335. package/src/xychart/colors.ts +0 -124
  336. package/src/xychart/config.ts +0 -88
  337. package/src/xychart/layout.ts +0 -656
  338. package/src/xychart/parser.ts +0 -504
  339. package/src/xychart/renderer.ts +0 -640
  340. package/src/xychart/types.ts +0 -282
@@ -1,98 +0,0 @@
1
- # Divergences
2
-
3
- Current implementation decisions that differ from, or materially narrow, the broader `AGENT_NATIVE.md` architecture. This file is **not** a backlog; active work lives only in `TODO.md`. Historical loop logs were removed because they had become process noise. Use Git history for archaeology.
4
-
5
- ## What still matters
6
-
7
- ### Determinism is structural, not seeded
8
-
9
- - Removed the old `LayoutContext` / `SeededRNG` / `Clock` / `withSeededRandom` / font-metrics apparatus.
10
- - `RenderedLayout` has no `seed` field.
11
- - `VerifyOptions` is intentionally small: `{ suppress?, labelCharCap? }`.
12
- - Claim scope: same-input structural layout determinism is tested for the supported runtime/version matrix. Cross-machine or cross-architecture float equality is **not** claimed.
13
-
14
- ### Package and compatibility choices
15
-
16
- - Product/docs, npm package name, and repository path are **Agentic Mermaid** / `agentic-mermaid` / `adewale/agentic-mermaid`. The agent surface ships as the `./agent` subpath.
17
- - `MermaidGraph` and `renderMermaidSVGAsync` remain for compatibility with existing renderer/tests/consumers.
18
- - ~~`state` diagrams currently share the flowchart body (`body.kind: 'flowchart'`)…~~ **Superseded by BUILD-19.** State diagrams now own a dedicated `StateBody` IR (`body.kind: 'state'`) with state-shaped ops and a real `asState` narrower. `asFlowchart` returns `null` on a state diagram (a breaking change within the unreleased agent surface). Verify still gets full Tier 1 + Tier 2 geometric coverage: the `StateBody` projects to a `MermaidGraph` via the legacy parser (`stateBodyToGraph`) and runs the identical flowchart `verifyGraph`. The modeled subset is simple states/transitions/`[*]` pseudostates/composites/`direction`; unmodeled syntax (`<<fork>>`/`<<choice>>`/`<<join>>`, history, concurrency `--`, notes, `classDef`/`class`/`:::`) falls back to a lossless opaque body. Corpus round-trip for state jumped 5% → 100%.
19
-
20
- ### Mutation surface is intentionally narrower than Mermaid syntax
21
-
22
- Structured mutation is exposed for every built-in renderable family, but only when that body's parser/IR/serializer/verifier can preserve the modeled semantics:
23
-
24
- - flowchart/state;
25
- - sequence (BUILD-18 — segment-preserving: participant/message ops stay live while Note/alt/loop/par/activate/autonumber/title ride along verbatim as opaque-block segments; only un-segmentable input such as an unbalanced `end` falls back to whole-body opaque);
26
- - timeline;
27
- - class;
28
- - ER;
29
- - journey (BUILD-15 pilot);
30
- - architecture (BUILD-17 — the modeled subset of groups/services/junctions/edges);
31
- - xychart (BUILD-16 — the modeled subset of title/axes/series);
32
- - pie;
33
- - quadrant;
34
- - gantt.
35
-
36
- Opaque/source-level bodies:
37
-
38
- - any known-family diagram that falls back to opaque because it contains unmodeled syntax (e.g. architecture `{group}` boundary modifiers, accTitle/accDescr, malformed pie entries, out-of-range quadrant coordinates, or un-segmentable sequence/gantt syntax).
39
-
40
- For source-level bodies, agents may render, verify, describe, and round-trip preserved source. They do **not** get typed mutation ops. `am mutate` returns `UNSUPPORTED_FAMILY` for those bodies.
41
-
42
- ### Structured-or-opaque is load-bearing
43
-
44
- Known-family input must never be partially parsed and then re-emitted with unknown constructs dropped. If the structured parser cannot preserve a construct, the body stays opaque/source-preserved and serializes from `body.source`.
45
-
46
- BUILD-18 refines "opaque/source-preserved" for sequence into a finer grain: a sequence body now interleaves structured statements with **opaque-block segments** holding unmodeled lines verbatim, so the structured ops survive instead of being forfeited at the first unmodeled line. The never-lossy invariant is unchanged — the segment lines are byte-for-byte preserved — and whole-body opaque is still the fallback for un-segmentable input. Class/ER/timeline can adopt the same segment-preserving body as follow-up work.
47
-
48
- This applies even when the diagram renders successfully. Render support is not the same as structured editing support.
49
-
50
- ### Code Mode is local, synchronous, and lineage-checked
51
-
52
- - The MCP server exposes a Code Mode-style `execute(code)` tool backed by local `node:vm`; it is not Cloudflare Codemode and not a container/OS security boundary.
53
- - Code Mode is synchronous: no `async`/`await`, Promise jobs, dynamic import, finalizers, or blocking/realm-creating globals.
54
- - SDK-returned diagrams are read-only. Structured edits must go through `mermaid.mutate(...)` and must use trusted diagram lineage from parse/mutate/narrower outputs.
55
-
56
- ### Runtime surfaces are part of the contract
57
-
58
- Keep these in sync when changing agent behavior:
59
-
60
- - `src/agent/*` public API;
61
- - `am capabilities` and CLI help;
62
- - MCP SDK declaration and server instructions;
63
- - `Instructions_for_agents.md` / `am --agent-instructions`;
64
- - `skills/agentic-mermaid-diagram-workflow/*`;
65
- - `skills/agentic-mermaid-live-editor/SKILL.md`;
66
- - `llms.txt`;
67
- - `docs/agent-api-cookbook.md`.
68
-
69
- Doc-sync tests exist because agents consume docs and capability JSON as runtime surface, not just prose.
70
-
71
- ## Important fixed issues worth remembering
72
-
73
- These are retained only because they explain guardrails that may otherwise look excessive:
74
-
75
- - **Opaque fidelity:** opaque bodies serialize from preserved `body.source`, not normalized `canonicalSource`, so indentation/comments/source text survive parse → serialize.
76
- - **`am parse | am serialize`:** JSON payloads preserve flowchart styling maps and defensively normalize loose SDK-shaped subgraphs.
77
- - **Subgraph payload hardening:** `synthesizeFromGraph` tolerates missing `children`, null entries, cyclic references, non-tuple map arrays, and non-integer linkStyle keys.
78
- - **Timeline mutation:** timeline edits update `canonicalSource`, validate mutation text, allocate unique IDs, and fall back to opaque for header-suffix/lossy syntax.
79
- - **Shared routing cleanup:** SVG, ASCII, and agent parsing use shared diagram-family detection from `src/mermaid-source.ts`; agent parsing adds only a loose known-family path so malformed known headers can round-trip as opaque.
80
- - **Source-based `LABEL_OVERFLOW`:** verification checks labels for opaque/source-level families via family label extractors, so fallback does not mean "unverified."
81
-
82
- ## Evidence and limits
83
-
84
- What the current suite demonstrates:
85
-
86
- - typed mutation works for the exposed structured families;
87
- - unsupported/opaque/source-level bodies refuse structured mutation instead of losing source;
88
- - verify-before-serialize and Code Mode trace linting catch key agent anti-patterns;
89
- - corpus gates cover Mermaid docs examples and MermaidSeqBench round-trip stability;
90
- - docs/capabilities/CLI/MCP declarations are checked for consistency.
91
-
92
- What it does **not** yet prove:
93
-
94
- - market demand;
95
- - that a real external consumer likes the API;
96
- - that a selected release model follows the intended loop from docs alone.
97
-
98
- Those validation items remain in `TODO.md`.
@@ -1,66 +0,0 @@
1
- # Dogfooding our docs: render and pin our own documentation with our own tools
2
-
3
- Status: strategy. Drafted 2026-06-20.
4
-
5
- Agentic Mermaid is a deterministic diagram engine with a typed, verifiable agent surface. Our
6
- documentation should meet the same bar as our output: **generated by the system it documents, and
7
- pinned by a test so it cannot silently drift.** An architecture diagram that rots, or a "supported
8
- families" list that lags the registry, is worse than no doc — it actively misleads.
9
-
10
- ## Principle
11
-
12
- If a piece of documentation can be *derived* from the code, derive it — then add a test that
13
- regenerates it from the source of truth and fails when the committed doc disagrees. This is the
14
- documentation analogue of the determinism and round-trip guarantees the engine already enforces
15
- ([`AGENT_NATIVE.md`](../../AGENT_NATIVE.md) §1, §3): the same inputs must produce the same artifact,
16
- and the artifact is checked, not trusted.
17
-
18
- Two regeneration shapes, both gated by `UPDATE_GOLDEN=1 bun test <file>`:
19
-
20
- | Shape | Source of truth | Artifact | Test asserts |
21
- |---|---|---|---|
22
- | **Rendered figure** | a committed `.mmd` | a committed `.svg`, rendered by `renderMermaidSVG` | deterministic re-render + byte-match (normalized) |
23
- | **Projected table/list** | a code registry (e.g. `BUILTIN_FAMILY_METADATA`) | a marked region in a `.md` | rebuild from the registry + region match |
24
-
25
- ## Instances in the repo
26
-
27
- 1. **The architecture figure** — `docs/design/system/architecture.{mmd,svg}`, pinned by
28
- `src/__tests__/docs-architecture-diagram.test.ts`. The three-stacks diagram is authored as
29
- Mermaid and rendered by our own engine; the test asserts the render is deterministic across
30
- calls and matches the committed SVG. Docs *about* the system, produced *by* the system.
31
- 2. **The family roster table** — the built-in-families table in
32
- `docs/design/system/abstraction-audit.md` §2, pinned by `src/__tests__/audit-family-table.test.ts`.
33
- It is generated from `BUILTIN_FAMILY_METADATA`; add or rename a family and the test fails until
34
- the audit is regenerated.
35
-
36
- These join the repo's existing doc-sync guarantees, which already follow the same philosophy:
37
-
38
- - `Instructions_for_agents.md` is byte-identical to `am --agent-instructions` (doc-sync test).
39
- - `package.json` keywords, `llms.txt`, editor examples, and CLI capabilities are all projected from
40
- `BUILTIN_FAMILY_METADATA` and checked (`doc-sync.test.ts`, `diagram-family-citizenship.test.ts`).
41
- - Every `LayoutWarning` code and `MutationOp` kind must appear in `AGENT_NATIVE.md` (doc-sync).
42
-
43
- ## How to add a dogfooded doc
44
-
45
- **A rendered figure:**
46
- 1. Author the source as `docs/.../<name>.mmd`.
47
- 2. Add a snapshot test modeled on `docs-architecture-diagram.test.ts` that renders the `.mmd` with
48
- `renderMermaidSVG` and compares to `<name>.svg`, with an `UPDATE_GOLDEN` write path.
49
- 3. Run `UPDATE_GOLDEN=1 bun test <test>` to materialize the artifact; commit both.
50
-
51
- **A projected table/list:**
52
- 1. Wrap the region in the doc with `<!-- NAME:start -->` / `<!-- NAME:end -->` markers.
53
- 2. Add a test modeled on `audit-family-table.test.ts` that rebuilds the region from the code
54
- registry and compares, with an `UPDATE_GOLDEN` write path.
55
- 3. Regenerate and commit.
56
-
57
- ## Boundaries
58
-
59
- - **Pin what is *derivable*.** Prose judgement — the audit's analysis, the recommendations — is not
60
- mechanically generated; it is reviewed like code. Only the derivable parts (the figure, the
61
- roster) are auto-pinned.
62
- - **Rendered-SVG snapshots carry the "within one ELK version" determinism assumption** of the
63
- existing `*-svg-snapshot` goldens (`AGENT_NATIVE.md` §1). They guard drift within the CI
64
- environment, not cross-version byte-equality.
65
- - **Keep generated regions small and clearly marked** so a reader — and `git diff` — can see what
66
- is machine-owned versus hand-written.
@@ -1,338 +0,0 @@
1
- # Hosted MCP on Cloudflare: plan
2
-
3
- > **Status: shipped (PR #94).** This began as a design plan and is now the
4
- > as-built record of the hosted MCP endpoint live at
5
- > `https://agentic-mermaid.dev/mcp`. The "Where we start" section below is
6
- > preserved as the pre-build starting point; the "Post-review hardening"
7
- > sections (rounds 1–5) track the audit findings applied after the initial
8
- > build.
9
-
10
- Plan for turning the website's `/mcp` 501 placeholder into a real hosted MCP
11
- endpoint at `https://agentic-mermaid.dev/mcp`. The account is on the **Workers
12
- Paid plan with Dynamic Workers** (<https://developers.cloudflare.com/dynamic-workers/>),
13
- which changes the earlier free-tier plan in one fundamental way: hosted Code
14
- Mode (`execute`) is now viable — agent-supplied JavaScript runs in an isolated
15
- dynamic Worker instead of being local-only.
16
-
17
- ## Where we start
18
-
19
- - The website is a Cloudflare Worker with Static Assets
20
- (`website/wrangler.jsonc`, `website/src/worker.js` at the time — now
21
- `worker.ts`); `/mcp` returned an honest 501 before this work.
22
- - The MCP server (`src/mcp/server.ts`) has a transport-agnostic JSON-RPC core,
23
- `handleRequest(req, context)`, plus stdio and node HTTP/SSE transports.
24
- - Tools today: `execute` (Code Mode in a hardened `node:vm` sandbox),
25
- `render_png` (native `@resvg/resvg-js`), `describe`.
26
- - **Viability probe (done):** the pure SDK surface — parse, mutate, verify,
27
- analyze, serialize, describe, `renderMermaidSVG`, `renderMermaidASCII`, the
28
- Code Mode facade — bundles for the workerd target at 2.2 MB raw / 657 KB
29
- gzip with zero Node dependencies, and renders correctly from that bundle.
30
- The only native/VM dependencies in the whole graph are `png.ts` (napi resvg)
31
- and `sandbox.ts` (`node:vm`), both replaced per the table below. elkjs is
32
- the bundled pure-JS build driven synchronously (`src/elk-instance.ts`) and
33
- needs nothing from Node.
34
-
35
- ## Hosted tool surface
36
-
37
- | Tool | Local implementation | Hosted implementation |
38
- |---|---|---|
39
- | `execute` | `node:vm` hardened sandbox | **Dynamic Worker** per code hash: harness module (SDK bundle + the same hardened facade) + agent code as a module, `globalOutbound: null`, `limits: { cpuMs, subRequests: 0 }` |
40
- | `render_png` | native `@resvg/resvg-js` | `@resvg/resvg-wasm` + bundled DejaVu Sans and built-in style faces (convenience surface; wasm output is not covered by the byte-determinism contract) |
41
- | `describe` | pure | same code, unchanged |
42
- | `render_svg` | — (use execute) | pure, hosted-only |
43
- | `render_ascii` | — (use execute) | pure, hosted-only |
44
- | `verify` | — (use execute) | pure, hosted-only |
45
- | `mutate` | — (use execute or CLI `am mutate`) | hosted-only declarative structured edit of an existing `source`; applies an op list, verifies, emits `{ ok, family, source, verify }` only on success |
46
- | `build` | — (use execute or CLI/library `buildMermaid`) | hosted-only declarative authoring from a `family` + op list; same verified envelope as `mutate` |
47
-
48
- The three hosted-only pure render/verify tools exist for cost, not philosophy:
49
- locally, `execute` is free, so Code Mode stays the single entry point
50
- (`docs/mcp-code-mode-rationale.md` still holds). Hosted, every `execute`
51
- spins/bills a dynamic Worker, so the common render/verify paths get direct
52
- tools that cost one ordinary Worker invocation and are edge-cacheable. The
53
- `mutate`/`build` tools are the deliberate structured-edit exception: they give
54
- weak clients a verified op-list path without asking them to write JavaScript,
55
- while still funneling through the same typed mutation core. The implementations
56
- live in the shared hosted core (`src/mcp/hosted-server.ts`), not the website, so
57
- there is exactly one implementation of each.
58
-
59
- ## Hosted `execute` via Dynamic Workers
60
-
61
- Design (API per <https://developers.cloudflare.com/dynamic-workers/api-reference/>):
62
-
63
- - The parent Worker holds a `worker_loaders` binding (`LOADER`). On
64
- `execute`, it calls `env.LOADER.get(id, async () => workerCode)` where:
65
- - `id` = `am-exec-<pkg.version>-<sha256(code)>` — identical code reuses a
66
- warm isolate; the callback only runs on cold load.
67
- - `workerCode.modules` = the prebuilt **harness** (SDK bundle + Code Mode
68
- facade + result plumbing, built by `website/build.ts` and imported into
69
- the parent as a text module) plus the agent code wrapped as ONE module
70
- per isolate. workerd compiles every registry module eagerly at isolate
71
- startup (discovered in the wrangler e2e — a lazy `import()` fallback
72
- inside one isolate is impossible), so the expression-first choice
73
- `sandbox.ts` makes is decided by the loader itself: the parent starts the
74
- expression-form isolate first and falls back to a statement-form isolate
75
- when startup fails with a SyntaxError. Statement-form code costs one
76
- failed isolate attempt; identical repeats never reach the loader thanks
77
- to the response cache.
78
- - `globalOutbound: null` — the isolate cannot `fetch()` or `connect()`
79
- at all; `env` is empty, so there is nothing to reach or leak.
80
- - `getEntrypoint(null, { limits: { cpuMs: min(timeoutMs, 30_000),
81
- subRequests: 0 } })` enforces the compute budget; hitting it throws,
82
- which maps to the same `{ ok: false, error }` shape as a local timeout.
83
- - The harness passes the user function the **same hardened `mermaid` facade**
84
- the vm sandbox uses (`createTracingMermaid`, extracted to a runtime-neutral
85
- `src/mcp/facade.ts`), so read-only results, trusted-diagram checks, and
86
- error messages match local behavior. Sync-only enforcement
87
- (`unsupportedCodeReason`) runs parent-side before any isolate is created —
88
- rejected code costs nothing.
89
- - Result contract is byte-identical in shape to local: `{ ok, value, logs,
90
- error }`, with the same undefined→null promotion and Map/Set JSON
91
- normalization.
92
-
93
- Known, documented divergences from local `execute`:
94
-
95
- 1. `timeoutMs` is enforced as **CPU** milliseconds (`cpuMs`), not wall-clock;
96
- for pure synchronous compute these are close but not identical.
97
- 2. A warm isolate can serve repeated identical code, so module-level global
98
- mutation (`globalThis.x = ...`) may be visible across calls with the same
99
- code — locally every call gets a fresh vm context. Response caching (below)
100
- makes identical requests return the first result anyway; the divergence is
101
- only reachable when the edge cache misses but the isolate is warm.
102
- 3. Isolation authority differs: locally the proxy facade is the security
103
- boundary; hosted, the isolate + `globalOutbound: null` is, and the facade
104
- is kept for behavioral parity.
105
- 4. Hosted output is bounded at the source: console logs cap at 1,000
106
- entries / 256 KB (a truncation marker is appended) and serialized results
107
- at 2 MB, with the parent's capped stream-read of the isolate response as
108
- the backstop. The vm sandbox has no output caps.
109
-
110
- Post-review hardening (external audit, round 1): Worker Loader isolate IDs
111
- carry a `deployTag` — package version **plus a hash of the harness bundle** —
112
- because the Worker Loader contract is that one ID always maps to the same
113
- WorkerCode; without the hash, a harness/SDK change that ships without a
114
- version bump could keep serving stale warm isolates. Request bodies and
115
- isolate responses are stream-read with hard byte caps (no buffering past the
116
- limit), `render_png` clamps `scale` to 0.1–8 and enforces a ~16.7 MP
117
- output-pixel budget, and the e2e probe pins workerd's codegen bans (`eval`,
118
- `Function` constructor) plus the log cap against a live isolate.
119
-
120
- Post-review hardening (external audit, round 2):
121
-
122
- - **Full-deploy cache version.** The harness hash covers Code Mode isolate
123
- IDs, but the /mcp *response* cache also stores `render_svg` / `verify` /
124
- `describe` / `render_png` results, whose implementations live in
125
- `hosted-server.ts`, `mcp-handler.ts`, `png-wasm.ts`, and the wasm/fonts —
126
- none of which are in the harness bundle. `cacheVersion` is now a
127
- `DEPLOY_VERSION` computed at build time (`website/src/deploy-hash.ts`,
128
- emitted to `generated/deploy-version.ts`): a length-prefixed SHA-256 over
129
- the **bundled worker JS closure + harness + wasm + fonts + the main
130
- worker's `compatibility_date`**. Any change to any hosted tool, the
131
- transport, the PNG path, an asset, or the worker's runtime semantics moves
132
- it and invalidates cached results, version bump or not. Isolate IDs keep the
133
- narrower harness hash (they *are* just harness + user module).
134
- - **Cache keys from normalized effective arguments.** The key was a hash of
135
- raw `req.params`; handlers drop unknown args and clamp `scale`/`timeoutMs`,
136
- so a caller could add `{ nonce }` or vary an out-of-range `scale` to force
137
- recompute and defeat the cost control. `cacheKeyFor` (in `hosted-server.ts`,
138
- the single source of truth the handlers also use for clamping) now derives
139
- the key from only the output-affecting arguments, normalized: unknown keys
140
- dropped, `scale` clamped and defaulted to its resolved value (omitted and
141
- explicit `scale: 2` share one entry), `timeoutMs` excluded from `execute`
142
- (it is a budget, not an input). Calls it deems uncacheable (unknown tool,
143
- missing required arg, non-base64 `render_png` output) bypass the cache and
144
- run directly — they error, and errors were never cached.
145
-
146
- **Scope of the cost-control guarantee (external audit, round 3 — adversarial
147
- multi-agent verification found no correctness/poisoning bug; these are the
148
- low-severity refinements it surfaced):**
149
-
150
- - The normalization covers **arguments**, not the `source`/`code` payload,
151
- which is keyed **verbatim by design**. Keying on the raw payload is what
152
- makes a cached response provably correspond to what that exact input
153
- renders; canonicalizing the payload (e.g. stripping Mermaid comments) is
154
- deliberately avoided because two payloads that canonicalize alike are not
155
- guaranteed to render byte-identically — a wrong cached result is far worse
156
- than a missed dedup. A caller can therefore still force recompute by varying
157
- insignificant payload bytes (comments/trailing whitespace). That residual is
158
- bounded by the **WAF rate limit** (the actual abuse backstop; see
159
- `website/README.md`), not by the cache.
160
- - `execute` is cached on `code` alone. Code Mode is intended for deterministic
161
- SDK workflows; a non-deterministic body (`Date`/`Math.random`) has its first
162
- result frozen for the cache TTL. This is pre-existing — `execute` results
163
- were always cached — and inherent to caching arbitrary code; it is not a
164
- cross-caller integrity issue (identical `code` → identical key by definition).
165
- - The response cache is a **cost optimization for legitimate repeat traffic**,
166
- not the abuse control. The WAF rate-limiting rule on `POST /mcp` is the
167
- primary defense against a determined attacker and remains a launch
168
- requirement.
169
-
170
- **Post-review hardening (external audit, round 4 — full-PR multi-agent audit,
171
- 5 lenses):** one P1 (wrapper-breakout containment) and a set of low-severity
172
- test-coverage and transport refinements. The verified fixes:
173
-
174
- - **Wrapper-breakout containment, stated honestly.** Agent code is concatenated
175
- into an ES module, so code that closes the harness function early with `}`
176
- reaches module scope. Two layers close what the isolate boundary already
177
- bounds: (1) the harness wraps the user function in **parentheses** (an
178
- expression position), which makes an injected top-level `import`/`export`/`;`
179
- a **SyntaxError** — so a `import ... from 'cloudflare:sockets'` breakout fails
180
- the isolate start rather than running; and (2) `hardenIsolateGlobals()` runs
181
- at isolate startup, **before** `user.js` is dynamically imported, and strips
182
- capability globals (`fetch`, `caches`, `crypto`, `connect`, …) plus
183
- `Error.prepareStackTrace`. A comma+IIFE tail can still *run* at eval time, but
184
- only against stripped capabilities. These are **defense in depth** — the
185
- guaranteed boundary remains the isolate config (`globalOutbound: null`, empty
186
- env, no bindings, `cpuMs`). `NEUTRALIZED_ISOLATE_GLOBALS` is restricted to
187
- globals provably unused by the harness and the pure SDK at run time (verified
188
- by the wrangler e2e, which still renders after hardening).
189
- - **Test-coverage gaps closed.** The Promise.race wall-clock backstop, the
190
- empty/non-JSON isolate-body malformed guard, `readCapped`'s byte-exact cap
191
- (boundary + cross-chunk + UTF-8 straddle), multi-chunk base64 encoding, and
192
- CORS headers on HTTP-level error responses now have discriminating tests
193
- (each fails when its mechanism is removed).
194
- - **Transport nits.** The Promise.race timer is cleared in a `finally` so a
195
- winning fetch leaves no dangling timer, and an empty/non-JSON isolate body
196
- degrades to `sandbox returned a malformed result` instead of leaking a raw
197
- `JSON.parse` error.
198
-
199
- Residual honesty: `website/e2e-mcp.sh` is a **manual** probe run against a live
200
- `wrangler dev` (it needs the Worker Loader), **not** a CI gate; the CI gate is
201
- `bun test src/__tests__/`. The isolate-level containment (globals stripped on
202
- the real `globalThis`, breakout rejected) is what the e2e pins; the unit suite
203
- pins the pure, runtime-neutral pieces (`neutralizeGlobalsOn` on a throwaway
204
- target, `userModuleSources` rejection, the log cap).
205
-
206
- **Post-review hardening (external audit, round 5 — transport abuse/conformance):**
207
-
208
- - **Batch fan-out cap.** A JSON-RPC array ran every item under `Promise.all`,
209
- so one 128 KB body could pack many `tools/call` items that each spin a
210
- billable isolate/render — a single request amplifying past the per-IP WAF
211
- limit. `MAX_BATCH_ITEMS` (20) now bounds a batch, refused with 400 before any
212
- item runs.
213
- - **`MCP-Protocol-Version` validation + 2025-06-18 batch rule.** The transport
214
- now rejects an explicit unsupported version header with 400, and — because
215
- MCP 2025-06-18 *removed* JSON-RPC batching — refuses an array body when the
216
- header pins that revision. Older negotiated versions (2024-11-05 / 2025-03-26,
217
- or no header) may still batch, so existing clients are unaffected.
218
- - **CORS Origin validation.** Wildcard `Access-Control-Allow-Origin: *` on a
219
- public, credential-less endpoint is not itself an access-control hole — CORS
220
- gates only browser reads and never the agent/server clients that ignore it —
221
- but `*` does let an arbitrary site silently drive a *visitor's* browser
222
- against the endpoint (distributed across visitor IPs, diluting the per-IP WAF
223
- limit). CORS is now **reflective with Origin validation**: a no-Origin client
224
- (agent/server) still gets `*`; a browser Origin is echoed only when it is
225
- same-origin / localhost / allowlisted (`agentic-mermaid.dev`); a disallowed
226
- browser Origin gets no `Access-Control-Allow-Origin` and a 403. This follows
227
- the MCP Streamable HTTP Origin-validation guidance without breaking any
228
- non-browser client (the website itself does not call `/mcp` from the browser).
229
-
230
- ## Transport: stateless Streamable HTTP
231
-
232
- Unchanged from the original plan and still the cheapest correct choice — the
233
- tools are pure functions of their inputs:
234
-
235
- - Single `POST /mcp` accepting JSON-RPC, responding `application/json`.
236
- No sessions, no SSE stream, `GET /mcp` → 405, notifications → 202.
237
- - No Durable Objects, agents SDK, KV, R2, or Queues.
238
- - Protocol version negotiated from a known-good list (the core currently pins
239
- `2024-11-05`; Streamable HTTP clients offer `2025-03-26`+).
240
- - **Response caching:** layout is deterministic, so `tools/call` responses
241
- (except nothing — all eight tools are deterministic) are cached in the Workers
242
- Cache API keyed on SHA-256 of `(tool, canonicalized arguments)`. Repeat
243
- requests skip compute entirely; for `execute` they also skip the dynamic
244
- Worker, which is the biggest cost lever.
245
- - Hygiene: 128 KB body cap, 64 KB Mermaid-source/code caps with structured
246
- errors pointing at the local CLI, `content-type` enforcement, Origin-validated
247
- CORS (no-Origin agent/server clients keep `*`; allowed browser origins are
248
- echoed; disallowed browser origins get 403), one WAF rate-limiting rule
249
- (e.g. 60 req/min/IP on `POST /mcp`, configured in the dashboard and
250
- recorded in `website/README.md`).
251
-
252
- ## Cost model (Workers Paid)
253
-
254
- | Meter | Included | Overage | Our exposure |
255
- |---|---|---|---|
256
- | Worker requests | 10 M/month | $0.30/M | every `/mcp` call + one internal request per uncached `execute` |
257
- | CPU time | 30 M ms/month | $0.02/M ms | renders are ms-scale; `execute` capped at 30 s by `cpuMs` |
258
- | Unique dynamic Workers | 1,000/month | $0.002/Worker/day | one per **unique** `execute` code string per day (hash-keyed + edge-cached; retries and repeats dedupe) |
259
-
260
- Levers that keep this near the plan's $5/month floor: edge caching of
261
- deterministic responses, code-hash isolate reuse, parent-side rejection of
262
- unsupported code, `subRequests: 0`, size caps, and the WAF rate limit as the
263
- abuse backstop. The pure tools keep the high-volume paths (render/verify) off
264
- the dynamic-Worker meter entirely. No other billable primitives are used.
265
-
266
- ## Verification strategy
267
-
268
- Two external toolkits gate this work, per the repo owner's direction:
269
-
270
- - **testing-best-practices** (<https://github.com/adewale/testing-best-practices>):
271
- - Red-green for new behavior: hosted-core tests written to fail without the
272
- implementation; red/green evidence stated in the PR.
273
- - **Differential testing** (`references/differential-testing.md`): the local
274
- `node:vm` sandbox is the reference implementation; a corpus of Code Mode
275
- snippets (happy paths, SDK misuse, mutation attempts, sync violations,
276
- serialization edges) runs through both `executeInSandbox` and the harness
277
- execution semantics, asserting matching `{ ok, value, logs, error }`.
278
- - Error-path tests, not just invalid input: loader failures, cpu-limit
279
- exceptions, oversized bodies, wrong content types, malformed JSON-RPC.
280
- - Real objects over mocks: transport tests drive the actual `fetch` handler
281
- with real `Request`/`Response`; the only faked seam is the Worker Loader
282
- binding (a purpose-built fake implementing `get/getEntrypoint`, since
283
- workerd is not available inside `bun test`), and a `wrangler dev` e2e
284
- covers the real binding.
285
- - **cfdoctor** (<https://github.com/adewale/cfdoctor>): static scan
286
- (`cfdoctor_static_scan.py`) plus the skill's audit playbook run against
287
- `website/` before shipping; findings triaged with evidence and fixed or
288
- explicitly accepted in the audit output.
289
-
290
- Plus the repo's own gates: `bun test src/__tests__/` (existing MCP tests must
291
- stay green through the refactor), `bunx tsc --noEmit`, `bun run
292
- website:check`, and a `wrangler dev` end-to-end pass (`website/e2e-mcp.sh`:
293
- initialize → tools/list → each tool, including a real dynamic-Worker
294
- `execute`). Known local-dev limitation found in that pass: wrangler dev does
295
- not enforce dynamic-worker `cpuMs`, so an unbounded sync loop wedges local
296
- workerd; production enforces `cpuMs` at the runtime and the parent races a
297
- wall-clock backstop so `/mcp` always answers. The e2e also flushed out an
298
- ELK instantiation failure under workerd (fixed in `src/elk-instance.ts` by
299
- flipping elk-worker's environment sniff with a temporary `document` global
300
- instead of deleting the undeletable `self`).
301
-
302
- ## Implementation steps
303
-
304
- 1. **Extract runtime-neutral modules.** `src/mcp/protocol.ts` (JSON-RPC
305
- types + reply/error helpers), `src/mcp/facade.ts` (`createTracingMermaid`,
306
- `unsupportedCodeReason`, wrapping helpers) importing the pure agent
307
- surface, not the barrel (the barrel exports `png.ts`, which drags native
308
- resvg into every graph). `server.ts`/`sandbox.ts` keep their exports and
309
- behavior; existing tests untouched.
310
- 2. **Hosted core** `src/mcp/hosted-server.ts`: `handleHostedRequest(req,
311
- context)` with the eight-tool surface; `execute` and `render_png` accept
312
- injected implementations so the core stays runtime-neutral and testable in
313
- `bun test`.
314
- 3. **Harness** `src/mcp/dynamic-harness.ts` (entry compiled to a single text
315
- asset by `website/build.ts`): fetch handler that builds the facade, imports
316
- the user module (expression-first with `SyntaxError` fallback), executes,
317
- serializes to the `ExecuteResult` contract.
318
- 4. **Website worker** `website/src/worker.ts`: existing static/redirect logic
319
- unchanged; `/mcp` branch implements the streamable-HTTP handler, cache
320
- layer, loader-backed `execute`, wasm `render_png`. `wrangler.jsonc` gains
321
- `worker_loaders`, text/data module rules for the harness and font, and
322
- keeps Static Assets.
323
- 5. **Tests** as described under verification; `src/__tests__/website-build.test.ts`
324
- now asserts the live hosted endpoint (the 501 assertion it carried is gone).
325
- 6. **cfdoctor audit + wrangler dev e2e**, fix findings.
326
- 7. **Docs**: `website/README.md`, `docs/mcp-http-transport.md` cross-link, a
327
- hosted-MCP section documenting the endpoint, tool surface, limits,
328
- divergences, and the dashboard-side rate-limit rule.
329
-
330
- ## Explicit non-goals
331
-
332
- - No REST render API — MCP only.
333
- - No accounts or tokens; abuse control is caps + rate limiting.
334
- - No hosted artifact storage; `render_png` returns base64 only (`output:
335
- "file"/"url"` stay local-server features).
336
- - Durable Object facets, dynamic Workflows, and egress-controlled outbound
337
- fetch (Dynamic Workers features) are all unused — the isolates need no
338
- state and no network.