agentic-mermaid 0.4.1 → 0.5.3

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 (253) hide show
  1. package/AGENT_NATIVE.md +94 -627
  2. package/CHANGELOG.md +651 -0
  3. package/Instructions_for_agents.md +44 -23
  4. package/LICENSES/Khroma-MIT.txt +21 -0
  5. package/LICENSES/Mermaid-MIT.txt +21 -0
  6. package/LICENSES/es-toolkit-MIT.txt +21 -0
  7. package/LICENSES/pele-MIT.txt +40 -0
  8. package/LICENSES/zenuml-core-MIT.txt +21 -0
  9. package/README.md +93 -50
  10. package/SECURITY.md +28 -12
  11. package/THIRD_PARTY_NOTICES.md +60 -0
  12. package/assets/hero.png +0 -0
  13. package/dist/agent-core.d.ts +478 -210
  14. package/dist/agent-core.js +1 -396
  15. package/dist/agent.d.ts +17 -8
  16. package/dist/agent.js +1 -408
  17. package/dist/agentic-mermaid-mcp.js +1 -11
  18. package/dist/am.js +1 -34562
  19. package/dist/browser-lazy/chunks/agentic-producer-core-CC6OOLDW.js +1 -0
  20. package/dist/browser-lazy/chunks/architecture-QUFVKYJR.js +13 -0
  21. package/dist/browser-lazy/chunks/block-6J3Q63BO.js +11 -0
  22. package/dist/browser-lazy/chunks/builtin-backends-7SOZZHPF.js +12 -0
  23. package/dist/browser-lazy/chunks/c4-SQVBZAKJ.js +10 -0
  24. package/dist/browser-lazy/chunks/chunk-2GPK4VFF.js +5 -0
  25. package/dist/browser-lazy/chunks/chunk-2I4UPL7Q.js +3 -0
  26. package/dist/browser-lazy/chunks/chunk-2SH76OTH.js +1 -0
  27. package/dist/browser-lazy/chunks/chunk-2UIA2IME.js +3 -0
  28. package/dist/browser-lazy/chunks/chunk-4LHYF3BS.js +1 -0
  29. package/dist/browser-lazy/chunks/chunk-5B2Z7575.js +1 -0
  30. package/dist/browser-lazy/chunks/chunk-5FQ534HP.js +1 -0
  31. package/dist/browser-lazy/chunks/chunk-5UVGUSD6.js +1 -0
  32. package/dist/browser-lazy/chunks/chunk-5X6MJX3H.js +1 -0
  33. package/dist/browser-lazy/chunks/chunk-AMJLVPUB.js +4 -0
  34. package/dist/browser-lazy/chunks/chunk-BU5LHXNJ.js +3 -0
  35. package/dist/browser-lazy/chunks/chunk-BY2CPG6B.js +1 -0
  36. package/dist/browser-lazy/chunks/chunk-CN7WTBWB.js +1 -0
  37. package/dist/browser-lazy/chunks/chunk-CQE4HQEW.js +1 -0
  38. package/dist/browser-lazy/chunks/chunk-CSFUKSU6.js +1 -0
  39. package/dist/browser-lazy/chunks/chunk-DF6765YX.js +17 -0
  40. package/dist/browser-lazy/chunks/chunk-E2WMJZNM.js +1 -0
  41. package/dist/browser-lazy/chunks/chunk-FAIMVOX5.js +6 -0
  42. package/dist/browser-lazy/chunks/chunk-GVOEAY52.js +1 -0
  43. package/dist/browser-lazy/chunks/chunk-HLWUNUCM.js +1 -0
  44. package/dist/browser-lazy/chunks/chunk-ICC7HBJX.js +1 -0
  45. package/dist/browser-lazy/chunks/chunk-J35HAQQF.js +20 -0
  46. package/dist/browser-lazy/chunks/chunk-J7FFAFQF.js +1 -0
  47. package/dist/browser-lazy/chunks/chunk-LH7HUDKW.js +1 -0
  48. package/dist/browser-lazy/chunks/chunk-N5ELX7LR.js +2 -0
  49. package/dist/browser-lazy/chunks/chunk-NMQQMRIR.js +1 -0
  50. package/dist/browser-lazy/chunks/chunk-NO7ZIVRU.js +1 -0
  51. package/dist/browser-lazy/chunks/chunk-OZ443IVJ.js +16 -0
  52. package/dist/browser-lazy/chunks/chunk-PQGPS2YV.js +7 -0
  53. package/dist/browser-lazy/chunks/chunk-QA6GJQ4X.js +1 -0
  54. package/dist/browser-lazy/chunks/chunk-RIVTHZAQ.js +1 -0
  55. package/dist/browser-lazy/chunks/chunk-SZELX2EJ.js +1 -0
  56. package/dist/browser-lazy/chunks/chunk-TYZOZXUJ.js +22 -0
  57. package/dist/browser-lazy/chunks/chunk-UL6NDWLL.js +1 -0
  58. package/dist/browser-lazy/chunks/chunk-VQYYGAXP.js +1 -0
  59. package/dist/browser-lazy/chunks/chunk-VWVZGQE2.js +1 -0
  60. package/dist/browser-lazy/chunks/chunk-X2UYKWKM.js +4 -0
  61. package/dist/browser-lazy/chunks/chunk-YCEICNM6.js +1 -0
  62. package/dist/browser-lazy/chunks/chunk-Z3FDIVLN.js +20 -0
  63. package/dist/browser-lazy/chunks/class-5QA4SCTS.js +13 -0
  64. package/dist/browser-lazy/chunks/decode-63UIZGD2.js +1 -0
  65. package/dist/browser-lazy/chunks/elk.bundled-L4CADV5W.js +24 -0
  66. package/dist/browser-lazy/chunks/er-7ZSKGN7Y.js +11 -0
  67. package/dist/browser-lazy/chunks/eventmodeling-2BPLHWRN.js +19 -0
  68. package/dist/browser-lazy/chunks/flowchart-NEIMZTB5.js +4 -0
  69. package/dist/browser-lazy/chunks/forest-implementation-AEP24AIP.js +1 -0
  70. package/dist/browser-lazy/chunks/gantt-RDPIFYAF.js +11 -0
  71. package/dist/browser-lazy/chunks/gitgraph-N4Y5RYB7.js +9 -0
  72. package/dist/browser-lazy/chunks/ishikawa-LD3YBDJN.js +6 -0
  73. package/dist/browser-lazy/chunks/journey-6YQR5DJX.js +16 -0
  74. package/dist/browser-lazy/chunks/kanban-PLMJ7NBW.js +20 -0
  75. package/dist/browser-lazy/chunks/mermaid-yaml-runtime-QVYX4EWK.js +136 -0
  76. package/dist/browser-lazy/chunks/mindmap-K2UBFO3Y.js +12 -0
  77. package/dist/browser-lazy/chunks/packet-CUW4QY6Y.js +7 -0
  78. package/dist/browser-lazy/chunks/pie-7F7UUCG3.js +15 -0
  79. package/dist/browser-lazy/chunks/quadrant-TOONO2WC.js +10 -0
  80. package/dist/browser-lazy/chunks/radar-J6JVWVAX.js +16 -0
  81. package/dist/browser-lazy/chunks/render-core-BZOM3K7D.js +3 -0
  82. package/dist/browser-lazy/chunks/requirement-CMAKDJPO.js +19 -0
  83. package/dist/browser-lazy/chunks/sankey-AWXQEP3F.js +13 -0
  84. package/dist/browser-lazy/chunks/sequence-Q2RJRB4L.js +5 -0
  85. package/dist/browser-lazy/chunks/state-WRBFQEDX.js +4 -0
  86. package/dist/browser-lazy/chunks/timeline-D2KILFXM.js +6 -0
  87. package/dist/browser-lazy/chunks/treemap-N3VYX5D4.js +6 -0
  88. package/dist/browser-lazy/chunks/xychart-G73OGK66.js +16 -0
  89. package/dist/browser-lazy/chunks/zenuml-H4BXNYXR.js +16 -0
  90. package/dist/browser-lazy/index.d.ts +544 -275
  91. package/dist/browser-lazy/index.js +1 -1
  92. package/dist/browser.global.js +802 -565
  93. package/dist/chunk-3LGLPG33.js +2 -0
  94. package/dist/chunk-7CRDX2QR.js +2 -0
  95. package/dist/chunk-7PPW2BMO.js +2 -0
  96. package/dist/chunk-DQDKA5ND.js +2 -0
  97. package/dist/chunk-EHCMJUMD.js +52 -0
  98. package/dist/chunk-GVCHLSHH.js +4 -0
  99. package/dist/chunk-H3NCS2XZ.js +19 -0
  100. package/dist/chunk-JZOJ4YGS.js +201 -0
  101. package/dist/chunk-MKE7GNYU.js +146 -0
  102. package/dist/chunk-NKNFOREZ.js +2 -0
  103. package/dist/chunk-O7H2UX5K.js +2 -0
  104. package/dist/chunk-OMY6NRQD.js +58 -0
  105. package/dist/chunk-UA234O4M.js +4 -0
  106. package/dist/chunk-VUFVLGTH.js +2 -0
  107. package/dist/chunk-XU5LPD2D.js +2 -0
  108. package/dist/cli-YGGTJZH7.js +226 -0
  109. package/dist/elk-constructor.js +2 -0
  110. package/dist/elk-constructor.node.js +11 -0
  111. package/dist/index-EeXqJXr5.d.ts +1223 -0
  112. package/dist/index.d.ts +4 -1
  113. package/dist/index.js +1 -334
  114. package/dist/jsonl-RPJY6YWO.js +2 -0
  115. package/dist/mcp-cli-63DYXTVD.js +2 -0
  116. package/dist/{index-BGA_-6LV.d.ts → paint-certification-6rwQjIv_.d.ts} +5283 -6173
  117. package/dist/server-5NEOHOFS.js +524 -0
  118. package/dist/svg-styles-BdnoM7AT.d.ts +724 -0
  119. package/dist/svg-styles.d.ts +1 -0
  120. package/dist/svg-styles.js +2 -0
  121. package/dist/svg.d.ts +13 -0
  122. package/dist/svg.js +2 -0
  123. package/dist/types-BcS52Mjt.d.ts +1360 -0
  124. package/dist/zenuml/chunks/appearance-UOQ5KA6O.js +1 -0
  125. package/dist/zenuml/chunks/builtin-backends-KDKE7YHO.js +12 -0
  126. package/dist/zenuml/chunks/chunk-42DXZ5C6.js +4 -0
  127. package/dist/zenuml/chunks/chunk-7TVYPTDW.js +1 -0
  128. package/dist/zenuml/chunks/chunk-EVPSMEKN.js +1 -0
  129. package/dist/zenuml/chunks/chunk-OXQWRSBM.js +20 -0
  130. package/dist/zenuml/chunks/chunk-QWX77U7G.js +2 -0
  131. package/dist/zenuml/chunks/chunk-RLJEI7YN.js +29 -0
  132. package/dist/zenuml/chunks/chunk-RZ6XK5CD.js +29 -0
  133. package/dist/zenuml/chunks/decode-BWERFDGK.js +1 -0
  134. package/dist/zenuml/chunks/mermaid-yaml-runtime-KZVU74WU.js +136 -0
  135. package/dist/zenuml/chunks/render-core-V5RQ737O.js +3 -0
  136. package/dist/zenuml/chunks/zenuml-EA2ZFRUX.js +20 -0
  137. package/dist/zenuml-frame.global.js +3626 -0
  138. package/dist/zenuml.d.ts +83 -0
  139. package/dist/zenuml.js +1 -0
  140. package/docs/MCP-DIRECTORY-LISTINGS.md +5 -1
  141. package/docs/README.md +59 -103
  142. package/docs/agent-api-cookbook.md +26 -16
  143. package/docs/agent-mutation-policy.md +86 -15
  144. package/docs/agent-workflow-examples.md +1 -1
  145. package/docs/api-advanced.md +385 -0
  146. package/docs/api.md +320 -366
  147. package/docs/ascii.md +40 -4
  148. package/docs/browser.md +46 -7
  149. package/docs/choosing-a-diagram.md +148 -0
  150. package/docs/code-mode-impact.md +1 -0
  151. package/docs/comparison.md +244 -28
  152. package/docs/computational-aesthetics-prototype-plan.md +10 -10
  153. package/docs/config.md +60 -2
  154. package/docs/custom-fonts.md +78 -21
  155. package/docs/diagram-families.md +160 -10
  156. package/docs/eventmodeling-and-zenuml.md +165 -0
  157. package/docs/features.md +21 -19
  158. package/docs/fork-differences.md +105 -32
  159. package/docs/getting-started.md +56 -51
  160. package/docs/layout-certification.md +126 -0
  161. package/docs/mcp-code-mode-rationale.md +20 -12
  162. package/docs/mcp-http-transport.md +14 -8
  163. package/docs/quality.md +145 -15
  164. package/docs/react.md +76 -31
  165. package/docs/style-authoring.md +23 -11
  166. package/docs/svg-semantic-contract.md +104 -4
  167. package/docs/testing-strategy.md +684 -126
  168. package/docs/theming.md +27 -8
  169. package/examples/styles/readme-hero.md +25 -0
  170. package/examples/styles/readme-hero.mmd +13 -0
  171. package/examples/styles/readme-hero.style.json +7 -0
  172. package/package.json +114 -57
  173. package/server.json +2 -2
  174. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +26 -8
  175. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +13 -3
  176. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +2 -1
  177. package/skills/agentic-mermaid-diagram-workflow/references/flowchart.md +9 -6
  178. package/skills/agentic-mermaid-diagram-workflow/references/hosted-mcp.md +1 -0
  179. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +4 -3
  180. package/skills/agentic-mermaid-diagram-workflow/references/timeline.md +7 -6
  181. package/skills/agentic-mermaid-diagram-workflow/references/upstream/agentflow.md +574 -0
  182. package/skills/agentic-mermaid-diagram-workflow/references/upstream/architecture.md +10 -2
  183. package/skills/agentic-mermaid-diagram-workflow/references/upstream/c4.md +18 -0
  184. package/skills/agentic-mermaid-diagram-workflow/references/upstream/classDiagram.md +123 -0
  185. package/skills/agentic-mermaid-diagram-workflow/references/upstream/cynefin.md +6 -1
  186. package/skills/agentic-mermaid-diagram-workflow/references/upstream/entityRelationshipDiagram.md +129 -9
  187. package/skills/agentic-mermaid-diagram-workflow/references/upstream/eventmodeling.md +22 -0
  188. package/skills/agentic-mermaid-diagram-workflow/references/upstream/flowchart.md +188 -60
  189. package/skills/agentic-mermaid-diagram-workflow/references/upstream/gitgraph.md +13 -7
  190. package/skills/agentic-mermaid-diagram-workflow/references/upstream/mindmap.md +110 -1
  191. package/skills/agentic-mermaid-diagram-workflow/references/upstream/packet.md +38 -0
  192. package/skills/agentic-mermaid-diagram-workflow/references/upstream/requirementDiagram.md +115 -0
  193. package/skills/agentic-mermaid-diagram-workflow/references/upstream/sequenceDiagram.md +101 -0
  194. package/skills/agentic-mermaid-diagram-workflow/references/upstream/stateDiagram.md +87 -0
  195. package/skills/agentic-mermaid-diagram-workflow/references/upstream/swimlanes.md +105 -2
  196. package/skills/agentic-mermaid-diagram-workflow/references/upstream/timeline.md +12 -6
  197. package/skills/agentic-mermaid-diagram-workflow/references/upstream/usecase.md +953 -0
  198. package/skills/agentic-mermaid-diagram-workflow/references/upstream/venn.md +77 -0
  199. package/skills/agentic-mermaid-diagram-workflow/references/upstream/xyChart.md +1 -1
  200. package/dist/browser-lazy/chunks/architecture-KTHE2HX3.js +0 -31
  201. package/dist/browser-lazy/chunks/chunk-2XCPFAD5.js +0 -46
  202. package/dist/browser-lazy/chunks/chunk-3H4F7RBT.js +0 -1
  203. package/dist/browser-lazy/chunks/chunk-4EVLW2NJ.js +0 -1
  204. package/dist/browser-lazy/chunks/chunk-5Q4S74UO.js +0 -1
  205. package/dist/browser-lazy/chunks/chunk-6OAPWSDO.js +0 -1
  206. package/dist/browser-lazy/chunks/chunk-A7L4WWVW.js +0 -143
  207. package/dist/browser-lazy/chunks/chunk-C7FJO2I3.js +0 -4
  208. package/dist/browser-lazy/chunks/chunk-D3XXDKIO.js +0 -1
  209. package/dist/browser-lazy/chunks/chunk-DXA5WL7H.js +0 -4
  210. package/dist/browser-lazy/chunks/chunk-EPFMQPXX.js +0 -1
  211. package/dist/browser-lazy/chunks/chunk-FFOHLPDA.js +0 -2
  212. package/dist/browser-lazy/chunks/chunk-FLD63DXE.js +0 -2
  213. package/dist/browser-lazy/chunks/chunk-G6OLSHRG.js +0 -7
  214. package/dist/browser-lazy/chunks/chunk-GRXCKTHX.js +0 -0
  215. package/dist/browser-lazy/chunks/chunk-I6VE56S7.js +0 -1
  216. package/dist/browser-lazy/chunks/chunk-KJVNXJ5Y.js +0 -1
  217. package/dist/browser-lazy/chunks/chunk-L227EPFP.js +0 -11
  218. package/dist/browser-lazy/chunks/chunk-LNIHDJSF.js +0 -1
  219. package/dist/browser-lazy/chunks/chunk-M6BT2HIP.js +0 -1
  220. package/dist/browser-lazy/chunks/chunk-NZRUMODH.js +0 -1
  221. package/dist/browser-lazy/chunks/chunk-OFRMKQA6.js +0 -1
  222. package/dist/browser-lazy/chunks/chunk-P7HMSKHJ.js +0 -1
  223. package/dist/browser-lazy/chunks/chunk-PXKL3O6H.js +0 -1
  224. package/dist/browser-lazy/chunks/chunk-PZEWZT35.js +0 -1
  225. package/dist/browser-lazy/chunks/chunk-STQRDZ67.js +0 -1
  226. package/dist/browser-lazy/chunks/chunk-SUFMSWTX.js +0 -1
  227. package/dist/browser-lazy/chunks/chunk-T7IJ6V4B.js +0 -3
  228. package/dist/browser-lazy/chunks/chunk-UAEZO5VL.js +0 -1
  229. package/dist/browser-lazy/chunks/chunk-WEUWIFG3.js +0 -1
  230. package/dist/browser-lazy/chunks/chunk-X64T74C5.js +0 -24
  231. package/dist/browser-lazy/chunks/class-F7R6TJ73.js +0 -9
  232. package/dist/browser-lazy/chunks/er-BDK7CM5L.js +0 -9
  233. package/dist/browser-lazy/chunks/flowchart-KFU6XV7S.js +0 -4
  234. package/dist/browser-lazy/chunks/gantt-I64VVDSP.js +0 -28
  235. package/dist/browser-lazy/chunks/gitgraph-EDUVJ5IJ.js +0 -8
  236. package/dist/browser-lazy/chunks/journey-TLO2RNM3.js +0 -40
  237. package/dist/browser-lazy/chunks/mindmap-OJCNDYPM.js +0 -9
  238. package/dist/browser-lazy/chunks/pie-HVG6XO7K.js +0 -19
  239. package/dist/browser-lazy/chunks/quadrant-U6S2L4MJ.js +0 -24
  240. package/dist/browser-lazy/chunks/radar-ZDLOBTVG.js +0 -32
  241. package/dist/browser-lazy/chunks/render-core-Y2DFNMQG.js +0 -12
  242. package/dist/browser-lazy/chunks/sequence-Z4WFS3VB.js +0 -12
  243. package/dist/browser-lazy/chunks/state-2HV2YGN3.js +0 -4
  244. package/dist/browser-lazy/chunks/timeline-XM4UFPOC.js +0 -20
  245. package/dist/browser-lazy/chunks/xychart-7JDHWTQY.js +0 -36
  246. package/dist/chunk-GGXCNCNA.js +0 -4118
  247. package/dist/chunk-IVD4NXNE.js +0 -615
  248. package/dist/chunk-JDMRPUAG.js +0 -37
  249. package/dist/chunk-JWAAW3FH.js +0 -149
  250. package/dist/chunk-LCGBT32P.js +0 -62036
  251. package/dist/chunk-RWMZRUIC.js +0 -4715
  252. package/dist/mcp-cli-4MLKC3ZH.js +0 -17
  253. package/docs/mutation-testing.md +0 -285
@@ -7,30 +7,66 @@ than it looks, this document says so — an honest map is more useful than a
7
7
  flattering one.
8
8
 
9
9
  For the *definition* of "good looking" and the determinism guarantees, see
10
- [`quality.md`](./quality.md). For mutation specifics, see
11
- [`mutation-testing.md`](./mutation-testing.md). For the layout/visual
10
+ [`quality.md`](./quality.md). For the layout/visual
12
11
  contracts, see [`layout-characterization/README.md`](./layout-characterization/README.md).
13
- The current measured complexity-aware and registry-derived interaction
14
- portfolio is specified in
15
- [`project/complexity-aware-test-portfolio-plan.md`](./project/complexity-aware-test-portfolio-plan.md);
16
- its immutable before and content-addressed candidate reports live under
17
- `eval/test-portfolio/`.
12
+ The complexity-aware and registry-derived interaction portfolio is implemented
13
+ in `src/__tests__/render-conformance-plan.test.ts` and its helpers. The
14
+ [July migration record](./project/archive/complexity-aware-test-portfolio-plan.md)
15
+ is historical; its removed reports and receipts are not current requirements.
18
16
 
19
17
  ## Local CI parity
20
18
 
19
+ Test-order and fresh-process sweeps are manual diagnostics for changes to shared
20
+ fixtures or module state. Replay a focused failure with
21
+ `AM_TEST_ORDER_SEED=<seed> bun run scripts/ci/find-test-order.ts shuffled <files>`
22
+ or replace `shuffled` with `fresh-process`. No scheduled whole-suite order sweep
23
+ or issue collector runs. Each test must initialize its own reference fixture;
24
+ another test's initialization is not a prerequisite.
25
+
21
26
  Run `bun run quality:check` to execute the complete GitHub **Quality gates**
22
27
  job locally. The command installs the frozen dependency graph and then runs
23
- the dependency audit, font and website regeneration checks, evidence
24
- freshness, sketch and whole-corpus audits, lint, repository-wide typechecking,
25
- hero freshness, and the golden-drift guard. The workflow calls this same
26
- entry point, so the local list and CI list cannot diverge.
27
-
28
- Run `bun run evidence:check` for the narrower generated-evidence audit. It
29
- executes every receipt-backed gallery plus palette-performance provenance,
30
- continues after failures, and reports all stale artifacts together. Evidence
31
- receipts hash their exact local source/fixture graph and the transitive Bun
32
- lockfile closure of packages imported by that graph. Unrelated tools that are
33
- present elsewhere in `bun.lock` do not invalidate visual evidence.
28
+ the dependency audit, the font-subset, lazy browser-family catalog, and website
29
+ regeneration checks, the whole-corpus audit, lint,
30
+ repository-wide typechecking, and hero freshness (the
31
+ list is `QUALITY_CHECKS` in `scripts/ci/quality-gates.ts`). The workflow calls
32
+ this same entry point, so the local list and CI list cannot diverge.
33
+
34
+ The E2E lanes build their own inputs and start alongside unit tests; only the
35
+ final `CI complete` gate waits for every lane. Package manifest and export/type
36
+ checks run after the Node 24 tarball consumer has built the package, rather
37
+ than rebuilding all browser artifacts in a unit shard. Unit shards build only
38
+ the Node bundle. Browser contracts run in two disjoint file shards, retaining
39
+ one test process per file. Payload measurement has its own lane: each site
40
+ build supplies the lazy bundle for comparison, so neither head nor base needs
41
+ a second full package build. Generic PR render evidence is generated on demand
42
+ with `scripts/pr-assets/meaning-preservation-evidence.ts BASE_SHA HEAD_SHA OUT_DIR`;
43
+ automatic rendering of the same fixtures is no longer appended to every PR.
44
+
45
+ Both tarball-consumer lanes also execute the Getting Started commands from the
46
+ built website's shell blocks and `docs/getting-started.md`'s bash fences in fresh
47
+ consumer directories. Only the registry package spec is replaced with the packed
48
+ tarball; the guide's separate repository-development alternative is excluded.
49
+ The gate checks verification JSON, parsed SVG and PNG files, terminal output,
50
+ style discovery, and stdio MCP initialization/tool discovery for both the CLI
51
+ command and the version-pinned config card. Failures name the page, step, and
52
+ copied command. Run it locally with
53
+ `cd e2e && bun test tarball-consumer-fuzz.e2e.test.ts --timeout 600000`.
54
+
55
+ Committed galleries and before/after sheets are dated review snapshots, not
56
+ gates: regenerate one with its `gallery:*` command when the rendering is
57
+ reviewed again. Where an evidence script carried a real assertion, the unit
58
+ suite now runs it against current code (for example the issue #87 link-rank
59
+ gaps and the Section B sheet, which is regenerated in memory and
60
+ byte-compared). Generated runtime modules are the narrow committed exception
61
+ when production code imports them. Other derived discovery views are generated
62
+ at delivery: `am llms-txt` and the website use current registries, while the
63
+ repository's authored `llms.txt` points readers to those sources. A second
64
+ committed capability roster and its snapshot-freshness test are unnecessary.
65
+ There are no input-hash receipts.
66
+
67
+ The sketch prototype remains an on-demand research tool (`bun run sketch:check`).
68
+ Production styling is exercised by the Scene/backend and render-conformance
69
+ tests; the browser label-contrast check renders the shipped looks.
34
70
 
35
71
  ## The central problem: this is a partly non-testable program
36
72
 
@@ -61,7 +97,6 @@ audit-clean").
61
97
  | **Derived (golden)** | Output matches a previously-blessed artifact | ASCII goldens, SVG snapshots, contact sheets, screenshot baselines |
62
98
  | **Derived (differential)** | Our output agrees with an external reference corpus | mermaid-docs corpus, MermaidSeqBench, upstream-suite bench |
63
99
  | **Metamorphic** | Related inputs produce related outputs (no ground truth needed) | round-trip idempotence, cross-process/cross-runtime determinism |
64
- | **Pseudo / fault sensitivity** | Selected seeded faults make the intended tests fail | incremental Stryker gate, sabotage suite |
65
100
  | **Heuristic / perceptual** | Geometry falls inside human-plausible bounds | `measureQuality`/`checkQuality`, ugly-detector, layout rubric, heuristic-tracker |
66
101
  | **Human / model** | Subjective quality on the axis nothing else covers | LLM-as-judge (periodic), manual visual review |
67
102
 
@@ -70,16 +105,28 @@ whether it gates per-PR, and what it does *not* prove.
70
105
 
71
106
  ## 1. Specified oracles — structural correctness
72
107
 
73
- **Tier-1 `verify`** is the non-negotiable structural gate: no
74
- `EMPTY_DIAGRAM`, `EDGE_MISANCHORED`, `OFF_CANVAS`, `GROUP_BREACH`,
75
- `UNKNOWN_SHAPE`, or `LABEL_OVERFLOW`. It is reliable and universal across
76
- families. **Tier-2** (geometric) and **Tier-3** (lint) are advisory.
108
+ **Tier-1 `verify`** is the non-negotiable structural gate: none of
109
+ <!-- BEGIN GENERATED: warning-codes:structural -->`EMPTY_DIAGRAM`, `EDGE_MISANCHORED`, `OFF_CANVAS`, `GROUP_BREACH`, `UNKNOWN_SHAPE`, `LABEL_OVERFLOW`, `UNRESOLVABLE_SCHEDULE`, `RENDER_FAILED`, `INTERNAL_ERROR`<!-- END GENERATED: warning-codes:structural --> fires. It is reliable and universal across
110
+ families. Geometric and lint findings use their canonical severity: most are
111
+ advisory, while measured `CONTENT_OCCLUDED` and an explicit error policy fail verification.
77
112
 
78
113
  Alongside it sit the **contract gates**: `am capabilities --json` schema
79
- tests, `doc-sync`/`agent-doc-sync` tests, and the diagram-family
80
- citizenship matrix. For an agent-native product the docs, schemas, CLI
81
- help, and `llms.txt` *are* runtime surface (Loop 14), so they are tested
82
- like code, not treated as prose.
114
+ tests, the `agent-doc-sync` tests, and the behavioural
115
+ diagram-family citizenship loop. For an agent-native product the docs, schemas, CLI
116
+ help, and live generated discovery *are* runtime surfaces (Loop 14), so their
117
+ behavior is tested like code. The repository `llms.txt` is an authored pointer
118
+ to those live surfaces.
119
+
120
+ Citizenship requires a small smoke input and a domain-rich scenario for every
121
+ registered built-in family. Required witnesses in the existing closed
122
+ `FAMILY_CONFORMANCE_PROFILES` record specify independent semantic expectations,
123
+ visible labels and a meaningful typed edit. Those expectations must survive
124
+ canonical export/reload and editing, alongside verification, graphical/terminal
125
+ rendering, layout regions and uncached determinism. This closes the former gap
126
+ where a minimal count fixture passed citizenship while richer inputs appeared
127
+ only in the separate rendering portfolio. Source length and node counts do not
128
+ establish representativeness; the interacting domain features do. See the
129
+ [citizenship review contract](./contributing/diagram-family-citizenship.md#representative-examples-are-mandatory).
83
130
 
84
131
  **Runs:** every PR (`bun run test`, including the canonical 30-second per-test budget).
85
132
  **Does not prove:** that the diagram is *visually* good. `verify.ok` is
@@ -95,25 +142,24 @@ independent spec:
95
142
  - **SVG snapshots** and **contact sheets** — per-family rendered output.
96
143
  - **Screenshot baselines** — `e2e/screenshots/baseline-*.png`, diffed against
97
144
  fresh Playwright renders with a per-channel threshold in `e2e/browser.test.ts`.
98
- - **Snapshot drift sentinel** — CI flags any change under `testdata/` so a
99
- golden never moves silently.
145
+ - **Golden review** — each PR description lists every changed golden under
146
+ `src/__tests__/testdata/` and why it changed, for reviewer judgment.
100
147
 
101
148
  **Runs:** ASCII/SVG goldens per PR. The browser/screenshot e2e suite also runs
102
- per PR — it is the five-lane `e2e` matrix in `ci.yml` (`needs: test`), including
149
+ per PR — the `e2e` matrix in `ci.yml` runs alongside unit and quality checks, including
103
150
  an installed-tarball consumer run at the advertised Node 22 floor. Its
104
- browser lane installs Chromium and runs `browser.test.ts` (screenshot diff)
105
- plus the security contracts and the explicitly enabled theme/style/accessibility
106
- browser suites. The browser lane builds the gitignored `website/public` tree once
107
- before those contracts serve it (`browser.test.ts` builds only the separate root
151
+ browser shards retain Chromium/Firefox/WebKit bundle checks, `browser.test.ts`
152
+ (screenshot diff), security contracts, and the explicitly enabled
153
+ theme/style/accessibility suites. The site-only shard needs Chromium. Each
154
+ shard builds the gitignored `website/public` tree once before its contracts serve it (`browser.test.ts` builds only the separate root
108
155
  `editor.html`). Each browser contract file then runs in its own Bun process so its
109
156
  Chromium/server hooks cannot overlap and exhaust a constrained hosted runner. The
110
157
  package and CI both invoke `e2e/run-browser-contracts.ts`, whose canonical file
111
158
  list also fails if any contract executes zero positive tests, while independent
112
159
  lanes run the CLI/single-binary, dist-artifact, and tarball-consumer suites.
113
- Broad contact-sheet rendering is periodic/triggered rather than a per-PR sweep;
114
- the committed citizenship manifest and pending review state remain cheap
115
- per-PR contracts. Structured human review is recommended for visual releases
116
- and remains hash-bound when recorded, but it is advisory rather than a package-publication gate.
160
+ Broad contact-sheet rendering is periodic/triggered rather than a per-PR sweep.
161
+ Human review is recommended for visual releases, but it is advisory rather than
162
+ a package-publication gate.
117
163
  **Does not prove:** that a *changed* golden is an improvement — only that
118
164
  change was noticed. Judging the change still needs a human or the
119
165
  before/after harness (`eval/layout-compare`).
@@ -131,34 +177,133 @@ Style registries and mandatory family conformance profiles generate:
131
177
  palette-sensitive role, external-reference and contact-sheet obligations;
132
178
  - a separate SVG/PNG/ASCII/Unicode portfolio with every-family Unicode stress.
133
179
 
134
- An independently implemented tuple enumerator must report zero missing
135
- obligations. Fake/removed-family, precedence, strict-reference, transparency,
136
- seed and terminal sabotage probes guard fault sensitivity. A new `DiagramKind`
137
- cannot compile without its minimal, representative, dense, text-stress,
138
- family-risk and eval-corpus-outlier conformance material.
139
-
140
- **Runs:** executable conformance and the bounded fault probes run per PR. The
180
+ Each family profile declares its automatic-outline roles or explains why they
181
+ do not apply. The SVG portfolio binds those Scene obligations to the actual
182
+ emitted primitives, including class-styled swatches and sketched outline
183
+ siblings. It checks positive visible ink and 3:1 source-paint contrast against
184
+ known opaque pages, preserves declared opacity, and requires every applicable
185
+ family/role/backend to have a witness. Representative looks without explicit
186
+ border roles exercise all six strata, including the default crisp look.
187
+ Deliberate role colors, `none`, and authored zero-width borders have separate
188
+ preservation checks; active family paint aliases are treated as overrides.
189
+ These checks do not certify every antialiased pixel or an unknown transparent
190
+ host background.
191
+
192
+ The planner and tuple-verifier helpers describe the interaction obligations;
193
+ the retained tests exercise family-profile closure, resolver behavior,
194
+ rendered output and determinism. Do not infer current enforcement merely from
195
+ the existence of a helper or from the historical plan's removed sabotage suite.
196
+ A new `DiagramKind` requires typed profiles and generators, including an
197
+ outline declaration. Missing fixture files or eval-corpus examples fail when
198
+ the conformance inputs are constructed.
199
+
200
+ **Runs:** executable conformance runs per PR. The
141
201
  larger citizenship/interaction/outlier contact sheets run when visual inputs
142
202
  change, during periodic design review, or before release.
143
203
  **Does not prove:** an unrendered three-way family/Look/Palette combination is
144
- beautiful. The exact retained obligations are published by the candidate
145
- report rather than implied by the word “exhaustive.”
204
+ beautiful. Inspect the current planner, verifier and actual assertions for the
205
+ retained obligations; there is no published candidate report to serve as a gate.
146
206
 
147
207
  ## 3. Derived oracles — differential corpora
148
208
 
149
209
  The strongest defense against our own blind spots is testing against
150
210
  sources we did not write:
151
211
 
212
+ - **Modern semantic gate** (`bun run test:mermaid-modern`, after
213
+ `bun run reference:mermaid` installs the pinned source and reference locally)
214
+ — selected assertions from the current Mermaid policy pin, with the exact
215
+ source commit and independently installed parser admitted before execution. Repeated subgraph declarations
216
+ must retain both member identities and the first title in public IR, typed
217
+ editing, positioned layout and SVG. Repeated class assignments must survive
218
+ typed rename and serialization in author order. Two official XYChart
219
+ examples must serialize to valid Mermaid with their titles, axes, values and
220
+ point labels intact. Reference projections contain those semantic fields;
221
+ Class arrow markers, metadata-shaped Flowchart named edges, quoted Mindmap
222
+ delimiters and reserved Quadrant axis text also have reviewed writer
223
+ assertions. These preservation assertions are complemented by reviewed seeded writer
224
+ regressions and operation checks: each native family's canonical example plus focused
225
+ witnesses compares selected ordered builder operands against the real
226
+ reference. The operation scopes are declared per family and do not imply
227
+ equality of all internal calls.
228
+ The checks do not compare Mermaid geometry or styling. Existing State, Architecture
229
+ and Sequence syntax gaps are recorded as diagnostics, not permanent expected
230
+ failures. This gate has an explicit local command and the dedicated **Modern
231
+ Mermaid semantic compatibility** workflow is a required reusable lane of canonical CI
232
+ for every PR and main revision, including reference policy and corpus changes; it does not change the normal product
233
+ compatibility pin or development dependency.
234
+ The job also publishes all-family documentation outcomes and 100 seeded
235
+ mutations as a seven-day diagnostic artifact. Those counts do not gate
236
+ unsupported families or expand the reviewed assertions.
237
+
238
+ **All-family grammar assessment** (`bun run eval:mermaid-grammar`) uses the
239
+ actual selected Mermaid 12+ grammar for every core family, including families
240
+ without native Agentic support. It records lexer tokens, Jison model calls or
241
+ Langium/Chevrotain syntax trees and native renderer-parser admission. Hooks
242
+ inside all 17 native parsers record consumed classified statements and actual
243
+ semantic writes, with values copied before later mutation; Flowchart also
244
+ records its consumed compound fragments. Every observed native attempt has
245
+ an unobserved acceptance/error/model control. A changed result fails the
246
+ assessment as an instrumentation failure.
247
+
248
+ Independent per-family adapters compare selected native semantic fields
249
+ against actual reference getters/actions/AST, retaining logical IDs, marker
250
+ sides, labels and parents where applicable. Additional adapters compare
251
+ ordered operands from actual native mutation events and actual reference
252
+ calls, expanding recorded batch arguments rather than returned IR. These
253
+ arrays are never sorted. Each case declares the compared operation scopes
254
+ and counts omitted kinds, methods and batch operands; empty scoped traces
255
+ are unavailable evidence. Stable-getter and original-to-serialized reference
256
+ comparisons remain separate. Parent fields are retained, and generated-looking
257
+ authored strings are not globally rewritten. None of these selected scopes
258
+ proves full semantic fidelity. Manual runs default to 5,000
259
+ seeded mutations per family; required CI uses 100 per family at seed 7.
260
+ The separate weekly contract-discovery job samples at a recorded fresh seed
261
+ with the bounds described below. Mutations draw on the
262
+ corpus's own token images and on fixed lexer-boundary fragments
263
+ (`grammar-fragments.ts`). Each run counts **silent reads** per family (pinned
264
+ Mermaid rejects, ours reads, verify names no problem) and lists them reduced;
265
+ weekly discovery retains confirmed public-contract candidates for human
266
+ review without opening issues. Per-input JSONL and detailed counterexamples are
267
+ ignored build artifacts. This measures native admission and reference grammar
268
+ round trips as well as the scoped native semantics/operations. Agentic has
269
+ no homologous Mermaid token/`yy` interface, so exact protocol equality
270
+ remains unavailable; the actual native units are labelled separately. Exact reference stream differences
271
+ need semantic review and are not conflated with lost content or layout.
272
+
152
273
  - **mermaid-docs corpus** (271 entries) — harvested from Mermaid's own
153
274
  documentation; gates parse, verify, and round-trip.
154
275
  - **MermaidSeqBench** (132 IBM-curated sequence diagrams) — parse rate,
155
276
  structured-vs-opaque split, verify rate, round-trip stability.
156
- - **upstream-suite bench** (`eval/mermaid-upstream-suite-bench/`, 736
157
- executable cases / 746 imported blocks from pinned upstream commits) — with
277
+ - **upstream-suite bench** (`eval/mermaid-upstream-suite-bench/`, 742
278
+ executable cases / 752 imported blocks from pinned upstream commits) — with
158
279
  a **ratchet** (`ratchet.json`) that fails the build if coverage drops or
159
- per-family local-gap budgets grow. Mindmap/GitGraph use their declared
160
- `f3dea583…` compatibility revision and account for all 26/69 official spec
161
- blocks in a dedicated executable oracle.
280
+ per-family local-gap budgets grow. These imported corpora retain the source
281
+ revisions recorded in their manifests. Mindmap/GitGraph retain their
282
+ `f3dea583…` source revision and account for all 26/69 official spec blocks in
283
+ a dedicated executable oracle; that historical corpus pin is separate from
284
+ the current writing/reference target.
285
+ - **Official-fence corpus** (`official-fence-corpus.test.ts`) — every one of
286
+ the Mermaid syntax fences on the pinned upstream pages named by the
287
+ upstream policy. The fences of rendered families must parse to a
288
+ structured body, verify, render, and keep their structural counts through
289
+ serialize → re-parse; the fences of unrendered families must be
290
+ diagnosed `UNSUPPORTED_FAMILY` with their bytes preserved. Deviations live in
291
+ one small expectations table (`id [ outcomes ] # reason`); a new deviation
292
+ and an unexpected pass both fail, so the table cannot go stale.
293
+ - **Grammar-based differentials** (`property-upstream-flowchart.test.ts`,
294
+ `property-upstream-sequence.test.ts`) — recursive `fc.letrec` grammars
295
+ generate sources that pinned upstream Mermaid 12.2.0 must accept; ours must
296
+ accept them too, agree on node/edge/participant counts, shapes, labels, and
297
+ arrows, and round-trip. A long-lived upstream worker
298
+ (`helpers/upstream-mermaid.ts`) lets async properties shrink. Known
299
+ divergences are pinned as explicit cases that fail once fixed.
300
+ The worker runs Node (CI uses Node 24) with development-only jsdom 26.1.0,
301
+ public preprocessing and actual DOMPurify; no sanitizer is replaced. The
302
+ Flowchart grammar property observes authored label operands at live builder
303
+ calls while still invoking each original method. Sanitized HTML can lose
304
+ those tokens, so that property is a grammar contract rather than a claim of
305
+ identical sanitized DB text. Selected modern tests independently inspect
306
+ public runtime values. Snapshots retain parent fields and explicit cycles.
162
307
 
163
308
  These exist because our hand-written fixtures encode what we already knew
164
309
  the parser modeled. Upstream examples are "adversarial in exactly the
@@ -166,15 +311,32 @@ right way" — they encode what real sources contain (wrapper-fidelity
166
311
  lesson). The `@{shape}` silent-loss and ER `}o` bugs both escaped 2,200+
167
312
  self-authored tests and were caught only by docs-derived sources.
168
313
 
169
- **Runs:** every PR (corpus + seqbench + upstream-suite ratchet).
314
+ Harvesting an upstream source is different from porting its assertions. A
315
+ fixture count recorded from our own implementation protects existing behavior;
316
+ it does not independently establish agreement with Mermaid's semantic model.
317
+ The selected modern assertions use reviewed upstream expectations and narrow
318
+ reference projections to close that gap for the covered behavior.
319
+
320
+ **Runs:** every PR (corpus + seqbench + upstream-suite ratchet); a separate CI
321
+ job installs the pinned modern reference for the selected semantic gate when
322
+ its code inputs change. Its broader
323
+ [`eval/mermaid-modern-compat`](../eval/mermaid-modern-compat/README.md) runner
324
+ reports documentation coverage across every modern core family, including
325
+ unsupported families. Optional extracted spec literals and seeded mutations
326
+ remain diagnostic seed corpora. Extracted test titles, CSS fragments and empty
327
+ scaffolds are not feature support or ported upstream assertions. Raw getter
328
+ differences are drift candidates requiring review; only the narrow assertions
329
+ above fail the semantic gate.
170
330
  **Faithfulness count-oracle:** the corpus gate now also asserts that the
171
331
  structured `{nodes, edges, groups}` tally survives a parse → serialize →
172
332
  re-parse cycle for **every** renderable family
173
333
  (`eval/shared/structural-count.ts`, gated in `agent-mermaid-corpus.test.ts`).
174
- Round-trip *byte*-stability only proves serialize∘parse is idempotent; this
175
- count check proves parse did not silently drop content — the ER `}o` class of
176
- bug ("100% parse success is not faithfulness", Loop 17) now fails CI directly
177
- rather than only where a hand-written oracle happened to bite.
334
+ Round-trip *byte*-stability only proves serialize∘parse is idempotent. The
335
+ count check detects changes in the number of nodes, edges and groups, including
336
+ the ER `}o` class of bug. It cannot establish that identities, labels, class
337
+ assignments or group membership remain correct, or that SVG displays every
338
+ parsed element. The modern semantic gate checks selected identity and content
339
+ contracts through independent upstream expectations and actual output.
178
340
 
179
341
  ## 4. Metamorphic oracles — relations instead of ground truth
180
342
 
@@ -197,6 +359,17 @@ must agree." This sidesteps the oracle problem without a human:
197
359
  Statement-permutation invariance is deliberately *not* asserted — source
198
360
  order is a stated design property, so permuting statements may legitimately
199
361
  change geometry.
362
+ - **Colour and source relations** — `property-invariance-colour.test.ts`:
363
+ palettes, colour options, named themes, theme colour variables, and family
364
+ colour config never move geometry (layout and paint-free SVG, via the shared
365
+ `helpers/svg-normalize.ts` `stripPaint` option), and an invalid value in any
366
+ painted key is refused by name rather than emitted or dropped (the #303
367
+ class, for every family at once). `property-invariance-source.test.ts`:
368
+ inserting `%%` comments and blank lines leaves SVG byte-identical, and a
369
+ chained flowchart edge equals its split form in facts, layout, and bytes.
370
+ - **Model-based editing** — `property-er-model.test.ts` drives the typed ER
371
+ edit operations with `fc.commands` against a simple model, including invalid
372
+ commands whose error code the model predicts.
200
373
 
201
374
  **Runs:** every PR.
202
375
  **Does not prove:** cross-architecture equality (x86_64 hash vs ARM64 hash
@@ -216,47 +389,131 @@ mutating process-global configuration).
216
389
 
217
390
  - Reproduce a specific roll: `AM_FC_SEED=<int> bun test <file>`.
218
391
  - Finder mode (deliberate randomness): `AM_FC_SEED=random bun run test`.
219
- - The policy is itself gated: `fc-seed-policy.test.ts` fails if the preload is
220
- unwired and source-checks every test file so suite-specific seeds cannot use
221
- process-global overrides in another shard; `zzz-fc-seed-policy-epilogue.test.ts`
222
- also catches runtime drift within its own process.
392
+ - A suite that needs its own regression seed passes it to `fc.assert`
393
+ options through `fcParameters(...)` from `src/__tests__/helpers/fc-parameters.ts`;
394
+ process-global configuration belongs to the preload alone. The helper keeps
395
+ local pins in ordinary gates, lets explicit `AM_FC_SEED` replay/finder controls
396
+ override them, and applies `AM_FC_NUM_RUNS` as a minimum without lowering a
397
+ stronger local case count. Use it for explicit `fc.assert`/`fc.check` options
398
+ and exploratory `fc.sample` calls. No-options runners inherit the preload
399
+ directly. A calibrated fixed-corpus ratchet must retain its exact seed and
400
+ cardinality: its finding budget is not a universal property. The hard-metric
401
+ properties beside the layout-rubric ratchets remain eligible for finders.
223
402
 
224
403
  Pinning is for *holding* known ground; randomness is for *finding* new
225
404
  counterexamples ("an invariant enforced by a random property is a lottery,
226
405
  not a gate" — `docs/contributing/lessons-learned.md`). Rolling seeds belong in
227
- finder lanes: the deep-fuzz scripts under `eval/`, or deliberate
228
- `AM_FC_SEED=random` sweeps. Before the pin was frozen, every unpinned suite was
406
+ finder lanes: the weekly `contract-discovery.yml` workflow, the deep-fuzz scripts
407
+ under `eval/`, or deliberate `AM_FC_SEED=random` sweeps.
408
+
409
+ The weekly **Contract discovery** workflow is a single read-only job capped at
410
+ ten runner-minutes, including setup. It first rolls one recorded seed through
411
+ the existing Flowchart and ER shadow-model command properties and the valid
412
+ Architecture round-trip property. Valid model generators construct legal grammar
413
+ contexts; reserved/malformed identifiers have separate source-preservation,
414
+ diagnostic and mutation-refusal tests. The normal pinned gate remains deterministic.
415
+
416
+ A finder-only fast-check reporter writes structured seed/path, counterexample,
417
+ callsite, run/shrink counts and failure details. Bun's JUnit result supplies the
418
+ owning named test and setup errors. Capturing a property failure still throws;
419
+ recording a failure cannot turn it into a pass. There is no display-log scraper,
420
+ property-title suppression list or GitHub issue-writing permission. Model failure
421
+ stops further search for that run and retains the replay evidence.
422
+
423
+ The grammar stage uses a seed-selected corpus sample (at most eight short inputs
424
+ per native family), 100 text mutations per family, two workers, at most two
425
+ retained candidates per contract/family and eight reduction checks. These are
426
+ bounded discovery settings, not a claim of exhaustive grammar coverage. It keeps
427
+ portable-write, native reload/meaning and source-diagnostic candidates separately
428
+ from raw reference token/action/model differences. A reduction must retain the
429
+ same candidate contract violation; otherwise the original witness remains. Partial
430
+ summaries and the last source survive an interrupted search. Expensive native/
431
+ reference instrumentation is a diagnostic; parser disagreement alone is not a bug.
432
+
433
+ Canonical CI still replays the reviewed corpus in
434
+ `eval/mermaid-modern-compat/corpus/grammar-finder-494.json` with `--check` and runs
435
+ the required pinned semantic and packed-consumer gates. The historical filename
436
+ retains provenance; it is not an issue-notification channel. Review-pin changes
437
+ invalidate reporting matches. A repaired input may improve: the corpus never
438
+ requires a known defect to persist.
439
+
440
+ Discovery emits JSON, a step summary and retained artifacts for human review;
441
+ it never creates or comments on issues. A failed run must be classified as a
442
+ product contract violation, test-domain/oracle mistake, reference/setup failure,
443
+ or interrupted search before assigning finite work. Replay on the current pin
444
+ and main; retain a minimal original/model counterexample in the existing owning
445
+ property and fix the failure class. Review existing finite grammar issues before
446
+ opening additional work. Useful yield is reproducible confirmed failure classes
447
+ per runner/review hour, with replay completeness and triage time alongside it;
448
+ mutation, disagreement and ticket counts do not measure correctness.
449
+ Pause recurring discovery while confirmed findings await repair or its oracle
450
+ needs correction; replay those finite inputs before spending another search budget.
451
+
452
+ The former nightly workflow and #362 collector are retired. Historical runs and
453
+ counterexamples remain evidence. Their open-ended notification infrastructure is
454
+ not needed to keep valid regression coverage. Larger grammar or order searches
455
+ remain explicit manual commands for targeted changes and reference upgrades.
456
+
457
+ Before the pin was frozen, every unpinned suite was
229
458
  swept across 48/24/12 seeds (scaled by runtime; 1,368 suite-runs total) with
230
459
  zero failures — the pin does not freeze a known-bad ticket, and the sweep is
231
460
  repeatable from the same knob.
232
461
 
462
+ ### Applying Hegel guidance with fast-check
463
+
464
+ Keep fast-check; use the upstream [Hegel skill](https://github.com/hegeldev/hegel-skill/tree/main/skills/hegel)
465
+ and [review checklist](https://github.com/hegeldev/hegel-skill/blob/main/skills/hegel-review/SKILL.md)
466
+ to improve properties, not to require a library migration. Derive expectations
467
+ from public contracts or an independent model, and check exact values as well
468
+ as validity. Cover valid inputs and near-valid rejection separately; preserve
469
+ the source when an agent parser deliberately falls back to opaque.
470
+
471
+ `fc.string()` defaults to printable ASCII. Choose `unit: 'grapheme'` for
472
+ printable Unicode, `unit: 'binary'` for Unicode scalars including controls, or
473
+ an explicit UTF-16 code-unit arbitrary for helpers accepting arbitrary JS
474
+ strings. Keep hostile tokens alongside broad generators. Construct valid
475
+ grammar contexts instead of filtering out failures; an explicit `examples`
476
+ entry bypasses the generator, including its maps and filters.
477
+
478
+ For cheap string/parser properties, run thousands of cases. Bound expensive
479
+ render materialization deliberately, vary relevant configuration, and add a
480
+ separate scale probe without a machine-specific timing threshold. Independent
481
+ XML parsing proves well-formedness; finding `<svg` and `</svg>` does not.
482
+ Use `fc.assert(fc.property(...))` for per-input invariants so failures shrink;
483
+ reserve `fc.sample` for aggregate/distribution measurements.
484
+
485
+ When applying the skill, have a separate reviewer derive missing properties
486
+ from implementation and contracts without seeing the tests. Record every
487
+ observed failure as either a reduced product finding or a corrected test
488
+ mistake. Do not silently trim a generator or relax an assertion after it fails.
489
+
233
490
  ## 5. Pseudo-oracles — do selected seeded faults make the suite fail?
234
491
 
235
- Tests can be green while missing faults. Two bounded gates challenge selected
236
- behaviors:
237
-
238
- - **Incremental mutation testing (Stryker)** — mutates the small, pure
239
- faithfulness counter on every PR. A full run takes about one minute and its
240
- measured floor is enforced. Broader ASCII, route, and family configs remain
241
- available as opt-in survivor harvests, not scheduled gates. A *survived*
242
- mutant is a test gap until classified; the historical catalogs record
243
- reviewed performance guards and unreachable-by-convention cases. Mutation
244
- testing has earned its keep when focused: it falsified an audit assumption
245
- and found dead code the unit tests couldn't.
246
- - **Sabotage suite** (`eval/sabotage/route-regressions.ts`) — deliberately
247
- reverts a fixed bug in a detached worktree and asserts the suite goes
248
- **red**, proving five named route/link regression tests actually bite.
249
-
250
- The broad scheduled mutation matrix was retired after 26 consecutive scheduled
251
- runs produced no success and its final repair grew to 39 coverage workers (41
252
- jobs total). The configs still
253
- emit useful local scores and JSON reports, but have no break floors and carry no
254
- acceptance authority. `docs/mutation-testing.md` records the commands, survivor
255
- history, retained operational evidence, and re-enrollment criteria.
256
-
257
- **Why this matters:** line coverage is reported per-PR as one merged LCOV
258
- artifact assembled from the three unit shards, but it is a weak
259
- adequacy signal — coverage is not strongly correlated with fault detection
492
+ Tests can be green while missing faults. The red-green-refactor cycle is the
493
+ defence: write the test, watch it fail for the right reason, then fix.
494
+
495
+ - **Red → green** (`scripts/ci/red-green.ts`, the `red-green` CI job) — when a
496
+ pull request changes production source and tests, at least one changed test
497
+ must produce an assertion failure attributed to a test location against base
498
+ production. Missing imports, non-assertion setup errors, unlocated suite-hook
499
+ assertions, timeouts and aborted runners are not
500
+ behavioral evidence; the gate reads the pinned Bun runner's JUnit report.
501
+ Unexpected production exceptions need an explicit `toThrow` / `not.toThrow`
502
+ assertion. This mechanically
503
+ checks the "tests that fail when the fix is reverted" rule for every PR,
504
+ instead of a hand-written probe per fix. Pure refactors opt out with the
505
+ `no-red-green` label. Like `payload-growth-approved`, it approves only the
506
+ head it was applied to (`scripts/ci/label-approval.ts`): CI ignores a label
507
+ applied before the latest push and removes it when new commits arrive.
508
+ Bun's JUnit report cannot distinguish located per-test hook assertions from
509
+ test-body assertions. Review their meaning: this gate is fault evidence, not
510
+ a substitute for auditing whether each assertion exercises production behavior.
511
+
512
+ **Why this matters:** line coverage is collected weekly and on manual dispatch
513
+ by `coverage.yml`, as one merged LCOV artifact assembled from three unit shards.
514
+ Locally, `bun run test:coverage` collects the same diagnostic. The required
515
+ `bun run test` suite runs every assertion without coverage instrumentation.
516
+ Coverage is a weak adequacy signal — coverage is not strongly correlated with fault detection
260
517
  once suite size is controlled (Inozemtseva & Holmes, ICSE 2014), whereas
261
518
  mutant detection *is* correlated with real-fault detection (Just et al.,
262
519
  FSE 2014). That supports focused mutation as one useful instrument, not mutation
@@ -276,14 +533,24 @@ This is where we approximate aesthetics deterministically:
276
533
  - **layout rubric / visual-rubric** — hard violations (must be 0) plus soft
277
534
  thresholds (crossings, bends, port-anchored rate).
278
535
  - **heuristic-tracker** — baseline-comparison of routing metrics with
279
- improvement/regression deltas, now gated per PR (`heuristic-tracker.test.ts`):
280
- hard violations must stay 0 and no tracked example may regress on a soft
281
- metric without a reviewed `baseline.json` update in the same change.
536
+ improvement/regression deltas. `heuristic-tracker.test.ts` gates the
537
+ portable part per PR: every tracked example scores without error and hard
538
+ violations stay 0. The soft-metric comparison (bends, crossings, straight
539
+ counts) is not gated, because those integers come from floating-point ELK
540
+ geometry that differs across machines; run
541
+ `bun run eval/heuristic-tracker/run.ts` to see soft deltas when tuning
542
+ routing.
282
543
  - **route-contract tripwires** — `ROUTE_*` codes that must stay 0; any hit
283
544
  means the layout pipeline regressed, not the diagram.
284
-
285
- **Runs:** `measureQuality`, ugly-detector, the layout rubric, and the
286
- heuristic-tracker ratchet all gate per PR.
545
+ - **chart-honesty pixel oracle** (`chart-honesty-text-*.test.ts`,
546
+ `property-chart-honesty.test.ts`) — every text of every family, in every
547
+ registered style, rasterized by resvg: WCAG contrast against the pixels
548
+ around its glyphs, glyphs on the canvas, authored text drawn or reported by
549
+ `verify`, and the same reading in every style
550
+ ([`design/system/chart-honesty.md`](design/system/chart-honesty.md)).
551
+
552
+ **Runs:** `measureQuality`, ugly-detector, the layout rubric, the
553
+ heuristic-tracker ratchet, and the chart-honesty oracle all gate per PR.
287
554
  **Bound provenance (Move 6):** each `QualityBounds` band now carries an
288
555
  explicit basis — `edgeCrossings` is `evidence` (the one aesthetic with strong
289
556
  human-subject support: Purchase 1997/2002), `labelLegibility` is `derived`
@@ -306,21 +573,15 @@ match a human designer's eye; they catch the worst regressions"
306
573
  layout/rendering changes (`contributing/visual-review-evidence.md`).
307
574
 
308
575
  **Runs:** the real LLM judge is **periodic / pre-release only** — model
309
- spend plus nondeterminism make it unfit for a per-PR gate. In CI it is
310
- replaced by a deterministic mock.
311
- **Protocol hardening (Move 1):** the readability/aesthetics axes of the CI
312
- mock are still derived from the perceptual metrics (it is a wiring stub), but
313
- the **faithfulness** axis now comes from `independentFaithfulness()` — a
576
+ spend plus nondeterminism make it unfit for a per-PR gate, and it has no CI
577
+ stand-in. Its **faithfulness** axis comes from `independentFaithfulness()` — a
314
578
  structural parse → serialize → re-parse count check that does *not* consult
315
- `measureQuality`, removing the circularity on that axis. The real judge has
579
+ `measureQuality`. The judge has
316
580
  primitives for the documented LLM-judge biases (Zheng et al., NeurIPS 2023):
317
581
  `judgePairwiseDebiased` scores both orders and trusts only an agreeing verdict
318
582
  (position bias), `assertJudgeIndependence` refuses a judge from the same model
319
583
  family that authored the diagram (self-enhancement), and `JudgeReference`
320
584
  threads a golden anchor (reference-guided scoring).
321
- **Known limitation:** the mock's readability/aesthetics axes remain
322
- metric-derived, so they cannot independently validate those metrics — only a
323
- real judge run can.
324
585
 
325
586
  ## What runs where
326
587
 
@@ -331,16 +592,228 @@ table here, which would drift. In broad strokes:
331
592
  Tier-1 verify, goldens, the differential + faithfulness + metamorphic gates,
332
593
  `measureQuality`/whole-corpus ugly-detector/layout-rubric, the heuristic-tracker ratchet,
333
594
  the corpus/seqbench/upstream benches — plus the high/critical dependency audit,
334
- type check, the hero check, the
335
- golden-drift gate, the parallel browser/CLI/binary/fuzz e2e matrix, the fast
336
- incremental mutation lane, and the independent focused sabotage lane. A final
337
- `CI complete` job waits for the required test aggregate, every E2E matrix job,
338
- and mutation, providing one protectable result that cannot turn green early.
339
- - **Manual / periodic:** opt-in broad Stryker survivor harvests,
340
- `layout-compare` before/after, the benchmark vs competitors, and the real
341
- LLM-as-judge run.
342
-
343
- ## Boundary-contract matrix — lessons applied from the 2026-07 agent audit
595
+ type check, the hero check, the parallel browser/CLI/binary/fuzz e2e matrix (whose
596
+ browser lane checks route payload budgets and, on pull requests, the gzip
597
+ delta against the base branch built on the same runner), and the
598
+ red → green changed-test check. Each unit shard runs its files with
599
+ `--parallel=2` (isolated worker processes), so a test file cannot depend on
600
+ another file's imports. CI prepares the shared website fixture in a separate
601
+ two-minute step before starting those workers, so parallel consumers reuse
602
+ a completed build instead of competing to generate it. A final `CI complete` job waits for the required
603
+ test aggregate, every E2E matrix job and red → green, providing one
604
+ protectable result that cannot turn green early.
605
+ - **Weekly (`contract-discovery.yml`):** one bounded model/grammar discovery job;
606
+ findings remain replay artifacts for human triage, without automated issues.
607
+ - **Per deploy (`deploy-cloudflare.yml`, release to-do):** when the deploy
608
+ skips because `package.json` names a version that is not on npm, it opens
609
+ one `release-todo` issue for that version, and a verified deploy of the
610
+ version closes it (`scripts/ci/release-todo.ts`, tested with a fake `gh` in
611
+ `release-todo.test.ts`). It adds no schedule and never fails a run. **Does
612
+ not prove:** what production serves; it only reports the skip.
613
+ - **Local performance campaigns:** benchmark suites are excluded from CI,
614
+ scheduled workflows, hosted manual Actions and publication dependencies.
615
+ `bun run bench:release` retains the matched Node corpus, scaling, agent edit,
616
+ allocation and memory diagnostics; `bun run bench:release:browser` compares
617
+ the exact packed candidate with pinned browser peers. Run them on an owned
618
+ local host and retain candidate/baseline identities and raw results. Controller
619
+ tests live in [Trident](https://github.com/adewale/trident) and run through `bun run bench:test`.
620
+ Normal product correctness, distribution and pinned-Mermaid semantic tests
621
+ remain CI gates. See [Trident](https://github.com/adewale/trident/blob/main/README.md), its
622
+ [unified runner](https://github.com/adewale/trident/blob/main/unified/README.md) and [the launcher](../eval/trident/README.md) for invocation coverage,
623
+ project updates and explicit gaps.
624
+ - **Normal local benchmarks:** `bun run bench` runs the bounded unified smoke
625
+ profile; `bun run bench:all` selects the full maintained catalog.
626
+ `bun run bench:samples` runs the website SVG/terminal samples;
627
+ `bun run bench:dave` runs just the full browser comparison (three rounds,
628
+ the same first 30 warm-up attempts, all 1,095 pinned fence occurrences
629
+ including duplicates and unsupported families). It builds and packs the
630
+ checkout, then reuses the publication scorecard against pinned Pele 1.9.0
631
+ and Mermaid 12.2.0. Chromium and initial npm access are required;
632
+ `--executable /path/to/chromium`, `--out DIR`, `--rounds N` and
633
+ `--case occurrence-id,...` are forwarded to the browser runner. A case
634
+ filter is a partial diagnostic, not a full-corpus result. Results go to
635
+ gitignored `bench-report/dave/`; competitor ratios remain advisory.
636
+ The local release browser scorecard retains its full-corpus warm-up by
637
+ default; `--warmup 30` selects Dave's
638
+ warm-up policy. Both disable AM's ELK result cache before loading the
639
+ library, so duplicate sources measure layout rather than memo lookup.
640
+ Timing is source through SVG return, not visual/semantic fidelity or
641
+ paint. The post does not specify its warm-up selection; choosing the
642
+ first 30 is an explicit adaptation. Generated reports are not committed.
643
+ To include Bezel's Rust renderer, install Rust's `wasm32-unknown-unknown`
644
+ target and `wasm-bindgen-cli` 0.2.104, then run:
645
+
646
+ ```sh
647
+ (cd ../trident && bun run bezel-wasm/build.ts --checkout ../bezel --out /tmp/bezel-bench)
648
+ bun run bench:dave --bezel-dir /tmp/bezel-bench --executable /path/to/chromium
649
+ ```
650
+
651
+ The adapter compiles an unmodified, clean Bezel checkout at `opt-level=3`
652
+ with LTO. Its manifest records the revision, compiler, dependency lock and
653
+ wrapper/WASM byte identities. Instantiation is outside the render timer;
654
+ timed calls use `render(source, &Options::default(), &Metrics).svg`. Reported
655
+ bundle size includes the WASM embedded in its JS wrapper. This measures
656
+ standalone browser rendering, not Bezel's GPUI gallery or native execution.
657
+ WASM memory is outside the separate V8 JS-memory diagnostic.
658
+ `bun run bench --profile full --suites editor-preview` complements that
659
+ render-only score with actual editor input through verified DOM and two
660
+ animation frames. It changes the root label in 5/50/150-node trees, keeps
661
+ default cache behavior and adaptive debounce, and records a 5 ms main-thread
662
+ heartbeat. Run baseline and candidate builds sequentially on an otherwise
663
+ idle runner. These targeted UI samples are separate from the full corpus.
664
+ The standalone editor embeds one module used by both the main thread and
665
+ its Blob worker; it does not embed separate copies of the rendering engine.
666
+ The real-browser standalone contract also caps the complete generated HTML
667
+ at 4,100,000 raw / 1,264,000 gzip bytes and checks worker crash recovery,
668
+ text output and the actual Canvas PNG download. The hosted editor retains
669
+ its separate, lazy worker asset and existing route payload budgets.
670
+
671
+ Two optional investigations expand these diagnostics without adding timing
672
+ gates:
673
+
674
+ ```sh
675
+ # AGENTIC_ROOT contains package.json and the actual dist/browser.global.js.
676
+ bun run bench --profile full --suites renderer-profile --package-root /path/to/extracted/package --bezel-dir /tmp/bezel-bench
677
+ bun run bench --profile full --suites editor-session
678
+ ```
679
+
680
+ The renderer investigation records family-level SVG acceptance, differential
681
+ inputs/errors and paired timing over the complete occurrence inventory. Its
682
+ separate source-build pass attributes stages over distinct sources after
683
+ warmup, both through SVG return and fully consumed receipts; layout includes
684
+ ELK, so those stage totals must not be added together. Chrome CPU profiles
685
+ cover the slowest shared-success example in eight families. Source profiles
686
+ retain function names; only the packaged pass establishes competitor ratios.
687
+ `--timing-report /path/to/renderer-profile.json` can reuse a completed timing
688
+ pass with identical artifact hashes/rounds and a complete adjacent
689
+ `samples.json`; it still reruns stage attribution and profiling.
690
+
691
+ The editor session runner covers trees, nested crosslinks, sequence, class,
692
+ ER, state, mindmap, Gantt and pie. Defaults are five fresh browser contexts,
693
+ 20 warm edits, 60 subsequent edits and a 30-edit burst requesting 80 ms intervals per
694
+ case. It records median/p95 latency, heartbeat gaps, receipt invalidation,
695
+ visible final labels, worker concurrency and forced-GC JS heaps in both
696
+ realms. Fresh contexts do not clear browser-process or OS caches; localhost
697
+ transfer is uncompressed and unshaped. Heap figures exclude native/DOM/Blob
698
+ backing allocations and process RSS. Use `--case tree-150` for longer focused
699
+ sessions, `--memory-every 100` for periodic retained-heap checkpoints outside
700
+ timed edits, and `--entry /original.html` for standalone builds served in a site
701
+ directory alongside its `capabilities.json` and fonts. Baseline/candidate
702
+ runs must remain sequential on an otherwise idle runner.
703
+
704
+ The editor's persistent worker owns preview/verification, ASCII/Unicode and
705
+ PNG projection/validation. A PNG operation invokes the existing main-thread
706
+ font/Canvas callback and transfers an allocation-bounded copy of its bytes
707
+ back for canonical PNG validation. Callback buffers are never detached.
708
+ One operation is active, with one replaceable preview and four queued exports;
709
+ source/config changes cancel stale work, including a PNG awaiting font/Canvas
710
+ I/O, so a new preview can proceed. Timeouts and crashes reject all waiters and
711
+ the next operation starts a fresh worker.
712
+
713
+ `editor-ownership-bench.ts --site /path/to/site --out bench-report/ownership
714
+ --executable /path/to/chromium` compares frozen `original.html` and current
715
+ `candidate.html` in alternating fresh contexts. Serve their fonts alongside
716
+ them. It separately measures the empty shell, first preview, ASCII/Unicode
717
+ and a real Canvas PNG, with heap checkpoints before/after preview and exports.
718
+ `--case tree-5,tree-50,tree-150,nested-crosslinks,sequence-40,class-20,er-20,state-25,mindmap-30,gantt-20,pie-12`
719
+ expands it to the full editor portfolio; `--style hand-drawn` measures a seeded
720
+ alternate backend. The runner attempts every format even after a render
721
+ rejection, retains incremental samples and reports per-format acceptance.
722
+ Successful SVG/ASCII/Unicode/PNG bytes and request identities must match
723
+ across variants and repetitions; rejected outputs must have matching errors.
724
+ Report rejected attempts separately from successful-operation latency.
725
+ - **Manual / periodic:** `layout-compare` before/after, the benchmark vs competitors,
726
+ `bun run bench:micro` (per-stage hotspot timings, allocation per call under
727
+ `--runtime node`, and `--baseline <ref>` for interleaved revision ratios;
728
+ advisory, never a gate). Filter `^source-edit:` for opaque Class/Flowchart
729
+ edits at 32, 256 and 2,048 nodes, including ten-edit chains and exact label
730
+ no-ops. These start from a warm parsed input and time complete result JSON
731
+ materialization; they exclude verification, layout and rendering. Reports
732
+ retain source/output byte counts and growth. Node allocation estimates cover
733
+ V8, not native memory or peak RSS. Compare with `--baseline <source-edit-ref>`
734
+ and calibrate timing noise with `--aa`; an unavailable baseline API is not a
735
+ fast successful edit. `bun run bench:peers` (the built package against
736
+ the pinned browserless and terminal peers in `eval/fork-graph/`, with output checks outside timing/allocation and label
737
+ checks before timing; includes the native Go Sketch worker, requiring Go >=1.25).
738
+ `bench:peers --corpus` attempts every official fence occurrence, including
739
+ unsupported families, with one warmup and three single-call samples per
740
+ successful input per round. This mode checks output presence rather than
741
+ labels or semantic/visual fidelity; the ordinary calibrated workloads keep
742
+ their label assertions. `bun run check:output-identity --baseline <ref>`
743
+ (byte identity of the distinct corpus inputs across SVG backends, SVG
744
+ post-passes, ASCII, Unicode and verify, for refactors that must not change
745
+ output), the real LLM-as-judge run, and the production freshness check
746
+ (`scripts/ci/production-freshness.ts`, in `docs/contributing/releasing.md`).
747
+ That check compares the build agentic-mermaid.dev serves with the `main`
748
+ commit it is given (the tip of `main`, in the documented command) and fails
749
+ once `main` has held undeployed commits for more than three days. A deploy run proves only the deploys that
750
+ ran; this proves deploys are still reaching production. **Does not prove:**
751
+ that the served build works (the deploy's candidate probes do that).
752
+
753
+ ## Contract review and completion
754
+
755
+ Before implementing a cross-surface change, put a short contract/evidence table
756
+ in the PR description. For a small change, use a sentence. Each contract states
757
+ observable behavior, its owning producer and consumers, and what could falsify
758
+ it. Choose expectations from public behavior or independent source evidence
759
+ before examining existing assertions. Reuse existing tests and probes; this is
760
+ a review method, not another generated manifest or test-presence gate.
761
+
762
+ Preserve distinctions until the component that owns the decision can resolve
763
+ them. Examples from PR #470 show why this matters:
764
+
765
+ | Contract | Distinction to preserve | Evidence at the owning boundary |
766
+ | --- | --- | --- |
767
+ | Worker recovery | An obsolete operation can still report a broken worker; deferred loading differs from an optional rendering failure | Cancel an export while its import fails, then exercise the queued preview and first retry; compare cold and warm receipts. |
768
+ | Deployment recovery | A temporary network failure differs from an old document requesting retired asset URLs | Serve two actual asset generations and preserve the latest draft/settings across explicit reload, including storage/encoding failure. |
769
+ | Benchmark identity | A rendered SVG or equal input count does not establish the requested theme or measured source | Assert active/restored theme identity and exact input/module/config identity; reject invalid resume before overwriting evidence. |
770
+ | Repository matching | Missing metadata differs from explicit unsupported, malformed or conflicting metadata | Enter through raw repository normalization and matching together; include URI ports and scp-style addresses. |
771
+ | Responsibility categories | A known member and valid labels do not establish complete source review | Cite pinned public APIs/authoring flows for assigned roles; leave unreviewed responsibilities explicit. |
772
+
773
+ Review purposeful transitions and interacting boundaries, rather than every
774
+ combination. Where the change applies, consider cold/warm state, cancellation
775
+ and failure ordering, representation changes, multiple consumers/platforms and
776
+ deployment generations. State exclusions instead of treating untested cases as
777
+ passed. Tests should still protect one contract at its owning boundary; do not
778
+ combine unrelated UI, persistence and export contracts into one test.
779
+
780
+ For each confirmed finding, identify the failed assumption and trace it from
781
+ raw input through all affected consumers. Distinguish a pre-existing defect
782
+ from one introduced by a repair. Review adjacent paths that share that
783
+ assumption, extend an existing behavioral test where possible, and demonstrate
784
+ red → green for the bug. If only the example is fixed, keep the broader gap
785
+ open. If exhaustive review is infeasible, narrow the claim and state the gap.
786
+
787
+ When multiple reviewers are requested, assign independently falsifiable
788
+ hypotheses and explicit ownership of interactions, not just folders. One review
789
+ must connect the changed producers to their consumers. Ask reviewers to derive
790
+ expected behavior before reading existing tests; permit no finding and require
791
+ a reproduction for actionable defects. The change author reconciles findings
792
+ against the combined candidate. A severity label or number of agents does not
793
+ replace that reconciliation.
794
+
795
+ Completion requires resolved confirmed in-scope defects, evidence for the
796
+ declared contracts, disclosed remaining limitations, and successful required CI
797
+ for the exact final head. Record the base/head and dependency revision, link
798
+ the CI run, wait for terminal results, then recheck the current base and PR head.
799
+ Read the base branch's live remote ref: the PR's recorded base commit and the
800
+ workflow event's base SHA can be stale. Inspect the actual CI checkout and its
801
+ parents to confirm which base/head combination ran; the workflow run's head SHA
802
+ alone does not identify its merge checkout.
803
+ After a push, follow the new run. A base update requires a fresh check of the
804
+ combined candidate; after code/dependency changes, requalify affected contracts.
805
+ Retained measurements may apply when their shipped/input bytes are unchanged;
806
+ keep their original revision, date and limits. Never report a pending run as
807
+ ready. The hygiene script reports heuristics, not this completion decision.
808
+
809
+ Repeat an audit to answer a named unresolved question or examine a material
810
+ change to a contract. Repeating the same prompt until no reviewer finds
811
+ anything is not a stopping rule. New discoveries can reopen an affected
812
+ contract; a clean audit alone neither proves completeness nor invalidates
813
+ sound evidence for other contracts. Do not create more gates or approval
814
+ artifacts merely to certify that the review procedure was followed.
815
+
816
+ ### Boundary-contract matrix — lessons applied from the 2026-07 agent audit
344
817
 
345
818
  The PR #162 audit exposed a blind spot that family/layout coverage did not
346
819
  address: a contract could be correct in one helper and false at another public
@@ -373,6 +846,90 @@ lowest honest tier and one shipped-boundary proof where packaging, transport,
373
846
  or generation can change the result. A test that silently returns because its
374
847
  environment is unavailable is not evidence for a CI-required capability.
375
848
 
849
+ ## What makes a good test
850
+
851
+ The sections above say which oracles the suite uses. This one is the standard
852
+ each test is held to. A good test catches a real regression, for the right
853
+ reason, cheaply, and says clearly what broke.
854
+
855
+ 1. **It can fail.** Breaking the behaviour it names turns it red. Check this
856
+ by mutating the code under test, not by reading the assertion. The common
857
+ failures are an assertion something else always satisfies (`toContain('v')`
858
+ when a label contains a `v`; a marker id that every SVG defines in
859
+ `<defs>`), and a fixture that never reaches the code (suppressing a warning
860
+ the input never raises).
861
+ 2. **It tests behaviour through a stable seam**: the API, CLI, MCP tool,
862
+ rendered output or a documented contract. It does not grep source text,
863
+ compare `Function.prototype.toString()`, or pin private names.
864
+ 3. **Its expectation comes from outside the code**: a specification, pinned
865
+ upstream Mermaid, a metamorphic relation, a property, or a reviewed golden
866
+ whose meaning is stated. A value harvested from our own output and pasted
867
+ back is not an oracle. The upstream bench's self-harvested labels once
868
+ pinned a wrong ellipse label as correct.
869
+ 4. **It has one reason to fail and a readable failure.** The name states the
870
+ behaviour; the assertion is object-shaped and carries the case, not a bare
871
+ `toBe(true)` over a conjunction.
872
+ 5. **It is deterministic and hermetic.** Pinned seeds, no wall clock, no
873
+ sleeps, no network, no dependence on file order, no leaked temp files or
874
+ environment variables.
875
+ 6. **It asserts something it did not set.** No assertion on a value the test
876
+ just assigned, on a mock's configured return, or on a `Record` completeness
877
+ that `tsc` already enforces. An early `return` or an `if (x) expect(…)`
878
+ that can skip every assertion is a missing assertion: assert the
879
+ precondition, or use `test.skipIf` with a reason. A property whose success
880
+ branch can go unexercised counts how often it ran and fails at zero.
881
+ 7. **It is not redundant**, and it does not make a doc or a second list copy
882
+ data the code owns; iterate the registry instead of a hand list of
883
+ families.
884
+ 8. **Its label is honest.** A test that pins a known bug says so and names
885
+ the `TODO.md` entry. A regression guard that cannot discriminate says so.
886
+ 9. **Its cost is proportionate.** Expensive work (renders, layouts, PNG
887
+ rasterization, a website build) is done once per file and shared.
888
+ 10. **It reads as arrange, act, assert**, with many cases as a table.
889
+
890
+ Shared helpers carry the common arrangements: `src/__tests__/helpers/temp-dir.ts`
891
+ for temporary directories that are removed after the file,
892
+ `src/__tests__/helpers/cli-capture.ts` for in-process CLI output, and
893
+ `src/__tests__/helpers/complexity.ts` for growth checks in place of wall-clock
894
+ ceilings.
895
+
896
+ A test protects observable behaviour or an independent public contract
897
+ (API, package contents, security, protocol, config, shipped bytes, and the
898
+ agent-facing runtime docs: `llms.txt`, CLI help, MCP instructions,
899
+ `am --agent-instructions`). There are no tests of tests: no lint over test
900
+ source, no grep over production source, no check that a doc's prose or a
901
+ second list matches the code, and no test of an eval or CI helper for its own
902
+ sake. Determinism is proven on output (`agent-determinism.test.ts`,
903
+ `ascii-determinism.test.ts`), not by banning tokens in source.
904
+
905
+ Files worth copying: `src/__tests__/property-upstream-flowchart.test.ts`
906
+ (grammar-generated differential against pinned upstream),
907
+ `src/__tests__/property-er-model.test.ts` (model-based test with a shadow
908
+ model), `src/__tests__/release-publish-steps.test.ts` (runs workflow shell
909
+ steps with stub tools), `src/__tests__/theme-color-admission.test.ts`
910
+ (table-driven across output routes), and
911
+ `src/__tests__/property-validator-surface-fuzz.test.ts` (the non-vacuity
912
+ counter).
913
+
914
+ `section-a-family-descriptor-conformance.test.ts` exercises built-in layout,
915
+ Scene lowering and terminal hooks through public operations, then inspects emitted
916
+ Scene roles, primitives and marker resources. Public dispatch and authored meaning
917
+ are checked by `characterization-families.test.ts` and
918
+ `diagram-family-citizenship.test.ts`. These executable witnesses also protect the
919
+ shared style, security and certification integration boundary.
920
+ Layout pass tests exercise production enforcement and resulting geometry; a
921
+ particular pass name or decomposition is not a rendering contract.
922
+
923
+ Call counts need an observable reason. Retain them for work admission (for
924
+ example, refusing oversized input before ELK or text measurement), deferred
925
+ optional graphical work, lazy terminal certification, and the single
926
+ positioned-artifact extension protocol.
927
+ Do not require an internal serializer or evidence enumerator to run exactly once
928
+ when the output, ownership and certificate are the actual contract. Default
929
+ pathfinder tie order and sequence spacing constants are likewise implementation
930
+ choices; test repeatability, valid routes and painted clearance, and use reviewed
931
+ goldens for intentional appearance baselines.
932
+
376
933
  ## Why the suite is shaped this way
377
934
 
378
935
  - **Cheap-and-deterministic gates per PR; expensive-and-noisy gates
@@ -396,23 +953,24 @@ These are real today and are the natural targets for *enhancing* existing
396
953
  gates rather than adding new machinery:
397
954
 
398
955
  1. The per-PR aesthetic signal is still the weakest gate; the metrics are
399
- admittedly rough. The CI LLM mock's readability/aesthetics axes remain
400
- metric-derived (only its faithfulness axis is now independent), so it
401
- cannot validate those metrics — only a real periodic judge run can.
402
- 2. Automatic mutation assurance is deliberately narrow: PR feedback uses the
403
- fast incremental faithfulness-counter lane plus five focused sabotage probes.
404
- Broad mutation configs are manual diagnostics because the retired scheduled
405
- matrix did not justify its runner and maintenance cost. Line coverage is
406
- framed as a finder, not a headline score.
407
- 3. The benchmark is not on the PR gate (timing variance). Browser/screenshot
408
- e2e and the heuristic-tracker ratchet now are.
956
+ admittedly rough, and nothing per-PR validates them independently — only a
957
+ real periodic judge run can.
958
+ 2. There is no automatic mutation lane in the current PR workflow. Red → green
959
+ proves an assertion in a changed test file discriminates the base; it does not
960
+ prove every changed behavior is protected. Focused isolated faults can audit
961
+ weak oracles locally. The diff-scoped mutation proposal #355 was closed
962
+ `not_planned`; it is not an active implementation commitment. Line coverage
963
+ remains a finder, not an adequacy score.
964
+ 3. Performance comparisons are local campaigns and do not run in CI or gate
965
+ publication. Their recorded results require an explicit update. Product
966
+ browser/screenshot e2e and the heuristic-tracker ratchet remain on the PR gate.
409
967
  4. Determinism is proven Bun↔Node on one architecture; cross-architecture
410
968
  byte equality is not asserted.
411
969
  5. `QualityBounds` thresholds are now provenance-tagged, but the `chosen`
412
970
  bands are still not validated against human-perception evidence.
413
- 6. Golden movement still needs human judgment, but the snapshot drift sentinel
414
- blocks unless the change is committed with the explicit `[approve-goldens]`
415
- review token; the token proves acknowledgment, not visual quality.
971
+ 6. Golden movement needs human judgment: each PR description lists every changed
972
+ golden and why it changed. Passing snapshot tests proves consistency with the
973
+ committed baseline, not that a changed baseline improves visual quality.
416
974
 
417
975
  The deepest gap is structural, and `project/lessons-learned.md` (Loop 13)
418
976
  names it: every quality signal here is self-generated. The portfolio above