@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.
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +23 -0
- package/README.md +158 -0
- package/dist/gallery/chunk-4g8rem85.css +656 -0
- package/dist/gallery/chunk-c6s9k9ty.js +5970 -0
- package/dist/gallery/frame/artifact.css +8424 -0
- package/dist/gallery/frame/chunks/abnfDiagram-N423BO3Z-jj74szbm.js +155 -0
- package/dist/gallery/frame/chunks/architecture-TIHT7OUA-cvdjn7dt.js +10 -0
- package/dist/gallery/frame/chunks/architectureDiagram-T3A2C74G-n4z77eg6.js +8675 -0
- package/dist/gallery/frame/chunks/blockDiagram-VBNYF7ZC-7xsqzrd1.js +3529 -0
- package/dist/gallery/frame/chunks/c4Diagram-5PPSVZJV-z2zjs7wx.js +2413 -0
- package/dist/gallery/frame/chunks/classDiagram-JCYQIIEL-ve8rngqp.js +47 -0
- package/dist/gallery/frame/chunks/classDiagram-v2-OCEON4UE-8cfaxb8z.js +47 -0
- package/dist/gallery/frame/chunks/cose-bilkent-JH36ORCC-sn92qexx.js +4822 -0
- package/dist/gallery/frame/chunks/cynefin-VYW2F7L2-tqjv913d.js +10 -0
- package/dist/gallery/frame/chunks/cynefinDiagram-MW4NZA55-c1c0dj12.js +500 -0
- package/dist/gallery/frame/chunks/dagre-VZM6K2ZE-7tyg4yt2.js +476 -0
- package/dist/gallery/frame/chunks/diagram-7IWD3JNH-y4eng9sc.js +526 -0
- package/dist/gallery/frame/chunks/diagram-B4RE2ZJO-rn3p7j5h.js +673 -0
- package/dist/gallery/frame/chunks/diagram-LBJQPF4R-w4a2cm9q.js +255 -0
- package/dist/gallery/frame/chunks/diagram-Q27KOJAE-aqc2v7wy.js +575 -0
- package/dist/gallery/frame/chunks/diagram-UB23O5K3-xncy8nyd.js +355 -0
- package/dist/gallery/frame/chunks/ebnfDiagram-BXEA7PRR-acwgxz16.js +175 -0
- package/dist/gallery/frame/chunks/erDiagram-JOGREHBK-mzp5nswt.js +1332 -0
- package/dist/gallery/frame/chunks/eventmodeling-45OFAUF4-sg07qr0c.js +10 -0
- package/dist/gallery/frame/chunks/flowDiagram-UKHOOZJN-2e5ygs44.js +29 -0
- package/dist/gallery/frame/chunks/ganttDiagram-PKOTCBZU-p2hhh7hr.js +2620 -0
- package/dist/gallery/frame/chunks/gitGraph-TEB2WS4Q-j6kw0naa.js +10 -0
- package/dist/gallery/frame/chunks/gitGraphDiagram-DS77QQ5N-4z9z8wjm.js +1347 -0
- package/dist/gallery/frame/chunks/info-DKCQHKI2-mpt80jd3.js +10 -0
- package/dist/gallery/frame/chunks/infoDiagram-6WML65LV-5rqm3mgg.js +65 -0
- package/dist/gallery/frame/chunks/ishikawaDiagram-WSZJBQD7-b51z96jy.js +975 -0
- package/dist/gallery/frame/chunks/journeyDiagram-NVQOT4AX-9jbfp0pk.js +1272 -0
- package/dist/gallery/frame/chunks/kanban-definition-27J2QSJJ-4x0vx4se.js +1116 -0
- package/dist/gallery/frame/chunks/katex-xfgk95tv.js +14034 -0
- package/dist/gallery/frame/chunks/markdown-1s5vwkf2.js +9 -0
- package/dist/gallery/frame/chunks/markdown-2h6bwsms.js +3239 -0
- package/dist/gallery/frame/chunks/markdown-2x113yh8.js +107 -0
- package/dist/gallery/frame/chunks/markdown-30b7rwmp.js +1943 -0
- package/dist/gallery/frame/chunks/markdown-356bfj4g.js +2031 -0
- package/dist/gallery/frame/chunks/markdown-44n9ea69.js +2406 -0
- package/dist/gallery/frame/chunks/markdown-56enrf2r.js +39 -0
- package/dist/gallery/frame/chunks/markdown-5avymm8t.js +725 -0
- package/dist/gallery/frame/chunks/markdown-6r4svars.js +33 -0
- package/dist/gallery/frame/chunks/markdown-6zz8efx2.js +24 -0
- package/dist/gallery/frame/chunks/markdown-7jp6prnb.js +19 -0
- package/dist/gallery/frame/chunks/markdown-809k7q41.js +55 -0
- package/dist/gallery/frame/chunks/markdown-82xmzhzd.js +1076 -0
- package/dist/gallery/frame/chunks/markdown-92n37gd4.js +7627 -0
- package/dist/gallery/frame/chunks/markdown-ag2sssf8.js +36 -0
- package/dist/gallery/frame/chunks/markdown-b4s7ntq2.js +19 -0
- package/dist/gallery/frame/chunks/markdown-c9k0fvp1.js +100 -0
- package/dist/gallery/frame/chunks/markdown-ceefymn8.js +125 -0
- package/dist/gallery/frame/chunks/markdown-d1wfbs70.js +243 -0
- package/dist/gallery/frame/chunks/markdown-dy5sgjhc.js +11022 -0
- package/dist/gallery/frame/chunks/markdown-dz3z5w30.js +30868 -0
- package/dist/gallery/frame/chunks/markdown-eg6ngz7y.js +30340 -0
- package/dist/gallery/frame/chunks/markdown-ff5wvb43.js +1149 -0
- package/dist/gallery/frame/chunks/markdown-ffg06nfz.js +86 -0
- package/dist/gallery/frame/chunks/markdown-fvfncadf.js +472 -0
- package/dist/gallery/frame/chunks/markdown-gvyszqpv.js +19 -0
- package/dist/gallery/frame/chunks/markdown-gx1p0ew0.js +88 -0
- package/dist/gallery/frame/chunks/markdown-gxye5frz.js +65 -0
- package/dist/gallery/frame/chunks/markdown-h7pzv34e.js +105 -0
- package/dist/gallery/frame/chunks/markdown-hab789t9.js +47 -0
- package/dist/gallery/frame/chunks/markdown-hjp6sj8t.js +125 -0
- package/dist/gallery/frame/chunks/markdown-jka64qgp.js +1993 -0
- package/dist/gallery/frame/chunks/markdown-k8xkn8mw.js +56 -0
- package/dist/gallery/frame/chunks/markdown-m7frq8x8.js +112 -0
- package/dist/gallery/frame/chunks/markdown-p740v4qm.js +36 -0
- package/dist/gallery/frame/chunks/markdown-pxfeh231.js +57 -0
- package/dist/gallery/frame/chunks/markdown-pzgd8z3s.js +87 -0
- package/dist/gallery/frame/chunks/markdown-qx3cngz2.js +1673 -0
- package/dist/gallery/frame/chunks/markdown-rawhfvvc.js +85 -0
- package/dist/gallery/frame/chunks/markdown-rd8w0ng9.js +49 -0
- package/dist/gallery/frame/chunks/markdown-rm2yxfjj.js +2773 -0
- package/dist/gallery/frame/chunks/markdown-s1jatrnk.js +993 -0
- package/dist/gallery/frame/chunks/markdown-sgfg5vvq.js +59 -0
- package/dist/gallery/frame/chunks/markdown-t4ethv9d.js +409 -0
- package/dist/gallery/frame/chunks/markdown-tnmabqvz.js +36 -0
- package/dist/gallery/frame/chunks/markdown-tvdpppw8.js +5292 -0
- package/dist/gallery/frame/chunks/markdown-v0x27twr.js +22 -0
- package/dist/gallery/frame/chunks/markdown-v14td68x.js +36 -0
- package/dist/gallery/frame/chunks/markdown-w6sgd54z.js +36 -0
- package/dist/gallery/frame/chunks/markdown-xw8dsk8b.js +2125 -0
- package/dist/gallery/frame/chunks/markdown-y18mve73.js +85 -0
- package/dist/gallery/frame/chunks/markdown-yengsms5.js +427 -0
- package/dist/gallery/frame/chunks/markdown-z1wc0v0d.js +1826 -0
- package/dist/gallery/frame/chunks/markdown-zazx9h4n.js +329 -0
- package/dist/gallery/frame/chunks/mermaid-de7p4v0x.js +30 -0
- package/dist/gallery/frame/chunks/mindmap-definition-FAOFIHXS-ra44cbem.js +1216 -0
- package/dist/gallery/frame/chunks/packet-7NZHBO7P-xppm5913.js +10 -0
- package/dist/gallery/frame/chunks/pegDiagram-VL7TDLO6-a2a4wg03.js +163 -0
- package/dist/gallery/frame/chunks/pie-RZYD4A2V-hnbxk152.js +10 -0
- package/dist/gallery/frame/chunks/pieDiagram-7S7Q4E2Y-f1vxjtmx.js +311 -0
- package/dist/gallery/frame/chunks/quadrantDiagram-CIZ2JOQS-ywafa6kh.js +1390 -0
- package/dist/gallery/frame/chunks/radar-I7S5WNFK-ewqd27se.js +10 -0
- package/dist/gallery/frame/chunks/railroad-3IZDKUUU-5hy4bdx3.js +10 -0
- package/dist/gallery/frame/chunks/railroad-abnf-AHOZXSZD-bh5sxz0d.js +10 -0
- package/dist/gallery/frame/chunks/railroad-ebnf-EBAXGLYW-6b9c3zeh.js +10 -0
- package/dist/gallery/frame/chunks/railroad-peg-LSFZ7HO6-avc8v39s.js +10 -0
- package/dist/gallery/frame/chunks/railroadDiagram-AXF67PYL-qc61wax4.js +131 -0
- package/dist/gallery/frame/chunks/requirementDiagram-LRYGKXZP-9bs4786c.js +1295 -0
- package/dist/gallery/frame/chunks/sankeyDiagram-W5VNT64P-awt1em8b.js +1339 -0
- package/dist/gallery/frame/chunks/sequenceDiagram-SI44F4Z6-qaf9mz3t.js +4324 -0
- package/dist/gallery/frame/chunks/sizeCapture-X5ZJPWSS-9fq685xd.js +64 -0
- package/dist/gallery/frame/chunks/stateDiagram-OKZ733FA-vx7a7hh0.js +456 -0
- package/dist/gallery/frame/chunks/stateDiagram-v2-UEYNNEHI-x4m6ws5t.js +46 -0
- package/dist/gallery/frame/chunks/swimlanes-SLNWSIFB-e4mxm1gh.js +8287 -0
- package/dist/gallery/frame/chunks/swimlanesDiagram-ULZ7WXOC-0h93e2qq.js +42 -0
- package/dist/gallery/frame/chunks/timeline-definition-Z64GVDOM-wp0bhbzg.js +1549 -0
- package/dist/gallery/frame/chunks/treeView-QDETBFTQ-hcx7n0vt.js +10 -0
- package/dist/gallery/frame/chunks/treemap-6X3UGDF4-av02skdw.js +10 -0
- package/dist/gallery/frame/chunks/vennDiagram-T6HMQDX7-pmvk1p7n.js +2566 -0
- package/dist/gallery/frame/chunks/wardley-OPB4EBWU-njsp6e40.js +10 -0
- package/dist/gallery/frame/chunks/wardleyDiagram-T6FBY63Y-eggmnv4z.js +979 -0
- package/dist/gallery/frame/chunks/xychartDiagram-ELKLHX3M-q40tnpe5.js +1954 -0
- package/dist/gallery/frame/frame.css +183 -0
- package/dist/gallery/frame/runtime/chart.js +42744 -0
- package/dist/gallery/frame/runtime/html.js +14 -0
- package/dist/gallery/frame/runtime/markdown.js +1403 -0
- package/dist/gallery/frame/runtime/mermaid.js +32 -0
- package/dist/gallery/frame/runtime/svg.js +14 -0
- package/dist/gallery/frame/runtime/tsx.js +52 -0
- package/dist/gallery/index.html +104 -0
- package/docs/reference/cli.md +163 -0
- package/docs/reference/export.md +106 -0
- package/docs/reference/html.md +215 -0
- package/docs/reference/http.md +85 -0
- package/docs/reference/mcp.md +78 -0
- package/docs/reference/security.md +119 -0
- package/docs/reference/storage.md +93 -0
- package/docs/reference/tsx.md +101 -0
- package/docs/reference/validation.md +212 -0
- package/package.json +89 -0
- package/scripts/build-gallery.ts +127 -0
- package/scripts/launch-netns.sh +67 -0
- package/skills/facet/SKILL.md +100 -0
- package/src/cli/client.ts +311 -0
- package/src/cli/commands/create.ts +49 -0
- package/src/cli/commands/doctor.ts +249 -0
- package/src/cli/commands/export.ts +179 -0
- package/src/cli/commands/instantiate.ts +35 -0
- package/src/cli/commands/list.ts +49 -0
- package/src/cli/commands/open.ts +75 -0
- package/src/cli/commands/pin.ts +39 -0
- package/src/cli/commands/promote.ts +49 -0
- package/src/cli/commands/publish.ts +122 -0
- package/src/cli/commands/read-back.ts +56 -0
- package/src/cli/commands/status.ts +114 -0
- package/src/cli/commands/watch.ts +146 -0
- package/src/cli/main.ts +522 -0
- package/src/cli/output.ts +106 -0
- package/src/cli/parser.ts +376 -0
- package/src/cli/presenter.ts +212 -0
- package/src/cli/service-metadata.ts +134 -0
- package/src/cli/spawn-service.ts +326 -0
- package/src/css.d.ts +4 -0
- package/src/gallery-web/app.ts +1581 -0
- package/src/gallery-web/export-menu.ts +121 -0
- package/src/gallery-web/export.ts +112 -0
- package/src/gallery-web/favicon.ts +54 -0
- package/src/gallery-web/frame/entries/chart.ts +6 -0
- package/src/gallery-web/frame/entries/html.ts +6 -0
- package/src/gallery-web/frame/entries/markdown.ts +6 -0
- package/src/gallery-web/frame/entries/mermaid.ts +6 -0
- package/src/gallery-web/frame/entries/svg.ts +6 -0
- package/src/gallery-web/frame/entries/tsx.ts +6 -0
- package/src/gallery-web/frame/frame-payload.ts +91 -0
- package/src/gallery-web/frame/renderer-validation.ts +13 -0
- package/src/gallery-web/frame/renderers/chart.ts +182 -0
- package/src/gallery-web/frame/renderers/dompurify-shim.ts +112 -0
- package/src/gallery-web/frame/renderers/html.ts +48 -0
- package/src/gallery-web/frame/renderers/markdown.ts +81 -0
- package/src/gallery-web/frame/renderers/mermaid.ts +120 -0
- package/src/gallery-web/frame/renderers/registry.ts +260 -0
- package/src/gallery-web/frame/renderers/svg.ts +393 -0
- package/src/gallery-web/frame/renderers/tsx.ts +61 -0
- package/src/gallery-web/frame/runtime.ts +501 -0
- package/src/gallery-web/frame/styles/artifact.css +8424 -0
- package/src/gallery-web/frame/styles/frame.css +183 -0
- package/src/gallery-web/frame/styles/html-source.css +23 -0
- package/src/gallery-web/frame/view-box.ts +33 -0
- package/src/gallery-web/frame-html.ts +72 -0
- package/src/gallery-web/index.html +104 -0
- package/src/gallery-web/session.ts +107 -0
- package/src/gallery-web/sse-client.ts +212 -0
- package/src/gallery-web/styles/app.css +420 -0
- package/src/gallery-web/styles/tokens.css +112 -0
- package/src/gallery-web/styles/verdict.css +186 -0
- package/src/gallery-web/swap.ts +64 -0
- package/src/gallery-web/theme.ts +40 -0
- package/src/gallery-web/view-state.ts +93 -0
- package/src/harness-adapters/claude-code/README.md +10 -0
- package/src/harness-adapters/claude-code/facet.sh +4 -0
- package/src/harness-adapters/codex/README.md +10 -0
- package/src/harness-adapters/codex/facet.sh +4 -0
- package/src/harness-adapters/mcp/README.md +13 -0
- package/src/harness-adapters/mcp/cli-bridge.ts +216 -0
- package/src/harness-adapters/mcp/main.ts +15 -0
- package/src/harness-adapters/mcp/server.ts +164 -0
- package/src/harness-adapters/mcp/tool-schemas.ts +53 -0
- package/src/harness-adapters/opencode/README.md +10 -0
- package/src/harness-adapters/opencode/facet.sh +4 -0
- package/src/runtime/compiled-entrypoints.ts +69 -0
- package/src/service/dispatcher.ts +717 -0
- package/src/service/export.ts +218 -0
- package/src/service/http-utils.ts +55 -0
- package/src/service/lexical/expectations.ts +210 -0
- package/src/service/lifecycle/idle-controller.ts +125 -0
- package/src/service/lifecycle/orphan-cleanup.ts +128 -0
- package/src/service/lifecycle/process-lock.ts +235 -0
- package/src/service/main.ts +136 -0
- package/src/service/process.ts +240 -0
- package/src/service/router-guards.ts +239 -0
- package/src/service/router.ts +627 -0
- package/src/service/security/auth.ts +168 -0
- package/src/service/security/host-origin.ts +138 -0
- package/src/service/security/http-guards.ts +13 -0
- package/src/service/security/leases.ts +182 -0
- package/src/service/security/token-store.ts +138 -0
- package/src/service/server.ts +356 -0
- package/src/service/store/database.ts +61 -0
- package/src/service/store/evidence-retention.ts +234 -0
- package/src/service/store/migrations.ts +167 -0
- package/src/service/store/repository-lifecycle.ts +121 -0
- package/src/service/store/repository.ts +732 -0
- package/src/service/store/schema.ts +218 -0
- package/src/service/stored-verdict.ts +97 -0
- package/src/service/stream.ts +301 -0
- package/src/service/verdict-enrichment.ts +45 -0
- package/src/shared/build/frame-bundle-plugins.ts +61 -0
- package/src/shared/build-mode.ts +28 -0
- package/src/shared/config/evidence-read.ts +98 -0
- package/src/shared/config/limits.ts +56 -0
- package/src/shared/config/paths.ts +96 -0
- package/src/shared/contracts/artifact-types.ts +2 -0
- package/src/shared/contracts/artifact.ts +98 -0
- package/src/shared/contracts/commands/_shared.ts +59 -0
- package/src/shared/contracts/commands/guards.ts +86 -0
- package/src/shared/contracts/commands/index.ts +204 -0
- package/src/shared/contracts/commands/names.ts +35 -0
- package/src/shared/contracts/commands/requests.ts +149 -0
- package/src/shared/contracts/commands/results.ts +200 -0
- package/src/shared/contracts/envelope.ts +140 -0
- package/src/shared/contracts/events.ts +41 -0
- package/src/shared/contracts/observed-counts.ts +19 -0
- package/src/shared/contracts/renderers.ts +6 -0
- package/src/shared/contracts/validation.ts +296 -0
- package/src/shared/errors/facet-error.ts +127 -0
- package/src/shared/errors/store-error.ts +68 -0
- package/src/shared/evidence-image.ts +37 -0
- package/src/shared/export.ts +31 -0
- package/src/shared/html/policy.ts +108 -0
- package/src/shared/html/style-vocabulary.ts +352 -0
- package/src/shared/logging/logger.ts +108 -0
- package/src/shared/logging/redact.ts +49 -0
- package/src/shared/security/frozen-csp.ts +13 -0
- package/src/shared/storage-version.ts +2 -0
- package/src/shared/tsx/execution.ts +29 -0
- package/src/shared/tsx/import-policy.ts +94 -0
- package/src/shared/util/dir-permissions.ts +94 -0
- package/src/shared/util/mermaid-nodes.ts +230 -0
- package/src/shared/util/process.ts +67 -0
- package/src/shared/util/time.ts +21 -0
- package/src/shared/version.ts +3 -0
- package/src/validation/sandbox/limits.ts +41 -0
- package/src/validation/sandbox/netns.ts +177 -0
- package/src/validation/tier0/chart.ts +177 -0
- package/src/validation/tier0/dom-shim.ts +54 -0
- package/src/validation/tier0/html.ts +408 -0
- package/src/validation/tier0/markdown.ts +285 -0
- package/src/validation/tier0/mermaid.ts +98 -0
- package/src/validation/tier0/runner.ts +504 -0
- package/src/validation/tier0/svg.ts +321 -0
- package/src/validation/tier0/tsx/allowlist-resolver.ts +77 -0
- package/src/validation/tier0/tsx/ast-policy-capabilities.ts +238 -0
- package/src/validation/tier0/tsx/ast-policy-shared.ts +71 -0
- package/src/validation/tier0/tsx/ast-policy.ts +181 -0
- package/src/validation/tier0/tsx/compiler.ts +156 -0
- package/src/validation/tier0/worker-dispatch.ts +211 -0
- package/src/validation/tier0/worker-entry.ts +96 -0
- package/src/validation/tier0/worker-input.ts +75 -0
- package/src/validation/tier1/browser-process.ts +147 -0
- package/src/validation/tier1/cdp-pipe.ts +281 -0
- package/src/validation/tier1/entries/chart.ts +5 -0
- package/src/validation/tier1/entries/html.ts +6 -0
- package/src/validation/tier1/entries/markdown.ts +5 -0
- package/src/validation/tier1/entries/mermaid.ts +5 -0
- package/src/validation/tier1/entries/svg.ts +5 -0
- package/src/validation/tier1/entries/tsx.ts +5 -0
- package/src/validation/tier1/frame-target.ts +162 -0
- package/src/validation/tier1/harness-entry.ts +261 -0
- package/src/validation/tier1/harness.ts +215 -0
- package/src/validation/tier1/isolated-probe.ts +94 -0
- package/src/validation/tier1/launcher.ts +177 -0
- package/src/validation/tier1/limits.ts +80 -0
- package/src/validation/tier1/nonce.ts +17 -0
- package/src/validation/tier1/protocol-probe.ts +519 -0
- package/src/validation/tier1/runner.ts +1122 -0
- package/src/validation/tier1/verdict.ts +246 -0
- package/src/validation/tier1/webp.ts +69 -0
- package/templates/README.md +88 -0
- package/templates/bar-compare.vl.json +63 -0
- package/templates/capacity-report.tsx +102 -0
- package/templates/decision-record.md +24 -0
- package/templates/deployment-state.mmd +40 -0
- package/templates/exemplar.md +91 -0
- package/templates/facet-story.tsx +799 -0
- package/templates/fleet-dashboard.html +141 -0
- package/templates/html-release-ledger.html +91 -0
- package/templates/html-status-report.html +114 -0
- package/templates/incident-console.tsx +147 -0
- package/templates/legion-boundaries.mmd +23 -0
- package/templates/legion-flow.mmd +15 -0
- package/templates/legion-sequence.mmd +14 -0
- package/templates/legion-state.mmd +7 -0
- package/templates/metric-card.svg +29 -0
- package/templates/observability-map.svg +101 -0
- package/templates/pipeline-audit.md +108 -0
- package/templates/release-metrics.vl.json +98 -0
- package/templates/service-topology.mmd +47 -0
- package/templates/status-report.md +32 -0
- package/templates/system-map.svg +53 -0
- package/templates/timeseries.vl.json +71 -0
- package/templates/tsx-interactive-counter.tsx +19 -0
- 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
|
+
}
|