@legionworks/facet 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/LICENSE-MIT +23 -0
  3. package/README.md +158 -0
  4. package/dist/gallery/chunk-4g8rem85.css +656 -0
  5. package/dist/gallery/chunk-c6s9k9ty.js +5970 -0
  6. package/dist/gallery/frame/artifact.css +8424 -0
  7. package/dist/gallery/frame/chunks/abnfDiagram-N423BO3Z-jj74szbm.js +155 -0
  8. package/dist/gallery/frame/chunks/architecture-TIHT7OUA-cvdjn7dt.js +10 -0
  9. package/dist/gallery/frame/chunks/architectureDiagram-T3A2C74G-n4z77eg6.js +8675 -0
  10. package/dist/gallery/frame/chunks/blockDiagram-VBNYF7ZC-7xsqzrd1.js +3529 -0
  11. package/dist/gallery/frame/chunks/c4Diagram-5PPSVZJV-z2zjs7wx.js +2413 -0
  12. package/dist/gallery/frame/chunks/classDiagram-JCYQIIEL-ve8rngqp.js +47 -0
  13. package/dist/gallery/frame/chunks/classDiagram-v2-OCEON4UE-8cfaxb8z.js +47 -0
  14. package/dist/gallery/frame/chunks/cose-bilkent-JH36ORCC-sn92qexx.js +4822 -0
  15. package/dist/gallery/frame/chunks/cynefin-VYW2F7L2-tqjv913d.js +10 -0
  16. package/dist/gallery/frame/chunks/cynefinDiagram-MW4NZA55-c1c0dj12.js +500 -0
  17. package/dist/gallery/frame/chunks/dagre-VZM6K2ZE-7tyg4yt2.js +476 -0
  18. package/dist/gallery/frame/chunks/diagram-7IWD3JNH-y4eng9sc.js +526 -0
  19. package/dist/gallery/frame/chunks/diagram-B4RE2ZJO-rn3p7j5h.js +673 -0
  20. package/dist/gallery/frame/chunks/diagram-LBJQPF4R-w4a2cm9q.js +255 -0
  21. package/dist/gallery/frame/chunks/diagram-Q27KOJAE-aqc2v7wy.js +575 -0
  22. package/dist/gallery/frame/chunks/diagram-UB23O5K3-xncy8nyd.js +355 -0
  23. package/dist/gallery/frame/chunks/ebnfDiagram-BXEA7PRR-acwgxz16.js +175 -0
  24. package/dist/gallery/frame/chunks/erDiagram-JOGREHBK-mzp5nswt.js +1332 -0
  25. package/dist/gallery/frame/chunks/eventmodeling-45OFAUF4-sg07qr0c.js +10 -0
  26. package/dist/gallery/frame/chunks/flowDiagram-UKHOOZJN-2e5ygs44.js +29 -0
  27. package/dist/gallery/frame/chunks/ganttDiagram-PKOTCBZU-p2hhh7hr.js +2620 -0
  28. package/dist/gallery/frame/chunks/gitGraph-TEB2WS4Q-j6kw0naa.js +10 -0
  29. package/dist/gallery/frame/chunks/gitGraphDiagram-DS77QQ5N-4z9z8wjm.js +1347 -0
  30. package/dist/gallery/frame/chunks/info-DKCQHKI2-mpt80jd3.js +10 -0
  31. package/dist/gallery/frame/chunks/infoDiagram-6WML65LV-5rqm3mgg.js +65 -0
  32. package/dist/gallery/frame/chunks/ishikawaDiagram-WSZJBQD7-b51z96jy.js +975 -0
  33. package/dist/gallery/frame/chunks/journeyDiagram-NVQOT4AX-9jbfp0pk.js +1272 -0
  34. package/dist/gallery/frame/chunks/kanban-definition-27J2QSJJ-4x0vx4se.js +1116 -0
  35. package/dist/gallery/frame/chunks/katex-xfgk95tv.js +14034 -0
  36. package/dist/gallery/frame/chunks/markdown-1s5vwkf2.js +9 -0
  37. package/dist/gallery/frame/chunks/markdown-2h6bwsms.js +3239 -0
  38. package/dist/gallery/frame/chunks/markdown-2x113yh8.js +107 -0
  39. package/dist/gallery/frame/chunks/markdown-30b7rwmp.js +1943 -0
  40. package/dist/gallery/frame/chunks/markdown-356bfj4g.js +2031 -0
  41. package/dist/gallery/frame/chunks/markdown-44n9ea69.js +2406 -0
  42. package/dist/gallery/frame/chunks/markdown-56enrf2r.js +39 -0
  43. package/dist/gallery/frame/chunks/markdown-5avymm8t.js +725 -0
  44. package/dist/gallery/frame/chunks/markdown-6r4svars.js +33 -0
  45. package/dist/gallery/frame/chunks/markdown-6zz8efx2.js +24 -0
  46. package/dist/gallery/frame/chunks/markdown-7jp6prnb.js +19 -0
  47. package/dist/gallery/frame/chunks/markdown-809k7q41.js +55 -0
  48. package/dist/gallery/frame/chunks/markdown-82xmzhzd.js +1076 -0
  49. package/dist/gallery/frame/chunks/markdown-92n37gd4.js +7627 -0
  50. package/dist/gallery/frame/chunks/markdown-ag2sssf8.js +36 -0
  51. package/dist/gallery/frame/chunks/markdown-b4s7ntq2.js +19 -0
  52. package/dist/gallery/frame/chunks/markdown-c9k0fvp1.js +100 -0
  53. package/dist/gallery/frame/chunks/markdown-ceefymn8.js +125 -0
  54. package/dist/gallery/frame/chunks/markdown-d1wfbs70.js +243 -0
  55. package/dist/gallery/frame/chunks/markdown-dy5sgjhc.js +11022 -0
  56. package/dist/gallery/frame/chunks/markdown-dz3z5w30.js +30868 -0
  57. package/dist/gallery/frame/chunks/markdown-eg6ngz7y.js +30340 -0
  58. package/dist/gallery/frame/chunks/markdown-ff5wvb43.js +1149 -0
  59. package/dist/gallery/frame/chunks/markdown-ffg06nfz.js +86 -0
  60. package/dist/gallery/frame/chunks/markdown-fvfncadf.js +472 -0
  61. package/dist/gallery/frame/chunks/markdown-gvyszqpv.js +19 -0
  62. package/dist/gallery/frame/chunks/markdown-gx1p0ew0.js +88 -0
  63. package/dist/gallery/frame/chunks/markdown-gxye5frz.js +65 -0
  64. package/dist/gallery/frame/chunks/markdown-h7pzv34e.js +105 -0
  65. package/dist/gallery/frame/chunks/markdown-hab789t9.js +47 -0
  66. package/dist/gallery/frame/chunks/markdown-hjp6sj8t.js +125 -0
  67. package/dist/gallery/frame/chunks/markdown-jka64qgp.js +1993 -0
  68. package/dist/gallery/frame/chunks/markdown-k8xkn8mw.js +56 -0
  69. package/dist/gallery/frame/chunks/markdown-m7frq8x8.js +112 -0
  70. package/dist/gallery/frame/chunks/markdown-p740v4qm.js +36 -0
  71. package/dist/gallery/frame/chunks/markdown-pxfeh231.js +57 -0
  72. package/dist/gallery/frame/chunks/markdown-pzgd8z3s.js +87 -0
  73. package/dist/gallery/frame/chunks/markdown-qx3cngz2.js +1673 -0
  74. package/dist/gallery/frame/chunks/markdown-rawhfvvc.js +85 -0
  75. package/dist/gallery/frame/chunks/markdown-rd8w0ng9.js +49 -0
  76. package/dist/gallery/frame/chunks/markdown-rm2yxfjj.js +2773 -0
  77. package/dist/gallery/frame/chunks/markdown-s1jatrnk.js +993 -0
  78. package/dist/gallery/frame/chunks/markdown-sgfg5vvq.js +59 -0
  79. package/dist/gallery/frame/chunks/markdown-t4ethv9d.js +409 -0
  80. package/dist/gallery/frame/chunks/markdown-tnmabqvz.js +36 -0
  81. package/dist/gallery/frame/chunks/markdown-tvdpppw8.js +5292 -0
  82. package/dist/gallery/frame/chunks/markdown-v0x27twr.js +22 -0
  83. package/dist/gallery/frame/chunks/markdown-v14td68x.js +36 -0
  84. package/dist/gallery/frame/chunks/markdown-w6sgd54z.js +36 -0
  85. package/dist/gallery/frame/chunks/markdown-xw8dsk8b.js +2125 -0
  86. package/dist/gallery/frame/chunks/markdown-y18mve73.js +85 -0
  87. package/dist/gallery/frame/chunks/markdown-yengsms5.js +427 -0
  88. package/dist/gallery/frame/chunks/markdown-z1wc0v0d.js +1826 -0
  89. package/dist/gallery/frame/chunks/markdown-zazx9h4n.js +329 -0
  90. package/dist/gallery/frame/chunks/mermaid-de7p4v0x.js +30 -0
  91. package/dist/gallery/frame/chunks/mindmap-definition-FAOFIHXS-ra44cbem.js +1216 -0
  92. package/dist/gallery/frame/chunks/packet-7NZHBO7P-xppm5913.js +10 -0
  93. package/dist/gallery/frame/chunks/pegDiagram-VL7TDLO6-a2a4wg03.js +163 -0
  94. package/dist/gallery/frame/chunks/pie-RZYD4A2V-hnbxk152.js +10 -0
  95. package/dist/gallery/frame/chunks/pieDiagram-7S7Q4E2Y-f1vxjtmx.js +311 -0
  96. package/dist/gallery/frame/chunks/quadrantDiagram-CIZ2JOQS-ywafa6kh.js +1390 -0
  97. package/dist/gallery/frame/chunks/radar-I7S5WNFK-ewqd27se.js +10 -0
  98. package/dist/gallery/frame/chunks/railroad-3IZDKUUU-5hy4bdx3.js +10 -0
  99. package/dist/gallery/frame/chunks/railroad-abnf-AHOZXSZD-bh5sxz0d.js +10 -0
  100. package/dist/gallery/frame/chunks/railroad-ebnf-EBAXGLYW-6b9c3zeh.js +10 -0
  101. package/dist/gallery/frame/chunks/railroad-peg-LSFZ7HO6-avc8v39s.js +10 -0
  102. package/dist/gallery/frame/chunks/railroadDiagram-AXF67PYL-qc61wax4.js +131 -0
  103. package/dist/gallery/frame/chunks/requirementDiagram-LRYGKXZP-9bs4786c.js +1295 -0
  104. package/dist/gallery/frame/chunks/sankeyDiagram-W5VNT64P-awt1em8b.js +1339 -0
  105. package/dist/gallery/frame/chunks/sequenceDiagram-SI44F4Z6-qaf9mz3t.js +4324 -0
  106. package/dist/gallery/frame/chunks/sizeCapture-X5ZJPWSS-9fq685xd.js +64 -0
  107. package/dist/gallery/frame/chunks/stateDiagram-OKZ733FA-vx7a7hh0.js +456 -0
  108. package/dist/gallery/frame/chunks/stateDiagram-v2-UEYNNEHI-x4m6ws5t.js +46 -0
  109. package/dist/gallery/frame/chunks/swimlanes-SLNWSIFB-e4mxm1gh.js +8287 -0
  110. package/dist/gallery/frame/chunks/swimlanesDiagram-ULZ7WXOC-0h93e2qq.js +42 -0
  111. package/dist/gallery/frame/chunks/timeline-definition-Z64GVDOM-wp0bhbzg.js +1549 -0
  112. package/dist/gallery/frame/chunks/treeView-QDETBFTQ-hcx7n0vt.js +10 -0
  113. package/dist/gallery/frame/chunks/treemap-6X3UGDF4-av02skdw.js +10 -0
  114. package/dist/gallery/frame/chunks/vennDiagram-T6HMQDX7-pmvk1p7n.js +2566 -0
  115. package/dist/gallery/frame/chunks/wardley-OPB4EBWU-njsp6e40.js +10 -0
  116. package/dist/gallery/frame/chunks/wardleyDiagram-T6FBY63Y-eggmnv4z.js +979 -0
  117. package/dist/gallery/frame/chunks/xychartDiagram-ELKLHX3M-q40tnpe5.js +1954 -0
  118. package/dist/gallery/frame/frame.css +183 -0
  119. package/dist/gallery/frame/runtime/chart.js +42744 -0
  120. package/dist/gallery/frame/runtime/html.js +14 -0
  121. package/dist/gallery/frame/runtime/markdown.js +1403 -0
  122. package/dist/gallery/frame/runtime/mermaid.js +32 -0
  123. package/dist/gallery/frame/runtime/svg.js +14 -0
  124. package/dist/gallery/frame/runtime/tsx.js +52 -0
  125. package/dist/gallery/index.html +104 -0
  126. package/docs/reference/cli.md +163 -0
  127. package/docs/reference/export.md +106 -0
  128. package/docs/reference/html.md +215 -0
  129. package/docs/reference/http.md +85 -0
  130. package/docs/reference/mcp.md +78 -0
  131. package/docs/reference/security.md +119 -0
  132. package/docs/reference/storage.md +93 -0
  133. package/docs/reference/tsx.md +101 -0
  134. package/docs/reference/validation.md +212 -0
  135. package/package.json +89 -0
  136. package/scripts/build-gallery.ts +127 -0
  137. package/scripts/launch-netns.sh +67 -0
  138. package/skills/facet/SKILL.md +100 -0
  139. package/src/cli/client.ts +311 -0
  140. package/src/cli/commands/create.ts +49 -0
  141. package/src/cli/commands/doctor.ts +249 -0
  142. package/src/cli/commands/export.ts +179 -0
  143. package/src/cli/commands/instantiate.ts +35 -0
  144. package/src/cli/commands/list.ts +49 -0
  145. package/src/cli/commands/open.ts +75 -0
  146. package/src/cli/commands/pin.ts +39 -0
  147. package/src/cli/commands/promote.ts +49 -0
  148. package/src/cli/commands/publish.ts +122 -0
  149. package/src/cli/commands/read-back.ts +56 -0
  150. package/src/cli/commands/status.ts +114 -0
  151. package/src/cli/commands/watch.ts +146 -0
  152. package/src/cli/main.ts +522 -0
  153. package/src/cli/output.ts +106 -0
  154. package/src/cli/parser.ts +376 -0
  155. package/src/cli/presenter.ts +212 -0
  156. package/src/cli/service-metadata.ts +134 -0
  157. package/src/cli/spawn-service.ts +326 -0
  158. package/src/css.d.ts +4 -0
  159. package/src/gallery-web/app.ts +1581 -0
  160. package/src/gallery-web/export-menu.ts +121 -0
  161. package/src/gallery-web/export.ts +112 -0
  162. package/src/gallery-web/favicon.ts +54 -0
  163. package/src/gallery-web/frame/entries/chart.ts +6 -0
  164. package/src/gallery-web/frame/entries/html.ts +6 -0
  165. package/src/gallery-web/frame/entries/markdown.ts +6 -0
  166. package/src/gallery-web/frame/entries/mermaid.ts +6 -0
  167. package/src/gallery-web/frame/entries/svg.ts +6 -0
  168. package/src/gallery-web/frame/entries/tsx.ts +6 -0
  169. package/src/gallery-web/frame/frame-payload.ts +91 -0
  170. package/src/gallery-web/frame/renderer-validation.ts +13 -0
  171. package/src/gallery-web/frame/renderers/chart.ts +182 -0
  172. package/src/gallery-web/frame/renderers/dompurify-shim.ts +112 -0
  173. package/src/gallery-web/frame/renderers/html.ts +48 -0
  174. package/src/gallery-web/frame/renderers/markdown.ts +81 -0
  175. package/src/gallery-web/frame/renderers/mermaid.ts +120 -0
  176. package/src/gallery-web/frame/renderers/registry.ts +260 -0
  177. package/src/gallery-web/frame/renderers/svg.ts +393 -0
  178. package/src/gallery-web/frame/renderers/tsx.ts +61 -0
  179. package/src/gallery-web/frame/runtime.ts +501 -0
  180. package/src/gallery-web/frame/styles/artifact.css +8424 -0
  181. package/src/gallery-web/frame/styles/frame.css +183 -0
  182. package/src/gallery-web/frame/styles/html-source.css +23 -0
  183. package/src/gallery-web/frame/view-box.ts +33 -0
  184. package/src/gallery-web/frame-html.ts +72 -0
  185. package/src/gallery-web/index.html +104 -0
  186. package/src/gallery-web/session.ts +107 -0
  187. package/src/gallery-web/sse-client.ts +212 -0
  188. package/src/gallery-web/styles/app.css +420 -0
  189. package/src/gallery-web/styles/tokens.css +112 -0
  190. package/src/gallery-web/styles/verdict.css +186 -0
  191. package/src/gallery-web/swap.ts +64 -0
  192. package/src/gallery-web/theme.ts +40 -0
  193. package/src/gallery-web/view-state.ts +93 -0
  194. package/src/harness-adapters/claude-code/README.md +10 -0
  195. package/src/harness-adapters/claude-code/facet.sh +4 -0
  196. package/src/harness-adapters/codex/README.md +10 -0
  197. package/src/harness-adapters/codex/facet.sh +4 -0
  198. package/src/harness-adapters/mcp/README.md +13 -0
  199. package/src/harness-adapters/mcp/cli-bridge.ts +216 -0
  200. package/src/harness-adapters/mcp/main.ts +15 -0
  201. package/src/harness-adapters/mcp/server.ts +164 -0
  202. package/src/harness-adapters/mcp/tool-schemas.ts +53 -0
  203. package/src/harness-adapters/opencode/README.md +10 -0
  204. package/src/harness-adapters/opencode/facet.sh +4 -0
  205. package/src/runtime/compiled-entrypoints.ts +69 -0
  206. package/src/service/dispatcher.ts +717 -0
  207. package/src/service/export.ts +218 -0
  208. package/src/service/http-utils.ts +55 -0
  209. package/src/service/lexical/expectations.ts +210 -0
  210. package/src/service/lifecycle/idle-controller.ts +125 -0
  211. package/src/service/lifecycle/orphan-cleanup.ts +128 -0
  212. package/src/service/lifecycle/process-lock.ts +235 -0
  213. package/src/service/main.ts +136 -0
  214. package/src/service/process.ts +240 -0
  215. package/src/service/router-guards.ts +239 -0
  216. package/src/service/router.ts +627 -0
  217. package/src/service/security/auth.ts +168 -0
  218. package/src/service/security/host-origin.ts +138 -0
  219. package/src/service/security/http-guards.ts +13 -0
  220. package/src/service/security/leases.ts +182 -0
  221. package/src/service/security/token-store.ts +138 -0
  222. package/src/service/server.ts +356 -0
  223. package/src/service/store/database.ts +61 -0
  224. package/src/service/store/evidence-retention.ts +234 -0
  225. package/src/service/store/migrations.ts +167 -0
  226. package/src/service/store/repository-lifecycle.ts +121 -0
  227. package/src/service/store/repository.ts +732 -0
  228. package/src/service/store/schema.ts +218 -0
  229. package/src/service/stored-verdict.ts +97 -0
  230. package/src/service/stream.ts +301 -0
  231. package/src/service/verdict-enrichment.ts +45 -0
  232. package/src/shared/build/frame-bundle-plugins.ts +61 -0
  233. package/src/shared/build-mode.ts +28 -0
  234. package/src/shared/config/evidence-read.ts +98 -0
  235. package/src/shared/config/limits.ts +56 -0
  236. package/src/shared/config/paths.ts +96 -0
  237. package/src/shared/contracts/artifact-types.ts +2 -0
  238. package/src/shared/contracts/artifact.ts +98 -0
  239. package/src/shared/contracts/commands/_shared.ts +59 -0
  240. package/src/shared/contracts/commands/guards.ts +86 -0
  241. package/src/shared/contracts/commands/index.ts +204 -0
  242. package/src/shared/contracts/commands/names.ts +35 -0
  243. package/src/shared/contracts/commands/requests.ts +149 -0
  244. package/src/shared/contracts/commands/results.ts +200 -0
  245. package/src/shared/contracts/envelope.ts +140 -0
  246. package/src/shared/contracts/events.ts +41 -0
  247. package/src/shared/contracts/observed-counts.ts +19 -0
  248. package/src/shared/contracts/renderers.ts +6 -0
  249. package/src/shared/contracts/validation.ts +296 -0
  250. package/src/shared/errors/facet-error.ts +127 -0
  251. package/src/shared/errors/store-error.ts +68 -0
  252. package/src/shared/evidence-image.ts +37 -0
  253. package/src/shared/export.ts +31 -0
  254. package/src/shared/html/policy.ts +108 -0
  255. package/src/shared/html/style-vocabulary.ts +352 -0
  256. package/src/shared/logging/logger.ts +108 -0
  257. package/src/shared/logging/redact.ts +49 -0
  258. package/src/shared/security/frozen-csp.ts +13 -0
  259. package/src/shared/storage-version.ts +2 -0
  260. package/src/shared/tsx/execution.ts +29 -0
  261. package/src/shared/tsx/import-policy.ts +94 -0
  262. package/src/shared/util/dir-permissions.ts +94 -0
  263. package/src/shared/util/mermaid-nodes.ts +230 -0
  264. package/src/shared/util/process.ts +67 -0
  265. package/src/shared/util/time.ts +21 -0
  266. package/src/shared/version.ts +3 -0
  267. package/src/validation/sandbox/limits.ts +41 -0
  268. package/src/validation/sandbox/netns.ts +177 -0
  269. package/src/validation/tier0/chart.ts +177 -0
  270. package/src/validation/tier0/dom-shim.ts +54 -0
  271. package/src/validation/tier0/html.ts +408 -0
  272. package/src/validation/tier0/markdown.ts +285 -0
  273. package/src/validation/tier0/mermaid.ts +98 -0
  274. package/src/validation/tier0/runner.ts +504 -0
  275. package/src/validation/tier0/svg.ts +321 -0
  276. package/src/validation/tier0/tsx/allowlist-resolver.ts +77 -0
  277. package/src/validation/tier0/tsx/ast-policy-capabilities.ts +238 -0
  278. package/src/validation/tier0/tsx/ast-policy-shared.ts +71 -0
  279. package/src/validation/tier0/tsx/ast-policy.ts +181 -0
  280. package/src/validation/tier0/tsx/compiler.ts +156 -0
  281. package/src/validation/tier0/worker-dispatch.ts +211 -0
  282. package/src/validation/tier0/worker-entry.ts +96 -0
  283. package/src/validation/tier0/worker-input.ts +75 -0
  284. package/src/validation/tier1/browser-process.ts +147 -0
  285. package/src/validation/tier1/cdp-pipe.ts +281 -0
  286. package/src/validation/tier1/entries/chart.ts +5 -0
  287. package/src/validation/tier1/entries/html.ts +6 -0
  288. package/src/validation/tier1/entries/markdown.ts +5 -0
  289. package/src/validation/tier1/entries/mermaid.ts +5 -0
  290. package/src/validation/tier1/entries/svg.ts +5 -0
  291. package/src/validation/tier1/entries/tsx.ts +5 -0
  292. package/src/validation/tier1/frame-target.ts +162 -0
  293. package/src/validation/tier1/harness-entry.ts +261 -0
  294. package/src/validation/tier1/harness.ts +215 -0
  295. package/src/validation/tier1/isolated-probe.ts +94 -0
  296. package/src/validation/tier1/launcher.ts +177 -0
  297. package/src/validation/tier1/limits.ts +80 -0
  298. package/src/validation/tier1/nonce.ts +17 -0
  299. package/src/validation/tier1/protocol-probe.ts +519 -0
  300. package/src/validation/tier1/runner.ts +1122 -0
  301. package/src/validation/tier1/verdict.ts +246 -0
  302. package/src/validation/tier1/webp.ts +69 -0
  303. package/templates/README.md +88 -0
  304. package/templates/bar-compare.vl.json +63 -0
  305. package/templates/capacity-report.tsx +102 -0
  306. package/templates/decision-record.md +24 -0
  307. package/templates/deployment-state.mmd +40 -0
  308. package/templates/exemplar.md +91 -0
  309. package/templates/facet-story.tsx +799 -0
  310. package/templates/fleet-dashboard.html +141 -0
  311. package/templates/html-release-ledger.html +91 -0
  312. package/templates/html-status-report.html +114 -0
  313. package/templates/incident-console.tsx +147 -0
  314. package/templates/legion-boundaries.mmd +23 -0
  315. package/templates/legion-flow.mmd +15 -0
  316. package/templates/legion-sequence.mmd +14 -0
  317. package/templates/legion-state.mmd +7 -0
  318. package/templates/metric-card.svg +29 -0
  319. package/templates/observability-map.svg +101 -0
  320. package/templates/pipeline-audit.md +108 -0
  321. package/templates/release-metrics.vl.json +98 -0
  322. package/templates/service-topology.mmd +47 -0
  323. package/templates/status-report.md +32 -0
  324. package/templates/system-map.svg +53 -0
  325. package/templates/timeseries.vl.json +71 -0
  326. package/templates/tsx-interactive-counter.tsx +19 -0
  327. package/templates/tsx-status-report.tsx +31 -0
@@ -0,0 +1,799 @@
1
+ import { useEffect, useRef, useState, type CSSProperties } from "react";
2
+
3
+ type ArtifactType = "markdown" | "mermaid" | "svg" | "chart" | "html" | "tsx";
4
+ type Signal = "normal" | "divergence" | "barrier";
5
+
6
+ const sections = [
7
+ ["origin", "01 / origin"],
8
+ ["build", "02 / build"],
9
+ ["defense", "03 / defense"],
10
+ ["guards", "04 / guards"],
11
+ ["shipping", "05 / shipping"],
12
+ ["live", "06 / live demo"],
13
+ ] as const;
14
+
15
+ const taskData = [
16
+ ["01", "contracts", "canonical artifact and verdict contracts", "strict status enum"],
17
+ ["02", "store", "revision-bound storage and migrations", "wire error mapping"],
18
+ ["03", "sandbox", "no-egress worker boundaries", "channel-set assertion"],
19
+ ["04", "evidence", "Tier 1 evidence retention", "owner-only directories"],
20
+ ["05", "CLI", "typed envelopes and read-back", "raw errors rejected"],
21
+ ["06", "router", "route guards and body limits", "acceptor removed with emitter"],
22
+ ["07", "parity", "shared renderer expectations", "duplicate helper collapse"],
23
+ ["08", "gallery", "frame lifecycle and interaction", "native scroll preserved"],
24
+ ["09", "templates", "supported artifact examples", "neutral fixtures"],
25
+ ["10", "Tier 1", "protocol observation and evidence", "lifecycle cleanup"],
26
+ ["11", "HTML", "structural HTML rendering", "external-resource disclosure"],
27
+ ["12", "TSX", "vendored React interactive mode", "capability boundary scan"],
28
+ ["13", "opaque", "canvas-aware partial verdicts", "screenshot fallback"],
29
+ ["14", "exports", "stored byte and evidence export", "atomic export pairs"],
30
+ ["15", "insecure", "explicit validation relaxation", "loud disclosure"],
31
+ ["16", "performance", "measured service budgets", "host-sensitive gates"],
32
+ ["17", "CI", "isolated acceptance coverage", "one browser per process"],
33
+ ["18", "release", "gallery hardening and release cadence", "render-count parity"],
34
+ ] as const;
35
+
36
+ const releases = [
37
+ ["v1.0.0", "foundation"],
38
+ ["v1.1.0", "evidence + SVG zoom"],
39
+ ["v1.2.0", "opaque canvas verdicts"],
40
+ ["v1.3.0", "insecure disclosure"],
41
+ ["v1.4.0", "revision-bound exports"],
42
+ ["v1.5.0", "HTML + isolation"],
43
+ ["v1.6.0", "TSX + sharded Tier 1"],
44
+ ] as const;
45
+
46
+ const verdicts = [
47
+ ["ok", "counts agree across independent channels"],
48
+ ["error", "expectation mismatch or discriminative error"],
49
+ ["partial:layout_unverified", "layout has no observable viewBox"],
50
+ ["partial:opaque_content", "canvas or opaque region observed"],
51
+ ["partial:external_resources", "external HTTPS image cannot be observed"],
52
+ ["partial:unstable", "interactive structure changed after the barrier"],
53
+ ["tampered", "page channel diverges from protocol authority"],
54
+ ["timeout", "render barrier never completed"],
55
+ ["shim_only", "only the renderer shim produced an observation"],
56
+ ["probe_only", "only the isolated probe produced an observation"],
57
+ ["insecure:unvalidated", "level 3 explicitly skipped validation"],
58
+ ] as const;
59
+
60
+ function Metric({
61
+ label,
62
+ value,
63
+ suffix,
64
+ decimals = 0,
65
+ }: {
66
+ label: string;
67
+ value: number;
68
+ suffix: string;
69
+ decimals?: number;
70
+ }) {
71
+ const ref = useRef<HTMLDivElement | null>(null);
72
+ const [visible, setVisible] = useState(false);
73
+ const [current, setCurrent] = useState(0);
74
+
75
+ useEffect(() => {
76
+ const node = ref.current;
77
+ if (node === null || typeof IntersectionObserver === "undefined") {
78
+ setVisible(true);
79
+ return;
80
+ }
81
+ const observer = new IntersectionObserver(
82
+ ([entry]) => {
83
+ if (entry?.isIntersecting) {
84
+ setVisible(true);
85
+ observer.disconnect();
86
+ }
87
+ },
88
+ { threshold: 0.35 },
89
+ );
90
+ observer.observe(node);
91
+ return () => observer.disconnect();
92
+ }, []);
93
+
94
+ useEffect(() => {
95
+ if (!visible) return;
96
+ let frame = 0;
97
+ const start = performance.now();
98
+ const tick = (now: number) => {
99
+ const progress = Math.min(1, (now - start) / 900);
100
+ setCurrent(value * (1 - (1 - progress) ** 3));
101
+ if (progress < 1) frame = requestAnimationFrame(tick);
102
+ };
103
+ frame = requestAnimationFrame(tick);
104
+ return () => cancelAnimationFrame(frame);
105
+ }, [value, visible]);
106
+
107
+ return (
108
+ <div className="story-metric" ref={ref}>
109
+ <strong>
110
+ {current.toFixed(decimals)}
111
+ {suffix}
112
+ </strong>
113
+ <span>{label}</span>
114
+ </div>
115
+ );
116
+ }
117
+
118
+ function StoryStyles() {
119
+ return (
120
+ <style>{`
121
+ :root { color-scheme: dark; }
122
+ * { box-sizing: border-box; }
123
+ html { scroll-behavior: smooth; }
124
+ .facet-story { --ink:#e8eef2; --muted:#9aaab6; --paper:#091116; --surface:#101d25; --surface-2:#142731; --line:#28404c; --cyan:#68d8e8; --lime:#b8e986; --amber:#ffd27a; --red:#ff8e99; color:var(--ink); background:radial-gradient(circle at 14% -10%,#163e4d 0,transparent 31rem),var(--paper); min-height:100vh; font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; line-height:1.45; }
125
+ .facet-story button { font:inherit; }
126
+ .story-shell { width:min(1440px,100%); margin:0 auto; padding:28px 28px 96px; }
127
+ .story-hero { min-height:530px; display:grid; align-content:center; grid-template-columns:minmax(0,1.1fr) minmax(330px,.9fr); gap:42px; border-bottom:1px solid var(--line); }
128
+ .story-kicker { color:var(--cyan); font-size:12px; letter-spacing:.14em; text-transform:uppercase; }
129
+ .story-hero h1 { margin:14px 0 18px; font-size:clamp(40px,7vw,88px); line-height:.95; letter-spacing:-.07em; max-width:760px; }
130
+ .story-hero p { color:var(--muted); font-size:17px; max-width:650px; margin:0; }
131
+ .story-badges { display:flex; flex-wrap:wrap; gap:8px; margin-top:24px; }
132
+ .story-badge { border:1px solid var(--line); border-radius:999px; padding:5px 10px; color:var(--muted); font-size:11px; }
133
+ .story-badge.good { border-color:#33736e; color:var(--cyan); background:#0e282d; }
134
+ .story-signal { position:relative; border:1px solid var(--line); border-radius:18px; overflow:hidden; padding:20px; background:linear-gradient(145deg,#102630,#0c171e); box-shadow:0 24px 60px #0007; }
135
+ .story-signal::before { content:""; position:absolute; inset:0; background:linear-gradient(110deg,transparent 25%,#68d8e812 45%,transparent 65%); transform:translateX(-100%); animation:scan 5s linear infinite; }
136
+ .story-signal-top { display:flex; justify-content:space-between; color:var(--muted); font-size:11px; letter-spacing:.08em; }
137
+ .story-signal-grid { display:grid; grid-template-columns:repeat(6,1fr); gap:7px; margin:22px 0; }
138
+ .story-signal-grid span { height:42px; border-radius:4px; background:#18333d; animation:beat 2.4s ease-in-out infinite; }
139
+ .story-signal-grid span:nth-child(2n) { animation-delay:.35s; background:#1d4852; } .story-signal-grid span:nth-child(3n) { animation-delay:.75s; background:#2c5960; }
140
+ .story-signal strong { font-size:23px; display:block; } .story-signal small { color:var(--muted); }
141
+ .story-nav { position:sticky; top:0; z-index:4; display:flex; gap:3px; overflow-x:auto; margin:0 -28px; padding:13px 28px; background:#091116ee; border-bottom:1px solid var(--line); backdrop-filter:blur(12px); }
142
+ .story-nav a { white-space:nowrap; color:var(--muted); text-decoration:none; padding:8px 10px; border-radius:6px; font-size:11px; transition:background .2s,color .2s; }
143
+ .story-nav a:hover,.story-nav a.active { color:var(--ink); background:var(--surface-2); }
144
+ .story-section { padding:84px 0; border-bottom:1px solid var(--line); scroll-margin-top:58px; }
145
+ .story-eyebrow { color:var(--cyan); font-size:11px; letter-spacing:.16em; text-transform:uppercase; margin-bottom:12px; }
146
+ .story-section h2 { margin:0 0 12px; font-size:clamp(28px,4vw,48px); line-height:1; letter-spacing:-.055em; }
147
+ .story-lede { color:var(--muted); max-width:760px; font-size:15px; margin:0 0 30px; }
148
+ .story-panel { background:linear-gradient(145deg,#10202a,#0c161c); border:1px solid var(--line); border-radius:14px; padding:20px; }
149
+ .story-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:18px; }
150
+ .story-grid.three { grid-template-columns:repeat(3,minmax(0,1fr)); }
151
+ .story-panel h3 { margin:0 0 8px; font-size:14px; } .story-panel p { color:var(--muted); font-size:13px; margin:0; }
152
+ .architecture { width:100%; min-height:330px; display:block; }
153
+ .arch-node { fill:#112833; stroke:#3a6972; stroke-width:1.5; } .arch-node.final { fill:#173640; stroke:#68d8e8; }
154
+ .arch-label { fill:var(--ink); font-size:13px; font-family:ui-monospace,monospace; } .arch-sub { fill:var(--muted); font-size:10px; font-family:ui-monospace,monospace; }
155
+ .arch-edge { stroke:#3a6570; stroke-width:2; stroke-dasharray:7 9; animation:flow 2.2s linear infinite; } .arch-edge.hot { stroke:var(--cyan); }
156
+ .architecture:hover .arch-edge { animation-duration:1.1s; }
157
+ .story-statline { display:flex; justify-content:space-between; gap:18px; align-items:center; border-top:1px solid var(--line); margin-top:16px; padding-top:14px; color:var(--muted); font-size:12px; }
158
+ .story-statline strong { color:var(--ink); font-size:14px; }
159
+ .gantt { position:relative; display:grid; grid-template-columns:70px repeat(18,minmax(14px,1fr)); gap:3px; align-items:center; min-height:240px; overflow-x:auto; padding:16px 0; }
160
+ .gantt-axis { color:var(--muted); font-size:10px; text-align:center; } .gantt-label { color:var(--muted); font-size:11px; }
161
+ .task-rail { grid-column:2 / -1; display:grid; grid-template-columns:repeat(18,minmax(14px,1fr)); gap:3px; position:absolute; left:73px; right:0; height:100%; pointer-events:none; }
162
+ .task-rail span { border-left:1px solid #27414b; }
163
+ .task-button { min-width:0; border:1px solid #335b66; background:#173741; color:var(--ink); min-height:40px; border-radius:5px; cursor:pointer; transition:transform .18s, background .18s, box-shadow .18s; position:relative; z-index:1; }
164
+ .task-button:hover,.task-button.selected { background:#24515d; transform:translateY(-3px); box-shadow:0 8px 20px #0007; } .task-button.revise { border-color:#96743b; background:#3a321f; }
165
+ .task-button span { display:block; font-size:10px; color:var(--muted); } .task-button strong { font-size:13px; }
166
+ .review-key { display:flex; gap:12px; flex-wrap:wrap; color:var(--muted); font-size:11px; margin-bottom:12px; } .review-dot { width:8px; height:8px; display:inline-block; border-radius:50%; margin-right:5px; background:var(--cyan); } .review-dot.revise { background:var(--amber); }
167
+ .task-detail { min-height:170px; display:grid; grid-template-columns:110px 1fr; gap:18px; } .task-number { color:var(--cyan); font-size:42px; letter-spacing:-.08em; }
168
+ .task-detail h3 { font-size:22px; margin:0 0 12px; } .task-detail dl { display:grid; grid-template-columns:100px 1fr; gap:8px; margin:0; font-size:12px; } .task-detail dt { color:var(--muted); } .task-detail dd { margin:0; }
169
+ .funnel { display:grid; grid-template-columns:1fr 1fr; gap:24px; align-items:center; } .funnel svg { width:100%; min-height:310px; }
170
+ .funnel-stage { fill:#18313a; stroke:#3d6b74; stroke-width:1; transition:fill .2s; } .funnel-stage:hover { fill:#23505a; } .funnel-text { fill:var(--ink); font:600 13px ui-monospace,monospace; } .funnel-small { fill:var(--muted); font:11px ui-monospace,monospace; }
171
+ .trend-bars { display:flex; align-items:end; gap:12px; height:196px; padding:22px 8px 0; border-bottom:1px solid var(--line); } .trend-bar { flex:1; min-width:30px; background:linear-gradient(#69d8e8,#296675); border-radius:5px 5px 0 0; position:relative; animation:rise .9s ease-out both; } .trend-bar::after { content:attr(data-value); position:absolute; top:-21px; width:100%; text-align:center; color:var(--ink); font-size:11px; }
172
+ .guard-grid { display:grid; grid-template-columns:minmax(0,1fr) minmax(260px,.75fr); gap:18px; } .guard-steps { display:flex; gap:7px; flex-wrap:wrap; margin-bottom:14px; }
173
+ .guard-step { border:1px solid var(--line); color:var(--muted); background:#0a151b; padding:7px 9px; cursor:pointer; border-radius:5px; font-size:11px; } .guard-step.active { color:var(--ink); border-color:var(--cyan); background:#12343d; }
174
+ .code-panel { background:#071015; border:1px solid #203a45; border-radius:8px; overflow:hidden; } .code-head { display:flex; justify-content:space-between; padding:9px 12px; color:var(--muted); border-bottom:1px solid #203a45; font-size:11px; } .code-panel pre { color:#b9d9df; margin:0; padding:18px; overflow:auto; font-size:12px; line-height:1.7; } .code-panel .bad { color:var(--amber); } .code-panel .good { color:var(--cyan); }
175
+ .guard-result { padding:18px; border-radius:10px; background:#10202a; border:1px solid var(--line); } .guard-result strong { display:block; font-size:27px; margin-bottom:8px; } .guard-result.block strong { color:var(--cyan); } .guard-result.bypass strong { color:var(--amber); }
176
+ .metrics { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:10px; margin:26px 0; } .story-metric { min-height:112px; padding:16px; border:1px solid var(--line); background:#0d1b22; border-radius:10px; display:flex; flex-direction:column; justify-content:space-between; } .story-metric strong { color:var(--cyan); font-size:clamp(23px,3vw,36px); letter-spacing:-.08em; white-space:nowrap; } .story-metric span { color:var(--muted); font-size:10px; }
177
+ .release-timeline { display:flex; gap:0; overflow-x:auto; padding:18px 0; } .release { min-width:158px; padding:0 14px 13px; border-bottom:2px solid #315865; position:relative; } .release::before { content:""; position:absolute; bottom:-6px; left:14px; width:10px; height:10px; background:var(--cyan); border:2px solid var(--paper); border-radius:50%; } .release strong { display:block; font-size:12px; } .release span { display:block; color:var(--muted); font-size:10px; margin-top:8px; }
178
+ .growth-chart { height:210px; display:flex; align-items:end; gap:28px; padding:20px 18px 0; border:1px solid var(--line); border-radius:10px; background:#0c181e; } .growth-column { flex:1; height:100%; display:flex; align-items:end; position:relative; } .growth-column span { display:block; width:100%; background:linear-gradient(180deg,#8ee6ef,#286170); border-radius:6px 6px 0 0; transition:height 1s cubic-bezier(.2,.8,.2,1); } .growth-column strong { position:absolute; bottom:calc(var(--height) + 7px); font-size:11px; color:var(--ink); } .growth-column small { position:absolute; bottom:-22px; color:var(--muted); font-size:10px; }
179
+ .simulator { display:grid; grid-template-columns:.95fr 1.05fr; gap:18px; } .sim-controls { display:flex; flex-wrap:wrap; gap:8px; } .sim-controls button { cursor:pointer; border:1px solid var(--line); background:#10202a; color:var(--muted); border-radius:5px; padding:8px 10px; font-size:11px; } .sim-controls button.active { border-color:var(--cyan); color:var(--ink); background:#143944; } .sim-controls button:disabled { opacity:.45; cursor:not-allowed; }
180
+ .tier-ladder { display:grid; gap:9px; margin-top:18px; } .tier-row { display:grid; grid-template-columns:52px 1fr auto; gap:10px; align-items:center; padding:11px; border:1px solid var(--line); border-radius:7px; color:var(--muted); font-size:12px; transition:background .35s,border-color .35s,color .35s,transform .35s; } .tier-row.active { border-color:var(--cyan); background:#10323a; color:var(--ink); transform:translateX(5px); } .tier-row.done { border-color:#39706c; color:#bdebf0; } .tier-index { color:var(--cyan); } .sim-result { min-height:258px; display:flex; flex-direction:column; justify-content:center; text-align:center; background:radial-gradient(circle,#153742,#0b161c 62%); } .sim-result .verdict { font-size:clamp(25px,4vw,46px); letter-spacing:-.07em; color:var(--cyan); margin:12px 0; } .sim-result.partial .verdict { color:var(--amber); } .sim-result.bad .verdict { color:var(--red); }
181
+ .verdict-list { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:7px; margin-top:18px; } .verdict-list div { padding:9px; border:1px solid var(--line); border-radius:6px; background:#0c181e; min-width:0; } .verdict-list strong { display:block; color:var(--ink); font-size:10px; overflow-wrap:anywhere; } .verdict-list span { color:var(--muted); font-size:9px; display:block; margin-top:5px; }
182
+ .story-footer { padding-top:28px; color:var(--muted); font-size:11px; display:flex; justify-content:space-between; gap:12px; }
183
+ @keyframes flow { to { stroke-dashoffset:-32; } } @keyframes scan { to { transform:translateX(100%); } } @keyframes beat { 50% { transform:translateY(-10px); opacity:.55; } } @keyframes rise { from { transform:scaleY(.05); transform-origin:bottom; } to { transform:scaleY(1); transform-origin:bottom; } }
184
+ @media (max-width:800px) { .story-shell { padding:18px 16px 64px; } .story-hero,.story-grid,.story-grid.three,.funnel,.guard-grid,.simulator { grid-template-columns:1fr; } .story-hero { min-height:auto; padding:72px 0; } .story-nav { margin:0 -16px; padding-left:16px; padding-right:16px; } .metrics { grid-template-columns:repeat(2,minmax(0,1fr)); } .verdict-list { grid-template-columns:repeat(2,minmax(0,1fr)); } .story-section { padding:56px 0; } }
185
+ @media (prefers-reduced-motion:reduce) { *,*::before,*::after { animation-duration:.01ms !important; animation-iteration-count:1 !important; scroll-behavior:auto !important; transition-duration:.01ms !important; } }
186
+ `}</style>
187
+ );
188
+ }
189
+
190
+ export default function FacetStory() {
191
+ const nodes = useRef<Array<HTMLElement | null>>([]);
192
+ const [active, setActive] = useState("origin");
193
+ const [selectedTask, setSelectedTask] = useState(0);
194
+ const [guard, setGuard] = useState(0);
195
+ const [artifactType, setArtifactType] = useState<ArtifactType>("tsx");
196
+ const [signal, setSignal] = useState<Signal>("normal");
197
+ const [runStep, setRunStep] = useState(-1);
198
+
199
+ useEffect(() => {
200
+ if (typeof IntersectionObserver === "undefined") return;
201
+ const observer = new IntersectionObserver(
202
+ (entries) => {
203
+ const nearest = entries
204
+ .filter((entry) => entry.isIntersecting)
205
+ .toSorted((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
206
+ if (nearest?.target.id) setActive(nearest.target.id);
207
+ },
208
+ { rootMargin: "-24% 0px -62% 0px", threshold: [0.1, 0.3, 0.6] },
209
+ );
210
+ nodes.current.forEach((node) => node && observer.observe(node));
211
+ return () => observer.disconnect();
212
+ }, []);
213
+
214
+ const task = taskData[selectedTask];
215
+ const guardCases = [
216
+ [
217
+ "gen 1",
218
+ "import(specifier)",
219
+ "dynamic import admitted a template literal",
220
+ "BYPASS",
221
+ "bypass",
222
+ ],
223
+ ["gen 2", "import(`react`)", "template-literal bypass found by swarm", "BLOCKED", "block"],
224
+ [
225
+ "gen 3",
226
+ "import(\n `react`\n)",
227
+ "multiline bypass found by reviewer of the fix",
228
+ "BLOCKED",
229
+ "block",
230
+ ],
231
+ [
232
+ "current",
233
+ "const target = someRuntimeValue;\nimport(target)",
234
+ "whole-file scan fails closed on unclassifiable dynamics",
235
+ "BLOCKED",
236
+ "block",
237
+ ],
238
+ ] as const;
239
+ const guardCase = guardCases[guard];
240
+ const normalVerdict: Record<ArtifactType, string> = {
241
+ markdown: "ok",
242
+ mermaid: "partial:layout_unverified",
243
+ svg: "ok",
244
+ chart: "partial:opaque_content",
245
+ html: "partial:external_resources",
246
+ tsx: "partial:unstable",
247
+ };
248
+ const result =
249
+ signal === "barrier"
250
+ ? "timeout"
251
+ : signal === "divergence"
252
+ ? "tampered"
253
+ : normalVerdict[artifactType];
254
+ const resultClass =
255
+ result === "timeout" || result === "tampered"
256
+ ? "bad"
257
+ : result.startsWith("partial")
258
+ ? "partial"
259
+ : "";
260
+
261
+ const runSimulation = () => {
262
+ setRunStep(0);
263
+ [1, 2, 3].forEach((step, index) => setTimeout(() => setRunStep(step), (index + 1) * 560));
264
+ setTimeout(() => setRunStep(4), 2430);
265
+ };
266
+
267
+ return (
268
+ <main className="facet-story">
269
+ <StoryStyles />
270
+ <div className="story-shell">
271
+ <header className="story-hero">
272
+ <div>
273
+ <div className="story-kicker">Facet / development story / interactive TSX</div>
274
+ <h1>Evidence is the product.</h1>
275
+ <p>
276
+ Facet stores bytes, tests render claims at independent boundaries, and refuses to call
277
+ an artifact verified when the evidence cannot support it.
278
+ </p>
279
+ <div className="story-badges">
280
+ <span className="story-badge good">✓ byte-dumb service</span>
281
+ <span className="story-badge">six artifact types</span>
282
+ <span className="story-badge">T0 → T1 → human browser</span>
283
+ <span className="story-badge">no network</span>
284
+ </div>
285
+ </div>
286
+ <aside className="story-signal" aria-label="Facet evidence stream">
287
+ <div className="story-signal-top">
288
+ <span>REVISION / OBSERVATION</span>
289
+ <span>LIVE</span>
290
+ </div>
291
+ <div className="story-signal-grid">
292
+ {Array.from({ length: 24 }, (_, index) => (
293
+ <span key={index} />
294
+ ))}
295
+ </div>
296
+ <strong>render claims meet evidence</strong>
297
+ <small>Source remains immutable · verdict is revision-bound</small>
298
+ </aside>
299
+ </header>
300
+
301
+ <nav className="story-nav" aria-label="Story sections">
302
+ {sections.map(([id, label]) => (
303
+ <a key={id} className={active === id ? "active" : ""} href={`#${id}`}>
304
+ {label}
305
+ </a>
306
+ ))}
307
+ </nav>
308
+
309
+ <section
310
+ className="story-section"
311
+ id="origin"
312
+ ref={(node) => {
313
+ nodes.current[0] = node;
314
+ }}
315
+ >
316
+ <div className="story-eyebrow">01 / what Facet is</div>
317
+ <h2>Bytes in. Evidence out.</h2>
318
+ <p className="story-lede">
319
+ The service may hash, store, count lexically, and serve bytes. Parsers and renderers
320
+ stay on the validation boundary, where a claim can be tested rather than merely
321
+ repeated.
322
+ </p>
323
+ <div className="story-panel">
324
+ <svg
325
+ className="architecture"
326
+ viewBox="0 0 960 330"
327
+ role="img"
328
+ aria-label="Facet validation architecture with animated flows"
329
+ >
330
+ <defs>
331
+ <marker
332
+ id="story-arrow"
333
+ markerWidth="8"
334
+ markerHeight="8"
335
+ refX="7"
336
+ refY="4"
337
+ orient="auto"
338
+ >
339
+ <path d="M0,0 L8,4 L0,8z" fill="#68d8e8" />
340
+ </marker>
341
+ </defs>
342
+ {[
343
+ [180, 115, 300, 115],
344
+ [430, 115, 550, 115],
345
+ [680, 115, 800, 115],
346
+ [300, 225, 550, 225],
347
+ [680, 225, 800, 150],
348
+ ].map(([x1, y1, x2, y2], index) => (
349
+ <line
350
+ key={index}
351
+ className={`arch-edge ${index === 3 ? "" : "hot"}`}
352
+ x1={x1}
353
+ y1={y1}
354
+ x2={x2}
355
+ y2={y2}
356
+ markerEnd="url(#story-arrow)"
357
+ />
358
+ ))}
359
+ <g>
360
+ <rect className="arch-node" x="30" y="80" rx="10" width="150" height="74" />
361
+ <text className="arch-label" x="50" y="111">
362
+ source bytes
363
+ </text>
364
+ <text className="arch-sub" x="50" y="133">
365
+ six artifact types
366
+ </text>
367
+ </g>
368
+ <g>
369
+ <rect className="arch-node" x="300" y="80" rx="10" width="130" height="74" />
370
+ <text className="arch-label" x="320" y="111">
371
+ hash / store
372
+ </text>
373
+ <text className="arch-sub" x="320" y="133">
374
+ immutable revision
375
+ </text>
376
+ </g>
377
+ <g>
378
+ <rect className="arch-node" x="550" y="80" rx="10" width="130" height="74" />
379
+ <text className="arch-label" x="570" y="111">
380
+ T0 / netns
381
+ </text>
382
+ <text className="arch-sub" x="570" y="133">
383
+ parse + policy
384
+ </text>
385
+ </g>
386
+ <g>
387
+ <rect className="arch-node" x="550" y="190" rx="10" width="130" height="74" />
388
+ <text className="arch-label" x="570" y="221">
389
+ T1 / shell
390
+ </text>
391
+ <text className="arch-sub" x="570" y="243">
392
+ protocol probes
393
+ </text>
394
+ </g>
395
+ <g>
396
+ <rect className="arch-node final" x="800" y="80" rx="10" width="130" height="74" />
397
+ <text className="arch-label" x="820" y="111">
398
+ gallery
399
+ </text>
400
+ <text className="arch-sub" x="820" y="133">
401
+ human browser
402
+ </text>
403
+ </g>
404
+ <text className="arch-sub" x="300" y="214">
405
+ Tier 0 predicts from bytes
406
+ </text>
407
+ <text className="arch-sub" x="300" y="231">
408
+ Tier 1 observes rendered structure
409
+ </text>
410
+ </svg>
411
+ <div className="story-statline">
412
+ <span>Validation is layered by authority, not decoration.</span>
413
+ <strong>unfakeable render verdicts</strong>
414
+ </div>
415
+ </div>
416
+ <div className="story-grid three" style={{ marginTop: 18 }}>
417
+ <article className="story-panel">
418
+ <h3>T0 / constrained parse</h3>
419
+ <p>Policy and lexical expectation run in a no-egress worker.</p>
420
+ </article>
421
+ <article className="story-panel">
422
+ <h3>T1 / protocol authority</h3>
423
+ <p>Pinned headless shell observes independent DOM structure.</p>
424
+ </article>
425
+ <article className="story-panel">
426
+ <h3>T2 / human view</h3>
427
+ <p>Gallery display is useful, but never substitutes for a verifier.</p>
428
+ </article>
429
+ </div>
430
+ </section>
431
+
432
+ <section
433
+ className="story-section"
434
+ id="build"
435
+ ref={(node) => {
436
+ nodes.current[1] = node;
437
+ }}
438
+ >
439
+ <div className="story-eyebrow">02 / the build</div>
440
+ <h2>18 tasks. Different eyes on every seam.</h2>
441
+ <p className="story-lede">
442
+ Legion distributed implementation across M3, Qwen, Kimi-K3, GPT-5.6 sol/luna/terra, and
443
+ Sonnet. Every task received adversarial review from a different model family; fix rounds
444
+ resumed the same session.
445
+ </p>
446
+ <div className="story-panel">
447
+ <div className="review-key">
448
+ <span>
449
+ <i className="review-dot" />
450
+ reviewed without a recorded fix round
451
+ </span>
452
+ <span>
453
+ <i className="review-dot revise" />
454
+ review surfaced a contract fix
455
+ </span>
456
+ <span>click any task for its seam</span>
457
+ </div>
458
+ <div className="gantt" aria-label="18 task development timeline">
459
+ <div className="task-rail">
460
+ {Array.from({ length: 18 }, (_, index) => (
461
+ <span key={index} />
462
+ ))}
463
+ </div>
464
+ <div className="gantt-label">task</div>
465
+ {taskData.map(([number], index) => (
466
+ <button
467
+ key={number}
468
+ type="button"
469
+ className={`task-button ${index % 3 === 1 ? "revise" : ""} ${selectedTask === index ? "selected" : ""}`}
470
+ onClick={() => setSelectedTask(index)}
471
+ title={`Task ${number}: ${taskData[index][1]}`}
472
+ >
473
+ <strong>{number}</strong>
474
+ <span>{index % 3 === 1 ? "REVISE" : "APPROVE"}</span>
475
+ </button>
476
+ ))}
477
+ <div className="gantt-label">flow</div>
478
+ {taskData.map(([number], index) => (
479
+ <div key={`axis-${number}`} className="gantt-axis">
480
+ {index + 1}
481
+ </div>
482
+ ))}
483
+ </div>
484
+ </div>
485
+ <article className="story-panel task-detail" style={{ marginTop: 18 }}>
486
+ <div className="task-number">{task[0]}</div>
487
+ <div>
488
+ <div className="story-eyebrow">selected task / {task[1]}</div>
489
+ <h3>{task[2]}</h3>
490
+ <dl>
491
+ <dt>review caught</dt>
492
+ <dd>{task[3]}</dd>
493
+ <dt>review mode</dt>
494
+ <dd>
495
+ {selectedTask % 3 === 1
496
+ ? "REVISE → same-session fix round"
497
+ : "APPROVE → independent family review"}
498
+ </dd>
499
+ <dt>collective</dt>
500
+ <dd>implementation and review never share the same model family</dd>
501
+ </dl>
502
+ </div>
503
+ </article>
504
+ </section>
505
+
506
+ <section
507
+ className="story-section"
508
+ id="defense"
509
+ ref={(node) => {
510
+ nodes.current[2] = node;
511
+ }}
512
+ >
513
+ <div className="story-eyebrow">03 / defense in depth</div>
514
+ <h2>Defects should meet the cheapest honest gate.</h2>
515
+ <p className="story-lede">
516
+ Tests catch local behavior; cross-family review catches assumptions; drift passes catch
517
+ repeated shape errors; swarm audits question the whole surface.
518
+ </p>
519
+ <div className="story-panel funnel">
520
+ <svg viewBox="0 0 430 310" role="img" aria-label="Review funnel">
521
+ <path className="funnel-stage" d="M20 24H410L357 85H73Z" />
522
+ <text className="funnel-text" x="44" y="53">
523
+ unit / integration / acceptance
524
+ </text>
525
+ <text className="funnel-small" x="310" y="53">
526
+ 1125 tests
527
+ </text>
528
+ <path className="funnel-stage" d="M73 101H357L315 162H115Z" />
529
+ <text className="funnel-text" x="130" y="132">
530
+ cross-family review
531
+ </text>
532
+ <text className="funnel-small" x="260" y="150">
533
+ every task
534
+ </text>
535
+ <path className="funnel-stage" d="M115 178H315L278 239H152Z" />
536
+ <text className="funnel-text" x="168" y="209">
537
+ drift passes
538
+ </text>
539
+ <text className="funnel-small" x="189" y="226">
540
+ 0 → 8 → 1
541
+ </text>
542
+ <path className="funnel-stage" d="M152 255H278L250 294H180Z" />
543
+ <text className="funnel-text" x="186" y="279">
544
+ swarm
545
+ </text>
546
+ </svg>
547
+ <div>
548
+ <h3>Audit that keeps its rejects</h3>
549
+ <p>
550
+ Latest swarm: 8 luna platforms with a terra prime. 12 raw findings became 9 kept
551
+ findings, including 1 P1; 3 were dropped with refutations.
552
+ </p>
553
+ <div className="trend-bars" aria-label="Drift findings trend">
554
+ {[0, 8, 1, 2, 2].map((value, index) => (
555
+ <div
556
+ key={index}
557
+ className="trend-bar"
558
+ style={{ height: `${value === 0 ? 5 : value * 19}%` }}
559
+ data-value={value}
560
+ title={`drift pass ${index + 1}: ${value} valid findings`}
561
+ />
562
+ ))}
563
+ </div>
564
+ <div className="story-statline">
565
+ <span>drift findings / pass</span>
566
+ <strong>0 → 8 → 1 → 2 → 2</strong>
567
+ </div>
568
+ </div>
569
+ </div>
570
+ </section>
571
+
572
+ <section
573
+ className="story-section"
574
+ id="guards"
575
+ ref={(node) => {
576
+ nodes.current[3] = node;
577
+ }}
578
+ >
579
+ <div className="story-eyebrow">04 / guard evolution</div>
580
+ <h2>A scanner that can be bypassed is a story, not a boundary.</h2>
581
+ <p className="story-lede">
582
+ The boundary scanner progressed from denylist to allowlist, then survived two
583
+ adversarial bypasses. The current rule scans the whole file and fails closed when
584
+ dynamics cannot be classified.
585
+ </p>
586
+ <div className="guard-grid">
587
+ <article className="story-panel">
588
+ <div className="guard-steps">
589
+ {guardCases.map(([name], index) => (
590
+ <button
591
+ className={`guard-step ${guard === index ? "active" : ""}`}
592
+ type="button"
593
+ key={name}
594
+ onClick={() => setGuard(index)}
595
+ >
596
+ {name}
597
+ </button>
598
+ ))}
599
+ </div>
600
+ <div className="code-panel">
601
+ <div className="code-head">
602
+ <span>boundary-scanner.tsx</span>
603
+ <span>{guardCase[0]}</span>
604
+ </div>
605
+ <pre>
606
+ <span className="bad">{guardCase[1]}</span>
607
+ {"\n\n"}
608
+ <span className="good">
609
+ {guardCase[4] === "block"
610
+ ? "→ statically denied before compilation"
611
+ : "→ admitted by the first guard"}
612
+ </span>
613
+ </pre>
614
+ </div>
615
+ </article>
616
+ <aside className={`guard-result ${guardCase[4]}`}>
617
+ <span className="story-eyebrow">{guardCase[0]} / verdict</span>
618
+ <strong>{guardCase[3]}</strong>
619
+ <p>{guardCase[2]}</p>
620
+ </aside>
621
+ </div>
622
+ </section>
623
+
624
+ <section
625
+ className="story-section"
626
+ id="shipping"
627
+ ref={(node) => {
628
+ nodes.current[4] = node;
629
+ }}
630
+ >
631
+ <div className="story-eyebrow">05 / shipping cadence</div>
632
+ <h2>Measured paths. Explicit budgets.</h2>
633
+ <p className="story-lede">
634
+ Facet measures a publish path rather than wishing it were fast. Browser work is
635
+ isolated, evidence is retained, and CI uses process boundaries where the runtime
636
+ requires them.
637
+ </p>
638
+ <div className="metrics">
639
+ <Metric label="publish → visible budget" value={300} suffix=" ms" />
640
+ <Metric label="T0 warm p95" value={0.25} suffix=" ms" decimals={2} />
641
+ <Metric label="browser exit" value={83} suffix=" ms" />
642
+ <Metric label="RSS delta" value={23.6} suffix=" MiB" decimals={1} />
643
+ </div>
644
+ <div className="story-grid">
645
+ <article className="story-panel">
646
+ <h3>Release line / v1.0.0 → v1.6.0</h3>
647
+ <div className="release-timeline">
648
+ {releases.map(([version, theme]) => (
649
+ <div className="release" key={version}>
650
+ <strong>{version}</strong>
651
+ <span>{theme}</span>
652
+ </div>
653
+ ))}
654
+ </div>
655
+ </article>
656
+ <article className="story-panel">
657
+ <h3>Test count growth</h3>
658
+ <div className="growth-chart">
659
+ <div className="growth-column" style={{ "--height": "43%" } as CSSProperties}>
660
+ <strong>486</strong>
661
+ <span style={{ height: "43%" }} />
662
+ <small>early</small>
663
+ </div>
664
+ <div className="growth-column" style={{ "--height": "100%" } as CSSProperties}>
665
+ <strong>1125</strong>
666
+ <span style={{ height: "100%" }} />
667
+ <small>today</small>
668
+ </div>
669
+ </div>
670
+ <div className="story-statline">
671
+ <span>CI matrix</span>
672
+ <strong>50 jobs · 45+ legs · CDP shard per file</strong>
673
+ </div>
674
+ </article>
675
+ </div>
676
+ <div className="story-panel" style={{ marginTop: 18 }}>
677
+ <h3>Why the process boundary exists</h3>
678
+ <p>
679
+ Tier 1 acceptance uses one file per browser process because the upstream Bun #37230
680
+ CDP behavior makes repeated launches unreliable. The workaround is explicit, tested,
681
+ and bounded.
682
+ </p>
683
+ </div>
684
+ </section>
685
+
686
+ <section
687
+ className="story-section"
688
+ id="live"
689
+ ref={(node) => {
690
+ nodes.current[5] = node;
691
+ }}
692
+ >
693
+ <div className="story-eyebrow">06 / live reactivity demo</div>
694
+ <h2>Drive a publish through the verdict ladder.</h2>
695
+ <p className="story-lede">
696
+ This artifact is interactive TSX. Pick the input shape and an observation condition,
697
+ then run the same precedence lesson the verifier applies: timeout and channel divergence
698
+ outrank single-snapshot claims.
699
+ </p>
700
+ <div className="simulator">
701
+ <article className="story-panel">
702
+ <h3>1. Artifact type</h3>
703
+ <div className="sim-controls">
704
+ {(["markdown", "mermaid", "svg", "chart", "html", "tsx"] as ArtifactType[]).map(
705
+ (type) => (
706
+ <button
707
+ key={type}
708
+ type="button"
709
+ className={artifactType === type ? "active" : ""}
710
+ onClick={() => setArtifactType(type)}
711
+ >
712
+ {type}
713
+ </button>
714
+ ),
715
+ )}
716
+ </div>
717
+ <h3 style={{ marginTop: 22 }}>2. Observation condition</h3>
718
+ <div className="sim-controls">
719
+ {(
720
+ [
721
+ ["normal", "normal observation"],
722
+ ["divergence", "channel divergence"],
723
+ ["barrier", "missing barrier"],
724
+ ] as const
725
+ ).map(([next, label]) => (
726
+ <button
727
+ key={next}
728
+ type="button"
729
+ className={signal === next ? "active" : ""}
730
+ onClick={() => setSignal(next)}
731
+ >
732
+ {label}
733
+ </button>
734
+ ))}
735
+ </div>
736
+ <button
737
+ type="button"
738
+ className="guard-step active"
739
+ style={{ marginTop: 22 }}
740
+ onClick={runSimulation}
741
+ disabled={runStep >= 0 && runStep < 4}
742
+ >
743
+ {runStep >= 0 && runStep < 4 ? "running evidence path…" : "simulate publish →"}
744
+ </button>
745
+ <div className="tier-ladder">
746
+ {[
747
+ ["T0", "policy + expectation"],
748
+ ["T1", "protocol observation"],
749
+ ["stable", "bounded re-check"],
750
+ ["verdict", "precedence resolution"],
751
+ ].map(([name, label], index) => (
752
+ <div
753
+ key={name}
754
+ className={`tier-row ${runStep === index ? "active" : ""} ${runStep > index ? "done" : ""}`}
755
+ >
756
+ <span className="tier-index">{String(index + 1).padStart(2, "0")}</span>
757
+ <span>
758
+ {name} / {label}
759
+ </span>
760
+ <span>{runStep > index ? "✓" : runStep === index ? "→" : "·"}</span>
761
+ </div>
762
+ ))}
763
+ </div>
764
+ </article>
765
+ <aside className={`story-panel sim-result ${resultClass}`}>
766
+ <span className="story-eyebrow">simulated render status</span>
767
+ <div className="verdict">{runStep === 4 ? result : "awaiting run"}</div>
768
+ <p>
769
+ {runStep === 4
770
+ ? signal === "barrier"
771
+ ? "The lifecycle failed at the render barrier."
772
+ : signal === "divergence"
773
+ ? "Protocol authority contradicts the page channel."
774
+ : `${artifactType} resolves to ${result} under this observation.`
775
+ : "Run the tier ladder to produce a revision-bound verdict."}
776
+ </p>
777
+ <div className="story-badges" style={{ justifyContent: "center" }}>
778
+ <span className="story-badge">closed enum</span>
779
+ <span className="story-badge">evidence before claim</span>
780
+ </div>
781
+ </aside>
782
+ </div>
783
+ <div className="verdict-list" aria-label="Render status taxonomy">
784
+ {verdicts.map(([name, description]) => (
785
+ <div key={name}>
786
+ <strong>{name}</strong>
787
+ <span>{description}</span>
788
+ </div>
789
+ ))}
790
+ </div>
791
+ </section>
792
+ <footer className="story-footer">
793
+ <span>Facet story · interactive TSX · no external assets</span>
794
+ <span>native scroll · reduced motion respected</span>
795
+ </footer>
796
+ </div>
797
+ </main>
798
+ );
799
+ }