spexcode 0.3.0 → 0.4.1

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 (59) hide show
  1. package/package.json +1 -1
  2. package/spec-cli/src/anchors.ts +341 -0
  3. package/spec-cli/src/cli.ts +20 -20
  4. package/spec-cli/src/gateway.ts +8 -1
  5. package/spec-cli/src/git.ts +21 -13
  6. package/spec-cli/src/graph.ts +13 -7
  7. package/spec-cli/src/guide.ts +63 -15
  8. package/spec-cli/src/harness.ts +85 -34
  9. package/spec-cli/src/help.ts +33 -17
  10. package/spec-cli/src/index.ts +39 -7
  11. package/spec-cli/src/init.ts +5 -4
  12. package/spec-cli/src/lint.ts +103 -28
  13. package/spec-cli/src/localIssues.ts +19 -0
  14. package/spec-cli/src/migrate-table.ts +27 -16
  15. package/spec-cli/src/search.bench.mjs +2 -2
  16. package/spec-cli/src/session-timeline.ts +148 -0
  17. package/spec-cli/src/sessions.ts +29 -4
  18. package/spec-cli/src/specs.ts +44 -21
  19. package/spec-cli/templates/hooks/prepare-commit-msg +6 -1
  20. package/spec-cli/templates/spec/project/.plugins/{extract → commands/extract}/spec.md +1 -1
  21. package/spec-cli/templates/spec/project/.plugins/commands/spec.md +16 -0
  22. package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
  23. package/spec-cli/templates/spec/project/.plugins/core/spec.md +1 -1
  24. package/spec-cli/templates/spec/project/.plugins/prompts/spec.md +20 -0
  25. package/spec-cli/templates/spec/project/.plugins/skills/spec.md +17 -0
  26. package/spec-cli/templates/spec/project/.plugins/spec.md +5 -2
  27. package/spec-cli/templates/spec/project/spec.md +1 -1
  28. package/spec-dashboard/dist/assets/{Dashboard-C7Bzsv86.js → Dashboard-C_fGmOKK.js} +3 -3
  29. package/spec-dashboard/dist/assets/EvalsPage-Cnr1s3bq.js +2 -0
  30. package/spec-dashboard/dist/assets/{FoldToggle-D5iB4Ac2.js → FoldToggle-x9gtO1OQ.js} +1 -1
  31. package/spec-dashboard/dist/assets/{IssuesPage-CMFTsQhg.js → IssuesPage-5f_vL-JV.js} +1 -1
  32. package/spec-dashboard/dist/assets/MobileApp-DEO1jgGM.js +1 -0
  33. package/spec-dashboard/dist/assets/SessionInterface-CAlbMOFR.js +66 -0
  34. package/spec-dashboard/dist/assets/SessionWindow-JYbpPwNB.js +13 -0
  35. package/spec-dashboard/dist/assets/{Settings-BW5f0OaW.js → Settings-DKb5Ji_X.js} +1 -1
  36. package/spec-dashboard/dist/assets/index-BQu-oJ8J.js +41 -0
  37. package/spec-dashboard/dist/assets/index-BbMkwuix.css +1 -0
  38. package/spec-dashboard/dist/assets/launch-BM9GgvkX.js +6 -0
  39. package/spec-dashboard/dist/index.html +2 -2
  40. package/spec-eval/src/cli.ts +82 -21
  41. package/spec-eval/src/evaltab.ts +15 -6
  42. package/spec-eval/src/humanok.ts +43 -0
  43. package/spec-eval/src/scenarios.ts +116 -4
  44. package/spec-eval/src/sidecar.ts +35 -9
  45. package/spec-cli/templates/presets/careful/.plugins/clarify-before-code/spec.md +0 -11
  46. package/spec-cli/templates/spec/project/.plugins/supervisor/spec.md +0 -8
  47. package/spec-dashboard/dist/assets/EvalsPage-DKZZIdHq.js +0 -2
  48. package/spec-dashboard/dist/assets/MobileApp-DwuTKgdP.js +0 -1
  49. package/spec-dashboard/dist/assets/SessionInterface-CBS5_cmK.js +0 -71
  50. package/spec-dashboard/dist/assets/SessionWindow-CqAnjWfI.js +0 -9
  51. package/spec-dashboard/dist/assets/index-Cc26X4ce.css +0 -1
  52. package/spec-dashboard/dist/assets/index-Ce0wDyQS.js +0 -41
  53. /package/spec-cli/templates/spec/project/.plugins/{regroup → commands/regroup}/spec.md +0 -0
  54. /package/spec-cli/templates/spec/project/.plugins/{tidy → commands/tidy}/spec.md +0 -0
  55. /package/spec-cli/templates/spec/project/.plugins/{forge-link → prompts/forge-link}/spec.md +0 -0
  56. /package/spec-cli/templates/spec/project/.plugins/{memory-hygiene → prompts/memory-hygiene}/spec.md +0 -0
  57. /package/spec-cli/templates/spec/project/.plugins/{reproduce-before-fix → prompts/reproduce-before-fix}/spec.md +0 -0
  58. /package/spec-cli/templates/spec/project/.plugins/{distill → skills/distill}/digest.mjs +0 -0
  59. /package/spec-cli/templates/spec/project/.plugins/{distill → skills/distill}/spec.md +0 -0
@@ -0,0 +1 @@
1
+ :root{--paper: #fdf6e3;--panel: #f4eeda;--panel2:#eee8d5;--line: #ded7bf;--ink: #586e75;--ink2: #073642;--muted: #93a1a1;--blue: #268bd2;--green: #859900;--orange:#cb4b16;--red: #dc322f;--yellow:#b58900;--magenta:#d33682;--cyan: #2aa198;--term-bg: #0d1117;--mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace}:root[data-theme=dark]{--paper: #0d1117;--panel: #161b22;--panel2:#21262d;--line: #30363d;--ink: #c9d1d9;--ink2: #f0f6fc;--muted: #8b949e;--blue: #58a6ff;--green: #3fb950;--orange:#f0883e;--red: #f85149;--yellow:#d29922;--magenta:#bc8cff;--cyan: #39c5cf;--term-bg: #0d1117}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--paper);color:var(--ink);font-family:var(--mono);font-size:13px;-webkit-font-smoothing:antialiased}*{scrollbar-color:var(--line) transparent;scrollbar-width:thin}::-webkit-scrollbar{width:9px;height:9px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--line);border-radius:6px;border:2px solid var(--panel)}::-webkit-scrollbar-thumb:hover{background:var(--muted)}::-webkit-scrollbar-corner{background:transparent}.app{display:flex;height:100vh}.app-main{flex:1;min-width:0;display:flex;position:relative}.page-graph{flex:1;min-width:0;display:grid;grid-template-columns:1fr var(--fp-w, 250px);position:relative}.side-rail{flex:0 0 52px;display:flex;flex-direction:column;align-items:center;gap:6px;padding:10px 0;background:var(--panel);border-right:1px solid var(--line)}.rail-spacer{flex:1}.rail-btn{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:none;border:none;border-radius:9px;cursor:pointer;color:var(--muted)}.rail-btn:hover{background:var(--panel2);color:var(--ink2)}.rail-btn.on{background:color-mix(in srgb,var(--blue) 14%,transparent);color:var(--blue)}.page-pane{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--paper)}.page-evals,.page-issues{overflow:hidden;padding:0 14px 10px 0}.page-settings{overflow:auto;flex-direction:row;justify-content:center}.settings-body{width:min(720px,94%);padding:26px 0 60px}.page-title{font-size:17px;font-weight:700;color:var(--ink2);margin:6px 2px 14px}.pane-resizer{cursor:col-resize;z-index:6;position:relative}.pane-resizer:after{content:"";position:absolute;top:0;bottom:0;left:50%;width:3px;margin-left:-1.5px;background:transparent;border-radius:2px}.pane-resizer:hover:after,body.is-resizing .pane-resizer:after{background:color-mix(in srgb,var(--blue) 55%,transparent)}.page-graph>.pane-resizer{position:absolute;top:0;bottom:0;width:9px;right:calc(var(--fp-w, 250px) - 4px)}.si-resizer{flex:0 0 7px;margin:0 -3.5px}body.is-resizing,body.is-resizing *{cursor:col-resize!important;-webkit-user-select:none!important;user-select:none!important}.app.kbd-mode,.app.kbd-mode *{cursor:none!important}.app.kbd-mode .react-flow,.app.kbd-mode .react-flow *{pointer-events:none!important}.graph{position:relative;background:var(--paper);border-right:1px solid var(--line)}.react-flow__attribution{display:none}.react-flow__edge-path{stroke-dasharray:0}.move-edge .react-flow__edge-path{stroke-linecap:round}.hud{position:absolute;top:16px;left:18px;z-index:5;display:flex;flex-direction:row;align-items:center;gap:8px;pointer-events:none}.brand{color:var(--ink2);font-weight:700;letter-spacing:.5px}.hud .hud-help{align-self:center}.hud-help{pointer-events:auto;align-self:flex-start;cursor:pointer;width:22px;height:22px;display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:13px;line-height:1;color:var(--blue);background:var(--paper);border:1px solid var(--line);border-radius:4px}.hud-help:hover{border-color:var(--blue);color:var(--orange)}.lock-hint{position:absolute;top:16px;left:50%;transform:translate(-50%);z-index:6;display:flex;align-items:center;gap:10px;max-width:min(48vw,520px);padding:5px 8px 5px 11px;background:var(--paper);border:1px solid var(--line);border-left:4px solid var(--ov, var(--blue));border-radius:6px;box-shadow:0 6px 22px #657b8329;font-family:var(--mono);font-size:12px;color:var(--ink)}.lock-hint-lead{font-weight:700;flex:none;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lock-hint-body{color:var(--ink2);flex:1;min-width:0}.lock-hint kbd{border:1px solid var(--line);border-radius:3px;padding:0 5px;margin:0 1px;color:var(--blue);font-family:var(--mono);font-size:11px}.lock-hint-sep{color:var(--muted)}.lock-hint-release{flex:none;cursor:pointer;padding:1px 8px;background:var(--paper);border:1px solid var(--line);border-radius:4px;color:var(--muted);font-family:var(--mono);font-size:11px}.lock-hint-release:hover{border-color:var(--blue);color:var(--orange)}.graph-stats{position:absolute;bottom:14px;left:18px;z-index:5;display:flex;align-items:center;gap:5px;flex-wrap:wrap;max-width:calc(100% - 36px);padding:4px 9px;background:var(--paper);border:1px solid var(--line);border-radius:6px;box-shadow:0 4px 18px #657b831f;font-family:var(--mono);font-size:12px;color:var(--ink2)}.bstat-total{font-weight:700;color:var(--ink);padding-right:1px}.bstat{display:inline-flex;align-items:center;gap:3px;padding:1px 4px;border:0;border-radius:4px;background:none;font-family:var(--mono);font-size:12px;line-height:1.4;color:inherit;cursor:pointer}.bstat:hover:not(:disabled){background:var(--panel2);color:var(--ink)}.bstat:disabled{opacity:.38;cursor:default}.bstat-dot{width:7px;height:7px;border-radius:2px;flex:none}.bstat-drift{color:var(--yellow)}.bstat-issue{color:var(--magenta)}.bstat-sep{width:1px;align-self:stretch;margin:2px 3px;background:var(--line)}.graph-stats .score-badge{width:13px;height:13px;font-size:8px}.graph-stats .score-badge.empty{opacity:.6}.react-flow__node{transition:opacity .18s ease,transform .2s ease}.react-flow__node.is-far{opacity:.35}.react-flow__controls{box-shadow:none;border:1px solid var(--line);border-radius:4px;overflow:hidden}.react-flow__controls-button{background:var(--panel2);border-bottom:1px solid var(--line);color:var(--ink2)}.react-flow__controls-button:hover{background:var(--panel)}.react-flow__controls-button svg{fill:var(--ink)}.spec-node{position:relative;display:flex;flex-direction:column;justify-content:center;gap:3px;width:176px;min-height:46px;padding:5px 9px;background:var(--panel);border:1px solid var(--line);border-radius:4px;transition:border-color .12s}.spec-node.focused{border-color:var(--blue);outline:2px solid var(--blue);outline-offset:-1px}.spec-node.active.focused{border-color:var(--orange);outline-color:var(--orange)}.spec-node.drift.focused{border-color:var(--yellow);outline-color:var(--yellow)}.node-row1{display:flex;align-items:center;gap:7px}.node-row2{display:flex;align-items:center;gap:7px;min-height:18px}.node-dot{position:relative;width:7px;height:7px;border-radius:2px;flex:none}.pulse{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:2px;animation:pulse 1.6s ease-out infinite}@keyframes pulse{0%{transform:scale(1);opacity:.6}to{transform:scale(2.6);opacity:0}}.node-title{font-size:12px;font-weight:600;color:var(--ink2);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node-ago{font-size:10px;color:var(--muted);flex:none;white-space:nowrap}.node-ver{font-size:10px;color:var(--muted);flex:none}.node-expand{position:absolute;right:-7px;top:50%;transform:translateY(-50%);display:flex;align-items:center;gap:1px;font-size:9px;font-weight:700;line-height:1;color:var(--muted);background:var(--paper);border:1px solid var(--line);border-radius:7px;padding:2px 4px;pointer-events:none}.spec-node.focused .node-expand{color:var(--blue);border-color:var(--blue)}.node-editors{display:flex;align-items:center;gap:3px}.av-more{font-size:9px;font-weight:700;color:var(--muted);margin-left:1px}.avatar{display:inline-flex;border-radius:50%;box-shadow:0 0 0 1.5px var(--panel)}.av-face{display:inline-flex;align-items:center;justify-content:center;line-height:1;font-weight:700;overflow:hidden}.av-circle{border-radius:50%}.av-rounded{border-radius:5px}.av-square{border-radius:1px}.av-hex{border-radius:50%/18%;clip-path:polygon(25% 5%,75% 5%,100% 50%,75% 95%,25% 95%,0 50%)}.av-st-working{box-shadow:0 0 0 1.5px var(--panel),0 0 0 3px var(--green)}.av-st-idle{box-shadow:0 0 0 1.5px var(--panel),0 0 0 3px var(--muted)}.av-st-offline{box-shadow:0 0 0 1.5px var(--panel),0 0 0 3px var(--line);opacity:.55}.react-flow .react-flow__handle{opacity:0;pointer-events:none;background:transparent;border:none;width:5px;height:5px;min-width:0;min-height:0;border-radius:1px}.side{padding:18px 18px 24px;overflow-y:auto;background:var(--panel);border-left:1px solid var(--line)}.trail{display:flex;flex-wrap:wrap;align-items:center;gap:2px;margin-bottom:16px;font-size:11px}.trail .sep{color:var(--muted);margin:0 3px}.crumb{background:none;border:none;padding:1px 3px;cursor:pointer;color:var(--muted);font-family:var(--mono);font-size:11px}.crumb:hover{color:var(--blue)}.crumb.here{color:var(--ink2);font-weight:700}.side h1{font-size:17px;color:var(--ink2);margin:0 0 8px}.side h2{font-size:11px;color:var(--muted);margin:24px 0 10px;font-weight:500}.side h2:first-child{margin-top:0}.stats{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:4px;overflow:hidden}.stats div{display:flex;justify-content:space-between;font-size:12px;padding:7px 10px}.stats div:nth-child(odd){background:var(--panel2)}.stats span{color:var(--muted)}.stats b{color:var(--ink2);font-weight:600}.c-merged{color:var(--green)}.c-active{color:var(--orange)}.c-drift{color:var(--yellow)}.c-pending{color:var(--muted)}.badge{display:inline-block;font-size:11px;padding:2px 0;border:none;background:none}.badge.merged{color:var(--green)}.badge.active{color:var(--orange)}.badge.drift{color:var(--yellow)}.badge.pending{color:var(--muted)}.desc{color:var(--ink);line-height:1.55;font-size:12.5px;margin:12px 0 4px}.meta{margin-top:14px;display:flex;flex-direction:column;gap:0;border:1px solid var(--line);border-radius:4px;overflow:hidden}.meta div{display:flex;justify-content:space-between;font-size:12px;padding:7px 10px}.meta div:nth-child(odd){background:var(--panel2)}.meta span{color:var(--muted)}.meta b{color:var(--ink2);font-weight:600}.peek-btn{margin-top:22px;width:100%;padding:9px;background:var(--paper);color:var(--blue);border:1px solid var(--blue);border-radius:4px;font-family:var(--mono);font-size:13px;font-weight:600;cursor:pointer}.peek-btn:hover:not(:disabled){background:var(--blue);color:var(--paper)}.peek-btn:disabled{color:var(--muted);border-color:var(--line);cursor:default}.ov-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:20;background:#657b8340;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center}.ov-panel{width:min(900px,90vw);height:min(600px,84vh);background:var(--paper);border:1px solid var(--blue);border-radius:6px;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 12px 40px #657b8340}.ov-head{display:flex;align-items:center;gap:14px;padding:7px 12px;background:var(--panel2);border-bottom:1px solid var(--line)}.ov-title{font-size:13px;font-weight:700;color:var(--ink2)}.ov-tabs{display:flex;gap:4px}.ov-tab{background:none;border:1px solid transparent;border-radius:4px;padding:3px 8px;cursor:pointer;color:var(--muted);font-family:var(--mono);font-size:12px}.ov-tab:hover{color:var(--ink2)}.ov-tab.on{color:var(--ink2);border-color:var(--blue);background:var(--paper)}.ov-tab kbd{font-family:var(--mono);font-size:10px;color:var(--blue);border:1px solid var(--line);border-radius:2px;padding:0 3px;margin-right:3px;background:var(--paper)}.ov-tab-counts{display:inline-flex;gap:3px;margin-left:5px;vertical-align:baseline}.ovc{font-family:var(--mono);font-size:9px;font-weight:700;line-height:1.5;padding:0 4px;border-radius:3px;border:1px solid var(--line)}.ovc.st-open{color:var(--green);border-color:color-mix(in srgb,var(--green) 45%,transparent)}.ovc.st-closed{color:var(--magenta);border-color:color-mix(in srgb,var(--magenta) 45%,transparent)}.ovc.st-edit{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 45%,transparent)}.ov-hint{margin-left:auto;font-size:11px;color:var(--muted)}.ov-body{flex:1;min-width:0;overflow:hidden;display:flex}.pane-doc{flex:1;padding:22px 26px;overflow:auto}.pane-doc h1{font-size:18px;color:var(--ink2);margin:0 0 14px}.pane-doc blockquote{margin:0 0 16px;padding:8px 14px;border-left:3px solid var(--blue);background:var(--panel2);color:var(--ink);line-height:1.6;font-size:13px}.pane-doc .doc-stat{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:0 0 16px;padding:6px 10px;background:var(--panel2);border:1px solid var(--line);border-radius:5px}.stat-status{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em}.stat-dot{width:7px;height:7px;border-radius:2px;flex:none;background:var(--muted)}.stat-status.st-merged{color:var(--green)}.stat-status.st-merged .stat-dot{background:var(--green)}.stat-status.st-active{color:var(--orange)}.stat-status.st-active .stat-dot{background:var(--orange)}.stat-status.st-drift{color:var(--yellow)}.stat-status.st-drift .stat-dot{background:var(--yellow)}.stat-status.st-pending{color:var(--muted)}.stat-status.st-pending .stat-dot{background:var(--muted)}.stat-chip{display:inline-flex;align-items:center;gap:4px;font-size:10.5px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:3px;padding:1px 6px}.stat-chip.stat-drift{color:var(--yellow);font-weight:700;border-color:color-mix(in srgb,var(--yellow) 45%,transparent)}.stat-sess{margin-left:auto;display:inline-flex;align-items:center;gap:5px;font-size:10.5px;color:var(--muted);min-width:0}.stat-sess b{color:var(--ink2);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pane-doc .doc-gov{margin:0 0 16px}.doc-gov-h{display:block;font-size:10.5px;color:var(--muted);margin-bottom:6px}.doc-gov-h b{color:var(--ink2);font-weight:700}.doc-gov-files{display:flex;flex-wrap:wrap;gap:5px}.gov-f{font-size:11px;color:var(--blue);background:var(--panel2);border:1px solid color-mix(in srgb,var(--blue) 22%,var(--line));padding:1px 7px;border-radius:3px}.doc-gov.prose .doc-gov-h{font-style:italic}.doc-body{border-top:1px solid var(--line);padding-top:14px;font-size:13px;line-height:1.65;color:var(--ink)}.doc-body p{margin:0 0 10px}.pane-loading{display:flex;align-items:center;justify-content:center;padding:48px 0}.spinner{width:22px;height:22px;border-radius:50%;border:2px solid var(--line);border-top-color:var(--ink2);animation:si-attach-spin .7s linear infinite}.doc-body ul{margin:0 0 10px;padding-left:18px}.doc-body li{margin:2px 0}.doc-body code{font-size:12px;background:var(--panel2);color:var(--ink2);padding:0 4px;border-radius:3px}.doc-body strong{color:var(--ink2)}.doc-h{font-size:12px;color:var(--ink2);font-weight:700;margin:14px 0 6px}.doc-pre{margin:0 0 10px;padding:10px 12px;background:var(--panel2);border-radius:4px;overflow-x:auto;font-size:12px;line-height:1.5}.doc-pre code{background:none;padding:0;color:var(--ink)}.doc-link{color:var(--blue)}.doc-table{width:100%;margin:0 0 12px;border-collapse:collapse;font-size:12px;display:block;overflow-x:auto}.doc-table th,.doc-table td{border:1px solid var(--line);padding:5px 9px;text-align:left;vertical-align:top}.doc-table th{background:var(--panel2);color:var(--ink2);font-weight:700}.doc-table code{white-space:nowrap}.spec-parts{display:flex;flex-direction:column;gap:14px;margin-top:14px}.spec-part{border:1px solid var(--line);border-radius:6px;border-left-width:3px;overflow:hidden}.spec-part.part-raw{border-left-color:var(--green)}.spec-part.part-expanded{border-left-color:var(--blue)}.spec-part.part-state{border-left-color:var(--orange)}.part-head{display:flex;align-items:baseline;gap:10px;padding:8px 12px;background:var(--panel2);flex-wrap:wrap}.part-title{font-size:12px;font-weight:700;color:var(--ink2);text-transform:uppercase;letter-spacing:.04em}.part-owner{font-size:10px;font-weight:700;padding:1px 7px;border-radius:9px;text-transform:uppercase;letter-spacing:.03em}.part-owner.owner-human{background:#8599002e;color:var(--green)}.part-owner.owner-agent{background:#268bd22e;color:var(--blue)}.part-note{font-size:11px;color:var(--muted);margin-left:auto}.part-body{padding:4px 14px 12px}.part-body .doc-body{border-top:none;padding-top:8px}.cs-sub+.cs-sub{margin-top:6px}.cs-label{font-size:11px;font-weight:700;color:var(--orange);text-transform:uppercase;letter-spacing:.04em;margin:12px 0 0}.ver-row{padding:10px 0;border-bottom:1px solid var(--line)}.ver-row.latest{border-left:2px solid var(--blue);padding-left:12px;margin-left:-14px}.rec-toggle{display:block;width:100%;text-align:left;background:none;border:0;padding:0;margin:0;cursor:pointer;font:inherit;color:inherit}.rec-caret{color:var(--muted);font-size:9px;width:9px;flex:none}.rec-head{display:flex;align-items:baseline;gap:10px;font-size:12px}.rec-v{color:var(--ink2);font-weight:700}.rec-hash{color:var(--muted);font-size:11px}.rec-date{color:var(--muted)}.rec-diff{margin-left:auto;display:flex;gap:8px;font-size:12px}.rec-add{color:var(--green)}.rec-del{color:var(--orange)}.rec-msg{color:var(--ink2);font-size:13px;margin-top:5px;word-break:break-word}.rec-sub{color:var(--muted);font-size:11px;margin-top:3px}.rec-msg.muted{color:var(--muted)}.rec-evidence{margin:12px 0 2px;padding:14px 0 0;border-top:1px solid var(--line)}.ev-note{font-size:11px;color:var(--muted);text-align:center;margin-top:12px}.ev-difflabel{font-size:11px;color:var(--muted);margin-bottom:8px}.ev-diff{margin:0;font-family:var(--mono);font-size:11px;line-height:1.55;background:var(--panel2);border:1px solid var(--line);border-radius:5px;padding:8px 0;overflow-x:auto}.ev-diff .dl{padding:0 12px;white-space:pre}.dl-hunk{color:var(--blue);opacity:.75}.dl-add{color:var(--green);background:#8599001a}.dl-del{color:var(--orange);background:#cb4b161a}.dl-ctx{color:var(--muted)}.pane-hist{flex:1;min-width:0;padding:16px 20px;overflow:auto}.pane-hist.empty{color:var(--muted);font-size:13px}.pane-solo{flex:1;min-width:0;display:flex}.spec-node.has-overlay{border-color:var(--ov);box-shadow:0 0 0 2px color-mix(in srgb,var(--ov) 22%,transparent)}.spec-node.has-overlay.ov-dirty{border-style:dashed}.spec-node.has-overlay.focused{outline-color:var(--ov)}.spec-node.ghost{background:transparent;border-style:dashed;opacity:.72}.spec-node.deleted .node-title{text-decoration:line-through;color:var(--muted)}.drift-badge{flex:none;font-size:10px;font-weight:700;color:var(--yellow);border:1px solid var(--yellow);border-radius:3px;padding:0 3px;line-height:1.4}.issue-badge{flex:none;font-size:10px;font-weight:700;color:var(--magenta);border:1px solid var(--magenta);border-radius:3px;padding:0 3px;line-height:1.4;cursor:default}.score-badge{flex:none;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;border:1.5px solid currentColor;font-size:9px;font-weight:900;line-height:1;cursor:default}.score-badge.pass{color:var(--green)}.score-badge.fail{color:var(--red)}.score-badge.stalePass,.score-badge.staleFail{color:var(--muted)}.score-badge.empty{color:var(--muted);opacity:.45}.scenario-count{flex:none;font-family:var(--mono);font-size:10px;font-weight:700;line-height:1.4;border:1px solid currentColor;border-radius:3px;padding:0 3px;cursor:default}.scenario-count.pass{color:var(--green)}.scenario-count.fail{color:var(--red)}.scenario-count.stalePass,.scenario-count.staleFail{color:var(--muted)}.scenario-count.empty{color:var(--muted);opacity:.7}.focus-panel{overflow:auto;background:var(--paper);display:flex;flex-direction:column;gap:18px;padding:16px 14px}.fp-head{display:flex;flex-direction:column;gap:3px}.fp-title{font-size:15px;font-weight:700;color:var(--ink2);word-break:break-word;line-height:1.25}.fp-desc{color:var(--ink);font-size:11px;line-height:1.4;margin-top:3px;display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}.fp-sec{display:flex;flex-direction:column;gap:5px}.fp-sec-head{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);border-bottom:1px solid var(--line);padding-bottom:5px}.fp-count{font-family:var(--mono);font-size:10px}.fp-count.pass{color:var(--green)}.fp-count.fail{color:var(--red)}.fp-count.stalePass,.fp-count.staleFail,.fp-count.empty{color:var(--muted)}.fp-empty{color:var(--muted);font-size:11px;font-style:italic;padding:3px 2px}.fp-scenario{position:relative;display:block;width:100%;padding:6px;text-align:left;background:none;border:none;border-radius:5px;font:inherit;color:inherit;cursor:pointer}.fp-scenario:hover{background:var(--panel2)}.fp-sc-mark{position:absolute;left:6px;top:6px;width:12px;text-align:center;font-weight:900;font-size:11px;line-height:1.5}.fp-scenario.pass .fp-sc-mark{color:var(--green)}.fp-scenario.fail .fp-sc-mark{color:var(--red)}.fp-scenario.stalePass .fp-sc-mark,.fp-scenario.staleFail .fp-sc-mark{color:var(--muted)}.fp-scenario.empty .fp-sc-mark,.fp-scenario.missing .fp-sc-mark{color:var(--muted);opacity:.55}.fp-sc-body{display:flex;flex-direction:column;gap:3px;min-width:0}.fp-sc-name{padding-left:20px;font-family:var(--mono);font-size:11px;font-weight:700;color:var(--ink2)}.fp-sc-expected{color:var(--ink);font-size:11px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.fp-sc-code{color:var(--muted);font-family:var(--mono);font-size:9px;opacity:.85}.tag-chips{display:inline-flex;flex-wrap:wrap;gap:3px;min-width:0}.tag-chip{font-family:var(--mono);font-size:9px;line-height:1.5;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:3px;padding:0 4px;white-space:nowrap}.search-item .tag-chips{flex:none}.fp-issue-counts{display:flex;gap:6px;font-size:9px}.focus-panel .issue-group-head{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:8px 2px 3px}.focus-panel .issue-card{padding:6px;border-radius:5px}.issue-card{display:block;max-width:100%;min-width:0;box-sizing:border-box;overflow:hidden;padding:5px 6px;border-radius:5px;text-decoration:none}.issue-card+.issue-card{margin-top:2px}.issue-card:hover{background:var(--panel2)}.issue-card-top{display:flex;align-items:center;gap:6px;min-width:0;margin-bottom:2px}.issue-num{font-size:10px;font-weight:700;color:var(--magenta);flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono)}.issue-state{font-size:8px;color:var(--muted);flex:none;text-transform:uppercase;letter-spacing:.04em;border:1px solid var(--line);border-radius:3px;padding:0 3px;line-height:1.5}.issue-state.st-open{color:var(--green);border-color:color-mix(in srgb,var(--green) 45%,transparent)}.issue-state.st-closed{color:var(--magenta);border-color:color-mix(in srgb,var(--magenta) 45%,transparent)}.issue-card-title{display:block;font-size:11px;line-height:1.35;color:var(--ink2);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.pane-issues{flex:1;min-width:0;padding:16px 20px;overflow:auto}.pane-issues.empty{color:var(--muted);font-size:13px}.pane-issues .issue-group-head{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:0 2px 6px}.pane-issues .issue-group-head.closed{margin-top:16px;border-top:1px solid var(--line);padding-top:14px}.pane-issues .issue-card{padding:7px 8px}.pane-issues .issue-card+.issue-card{margin-top:1px}.pane-issues .issue-card-title{font-size:12px}.pane-edit{flex:1;min-width:0;padding:16px 20px;overflow:auto}.pane-edit.empty{color:var(--muted);font-size:13px}.edit-rev{margin:0 0 18px}.edit-by{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted);margin-bottom:8px}.edit-by-label{font-weight:600}.edit-state{margin-left:auto;font-family:var(--mono);font-size:10px}.ov-marks{display:flex;gap:3px;flex:none;margin-left:2px}.ov-mark{font-size:11px;font-weight:700;line-height:1;color:var(--ov, var(--blue))}.ov-mark.ov-deleted{color:var(--orange)}.react-flow__node.ov-hot{opacity:1}.react-flow__node.ov-dim{opacity:.22}.sesswin{position:absolute;top:48px;left:16px;z-index:6;width:232px;max-height:min(80vh,calc(100vh - 112px));background:var(--paper);border:1px solid var(--line);border-radius:6px;box-shadow:0 6px 22px #657b8329;overflow-y:auto;overflow-x:hidden;font-size:12px}.sesswin-empty{padding:12px 10px;color:var(--muted);font-size:11px;line-height:1.5}.sesswin-empty kbd{border:1px solid var(--line);border-radius:3px;padding:0 4px;color:var(--blue)}.sess-row{display:flex;flex-wrap:wrap;align-items:center;column-gap:7px;row-gap:2px;width:100%;padding:7px 9px;background:none;border:none;border-bottom:1px solid var(--line);border-left:2px solid var(--ov, transparent);cursor:pointer;text-align:left;font-family:var(--mono);font-size:12px;color:var(--ink2);-webkit-user-select:none;user-select:none;transition:background .12s,border-left-width .12s}.sess-row:last-child{border-bottom:none}.sess-row:hover{background:var(--panel)}.sess-row.locked{background:color-mix(in srgb,var(--ov) 12%,var(--panel2));border-left-width:4px;color:var(--ink);font-weight:600}.sess-id{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sess-lock{font-size:10px;flex:none;line-height:1;filter:saturate(0)}.sess-meta{order:1;flex:0 0 100%;width:100%;min-width:0;margin-top:1px;display:flex;align-items:center;column-gap:7px;font-size:10px;line-height:1.3;opacity:.9;overflow:hidden}.sess-ops{color:var(--ov, var(--blue));font-weight:700;flex:none}.sess-glyph{flex:none;width:12px;text-align:center;font-size:11px;line-height:1}.sess-lead{flex:none;align-self:stretch;display:inline-flex;align-items:stretch;--rail: color-mix(in srgb, var(--muted) 42%, transparent)}.sess-rail{flex:none;width:14px;position:relative;min-height:1.7em}.sess-rail.rail:before,.sess-rail.tee:before,.sess-rail.elbow:before{content:"";position:absolute;left:50%;top:0;bottom:0;border-left:1px solid var(--rail)}.sess-rail.elbow:before{bottom:50%}.sess-rail.tee:after,.sess-rail.elbow:after{content:"";position:absolute;left:50%;right:1px;top:50%;border-top:1px solid var(--rail)}.sess-fold{flex:none;display:inline-flex;align-items:center;justify-content:center;align-self:center;cursor:pointer;-webkit-user-select:none;user-select:none}.sess-fold.pod{min-width:16px;width:auto;height:14px;padding:0 4px;border-radius:999px;border:1px solid;font-size:9.5px;font-weight:700;line-height:1;color:var(--paper)}.sess-fold.pod.open{background:transparent}.sess-fold.pod:hover{filter:brightness(1.25)}.sesswin .sess-row{flex-wrap:nowrap}.sesswin .sess-meta{flex:none;width:auto;min-width:0;margin-top:0;margin-left:auto}.sesswin-zone{--zh: var(--muted);display:flex;align-items:center;gap:7px;padding:8px 9px 2px;font-size:9.5px;font-weight:700;letter-spacing:.9px;text-transform:uppercase;-webkit-user-select:none;user-select:none;color:var(--zh)}.sesswin-zone:after{content:"";flex:1;height:1px;background:var(--zh);opacity:.25}.sesswin-zone-need{--zh: var(--yellow)}.sesswin-zone-run{--zh: var(--green)}.sesswin-zone-offline{--zh: var(--muted)}.sess-menu{position:fixed;z-index:60;min-width:116px;background:var(--paper);border:1px solid var(--line);border-radius:5px;box-shadow:0 6px 22px #657b8338;overflow:hidden;padding:3px}.sess-menu-item{display:block;width:100%;text-align:left;background:none;border:none;border-radius:3px;padding:5px 9px;cursor:pointer;font-family:var(--mono);font-size:12px;color:var(--ink2)}.sess-menu-item:hover{background:var(--blue);color:var(--paper)}.sess-menu-item.danger{color:var(--orange)}.sess-menu-item.danger:hover{background:var(--orange);color:var(--paper)}.sess-menu-sep{height:1px;margin:3px 4px;background:var(--line)}.sess-menu-item.sess-menu-sess{display:flex;align-items:center;gap:6px}.sess-menu-item.sess-menu-sess .sess-glyph{width:11px}.sess-rename-modal{width:min(360px,92vw)}.sess-rename{display:flex;flex-direction:column;gap:10px;padding:4px 2px 2px}.sess-rename-input{font-family:var(--mono);font-size:13px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:4px;padding:7px 9px}.sess-rename-input:focus{outline:none;border-color:var(--blue)}.sess-rename-actions{display:flex;justify-content:flex-end;gap:8px}.sess-rename-btn{background:var(--paper);border:1px solid var(--line);color:var(--ink);border-radius:4px;padding:4px 12px;cursor:pointer;font-family:var(--mono);font-size:12px}.sess-rename-btn:hover{background:var(--panel)}.sess-rename-save{border-color:var(--blue);color:var(--blue)}.sess-rename-save:hover{background:var(--blue);color:var(--paper)}.sess-rename-save:disabled{opacity:.5;cursor:default}.sess-confirm{display:flex;flex-direction:column;gap:12px;padding:4px 2px 2px}.sess-confirm-msg{margin:0;color:var(--ink);font-size:12px;line-height:1.4}.sess-rename-btn.danger{border-color:var(--orange);color:var(--orange)}.sess-rename-btn.danger:hover{background:var(--orange);color:var(--paper)}.sess-rename-btn.danger:disabled{opacity:.5;cursor:default}.legend-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:60;background:#657b8340;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center}.legend{position:relative;width:min(660px,92vw);max-height:86vh;overflow-y:auto;background:var(--paper);border:1px solid var(--blue);border-radius:6px;box-shadow:0 12px 40px #657b8340;font-size:12px}.legend-head{position:sticky;top:0;display:flex;align-items:center;justify-content:space-between;padding:6px 9px;background:var(--panel2);border-bottom:1px solid var(--line)}.legend-title{color:var(--muted);font-size:11px;letter-spacing:.5px}.legend-close{background:none;border:none;color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;line-height:1;padding:2px}.legend-close:hover{color:var(--orange)}.legend-body{padding:4px 10px 10px}.legend-sec{padding:8px 0;border-bottom:1px solid var(--line)}.legend-sec:last-child{border-bottom:none}.legend-h{font-size:11px;color:var(--ink2);font-weight:700;margin-bottom:6px}.legend-sub{color:var(--muted);font-weight:400}.keymap-keys{flex:none;width:132px;display:flex;flex-wrap:wrap;gap:4px}.legend kbd,.settings-body kbd{font-family:var(--mono);font-size:11px;color:var(--blue);line-height:1.4;border:1px solid var(--line);border-radius:3px;padding:0 4px;background:var(--paper)}.legend-row{display:flex;align-items:baseline;gap:8px;padding:3px 0}.legend-row .node-dot{position:relative;top:1px}.legend-name{color:var(--ink2);font-weight:600;flex:none;width:52px}.legend-desc{color:var(--ink);flex:1;line-height:1.4}.legend-glyph{color:var(--blue);font-weight:700;flex:none;width:18px;text-align:center}.legend-glyph.legend-ver{font-size:11px;color:var(--muted)}.legend .ov-mark{flex:none;width:18px;text-align:center}.legend .drift-badge{flex:none}.legend-ring{flex:none;width:22px;height:14px;border-radius:3px;background:var(--panel)}.legend-ring.ring-dashed{border:1px dashed var(--blue)}.legend-ring.ring-solid{border:1px solid var(--blue)}.legend-ring.ring-ghost{border:1px dashed var(--line);background:transparent;opacity:.72}.set-langs{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.set-lang{font-family:var(--mono);font-size:12px;color:var(--ink);cursor:pointer;border:1px solid var(--line);border-radius:4px;padding:4px 12px;background:var(--paper)}.set-lang:hover{border-color:var(--blue);color:var(--blue)}.set-lang.on{border-color:var(--blue);color:var(--blue);background:var(--panel2);font-weight:600}.set-hint{display:block;color:var(--muted);font-size:11px;line-height:1.4}.set-keys{display:flex;flex-direction:column;gap:2px;margin:2px 0 8px}.set-key-row{display:flex;align-items:center;gap:8px;padding:2px 0}.set-key-row .legend-desc{flex:1;font-size:12px}.bind-cell{flex:none;min-width:92px;min-height:24px;display:flex;flex-wrap:wrap;align-items:center;gap:4px;justify-content:center;cursor:pointer;background:var(--paper);border:1px solid var(--line);border-radius:4px;padding:2px 6px}.bind-cell:hover:not(.fixed){border-color:var(--blue)}.bind-cell.capturing{border-color:var(--blue);border-style:dashed;background:var(--panel2)}.bind-cell.custom{border-color:var(--blue)}.bind-cell.fixed{cursor:default;opacity:.6}.bind-cell kbd{border:none;padding:0 2px;background:transparent}.bind-hint{font-size:11px;color:var(--blue)}.set-foot{display:flex;align-items:center;justify-content:space-between;gap:12px}.set-reset{flex:none;font-family:var(--mono);font-size:11px;color:var(--muted);cursor:pointer;border:1px solid var(--line);border-radius:4px;padding:3px 10px;background:var(--paper)}.set-reset:hover{border-color:var(--red);color:var(--red)}.search-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:60;background:#657b8340;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:flex-start;justify-content:center;padding-top:12vh}.search-panel{width:min(92vw,clamp(640px,50vw,1100px));max-height:72vh;display:flex;flex-direction:column;overflow:hidden;background:var(--paper);border:1px solid var(--blue);border-radius:6px;box-shadow:0 14px 44px #657b8347;font-size:12px}.search-bar{display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line);background:var(--panel2)}.search-icon{color:var(--muted);font-size:14px;flex:none}.search-input{flex:1;min-width:0;background:transparent;border:none;outline:none;color:var(--ink2);font-family:var(--mono);font-size:14px;line-height:1.4}.search-input::placeholder{color:var(--muted)}.search-results{margin:0;padding:4px;list-style:none;overflow-y:auto;overflow-x:hidden}.search-empty{padding:8px 10px;color:var(--muted)}.search-item{display:flex;align-items:baseline;gap:8px;padding:5px 8px;border-radius:3px;cursor:pointer;white-space:nowrap}.search-item.on{background:var(--panel2)}.search-item .node-dot{position:relative;top:1px}.search-kind{flex:none;font-size:9px;line-height:1;letter-spacing:.4px;text-transform:uppercase;padding:2px 4px;border-radius:3px;border:1px solid var(--line);color:var(--muted);position:relative;top:-1px}.search-kind.k-spec{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 40%,var(--line))}.search-kind.k-session{color:var(--orange);border-color:color-mix(in srgb,var(--orange) 40%,var(--line))}.search-kind.k-issue{color:var(--green);border-color:color-mix(in srgb,var(--green) 40%,var(--line))}.search-kind.k-scenario{color:var(--cyan);border-color:color-mix(in srgb,var(--cyan) 40%,var(--line))}.search-title{color:var(--ink2);font-weight:600;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.search-path{color:var(--muted);font-size:11px;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.search-foot{padding:6px 12px;border-top:1px solid var(--line);background:var(--panel);color:var(--muted);font-size:11px}.si-page{flex:1;min-width:0;display:flex}.si-panel{flex:1;min-width:0;background:var(--paper);border:none;border-radius:0;overflow:hidden;display:flex}.si-list{flex:0 0 240px;border-right:1px solid var(--line);background:var(--panel);display:flex;flex-direction:column;overflow-y:auto}.si-list-unfold{flex:0 0 22px;display:flex;align-items:flex-start;justify-content:center;padding:9px 0 0;color:var(--muted);background:var(--panel);border:none;border-right:1px solid var(--line);cursor:pointer;min-height:0}.si-list-unfold:hover{color:var(--blue);background:var(--panel2)}.si-item{display:flex;flex-wrap:wrap;align-items:center;column-gap:7px;row-gap:2px;width:100%;padding:9px 12px;background:none;border:none;border-bottom:1px solid var(--line);border-left:2px solid var(--ov, transparent);cursor:pointer;text-align:left;font-family:var(--mono);font-size:12px;color:var(--ink2)}.si-item:hover{background:var(--panel2)}.si-item.on{background:color-mix(in srgb,var(--blue) 15%,var(--panel2));color:var(--ink2);font-weight:600;border-left-width:3px}.si-item.on:hover{background:color-mix(in srgb,var(--blue) 24%,var(--panel2))}.si-item.on:before{content:"❯";flex:none;color:var(--green);font-weight:700}.si-item{flex-wrap:nowrap;padding:5px 12px;font-size:11px}.si-item.on:before{content:none}.si-item .sess-meta{flex:none;width:auto;min-width:0;margin-top:0;margin-left:auto;opacity:1;overflow:visible}.si-item .sess-lock{color:var(--muted)}.si-item.on{display:block}.si-item.on .sess-meta{float:right;margin:0 0 0 8px}.si-item.on .sess-id{white-space:normal;overflow:visible}.si-zone{--zh: var(--muted);display:flex;align-items:center;gap:8px;padding:10px 12px 4px;font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;-webkit-user-select:none;user-select:none;color:var(--zh)}.si-zone:after{content:"";flex:1;height:1px;background:var(--zh);opacity:.25}.si-zone-need{--zh: var(--yellow)}.si-zone-run{--zh: var(--green)}.si-zone-offline{--zh: var(--muted)}.si-toprow{display:flex;gap:6px;padding:8px 10px;border-bottom:1px solid var(--line)}.si-selbar{display:flex;align-items:center;gap:6px;padding:8px 10px;border-bottom:1px solid var(--line)}.si-selcount{flex:1;font-family:var(--mono);font-size:11px;color:var(--ink2)}.si-selbar .sess-rename-btn{padding:4px 10px;font-size:11px}.si-check{flex:none;width:13px;height:13px;border:1.5px solid var(--muted);border-radius:3px;box-sizing:border-box}.si-check.on{border-color:var(--blue);background:var(--blue);position:relative}.si-check.on:after{content:"";position:absolute;left:3.5px;top:1px;width:3px;height:6px;border:solid var(--paper);border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}.si-item.picked{background:color-mix(in srgb,var(--blue) 12%,var(--panel2))}.si-item.picked:hover{background:color-mix(in srgb,var(--blue) 20%,var(--panel2))}.si-pill{flex:1;display:flex;align-items:center;justify-content:center;padding:7px 0;background:none;border:1px solid var(--line);border-radius:5px;cursor:pointer;font-family:var(--mono);line-height:1}.si-pill-glyph{font-size:15px;display:flex}.si-pill.new{color:var(--blue)}.si-pill:hover{background:var(--panel2);border-color:currentColor}.si-pill.new.on{background:color-mix(in srgb,var(--blue) 16%,var(--panel2));border-color:var(--blue)}.si-pill.search{color:var(--ink2)}.si-pill.search:hover{color:var(--blue)}.si-pill.forum{color:var(--ink2)}.si-pill.forum:hover{color:var(--blue)}.si-pill.forum.on{color:var(--blue);background:color-mix(in srgb,var(--blue) 16%,var(--panel2));border-color:var(--blue)}.fv-master{flex:1;min-height:0;display:grid;grid-template-columns:minmax(232px,280px) 1fr}.fv-master.folded{grid-template-columns:22px 1fr}.fv-list-col{position:relative;min-height:0;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--line);background:var(--paper)}.fv-list-col>.fv-notice{flex:none}.fv-list-col>.fv-group{flex:1 1 auto;min-height:0;overflow-y:auto;border-top:0}.fv-list-col>.fv-scroll{flex:1 1 auto;min-height:0;overflow-y:auto}.fv-list-col>.fv-scroll .fv-group{border-top:0}.fv-list-col>.fv-scroll .fv-group-head{padding-right:34px}.fv-fold{position:absolute;top:8px;right:6px;z-index:3;display:grid;place-items:center;padding:0;color:var(--muted);background:var(--panel);border:1px solid var(--line);border-radius:6px;width:22px;height:22px;cursor:pointer}.fv-fold:hover{color:var(--blue);border-color:var(--blue)}.fv-fold-inline{display:inline-flex;align-items:center;justify-content:center;flex:none;padding:0;color:var(--muted);background:var(--panel);border:1px solid var(--line);border-radius:6px;width:22px;height:22px;cursor:pointer}.fv-fold-inline:hover{color:var(--blue);border-color:var(--blue)}.fv-unfold{display:flex;align-items:flex-start;justify-content:center;padding:9px 0 0;color:var(--muted);background:var(--panel);border:none;border-right:1px solid var(--line);cursor:pointer;min-height:0}.fv-unfold:hover{color:var(--blue);background:var(--panel2)}.fold-toggle-icon{width:15px;height:15px;display:block;flex:none}.fv-detail{min-height:0;min-width:0;overflow-y:auto;background:var(--paper)}.fv-group{display:flex;flex-direction:column}.fv-group+.fv-group{border-top:1px solid var(--line)}.fv-group-head{position:sticky;top:0;z-index:2;display:flex;align-items:center;gap:6px;flex-wrap:wrap;row-gap:5px;padding:9px 12px;background:var(--panel2);border-bottom:1px solid var(--line)}.fv-group-title{font-size:9px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--ink2)}.fv-head-row{flex-basis:100%;display:flex;align-items:center;gap:6px}.fv-row,.ef-row{display:flex;align-items:center;gap:8px;width:100%;min-width:0;text-align:left;font:inherit;font-size:12px;color:var(--ink2);background:none;border:0;border-bottom:1px solid color-mix(in srgb,var(--line) 55%,transparent);padding:7px 12px;cursor:pointer}.fv-row:hover,.ef-row:hover{background:var(--panel2)}.fv-row.sel,.ef-row.sel{background:color-mix(in srgb,var(--blue) 12%,var(--paper));box-shadow:inset 2px 0 0 var(--blue)}.fv-row .fv-concern{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fv-note{margin:auto;padding:24px;color:var(--muted);font-family:var(--mono);font-size:12.5px;text-align:center}.fv-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--muted)}.fv-dot.st-open{background:var(--blue)}.fv-dot.st-landed{background:var(--green)}.fv-dot.st-closed{background:color-mix(in srgb,var(--muted) 55%,transparent)}.fv-replies{flex:none;font-family:var(--mono);font-size:10px;color:var(--muted);background:var(--panel2);border:1px solid var(--line);border-radius:9px;padding:0 6px;line-height:1.4}.fv-store{flex:none;font-family:var(--mono);font-size:9px;letter-spacing:.8px;text-transform:uppercase;color:var(--muted)}.fv-store-local{color:color-mix(in srgb,var(--blue) 65%,var(--muted))}.fv-concern{flex:1 1 auto;min-width:80px;color:var(--ink);font-weight:600;font-size:12px}.fv-status{flex:none;font-family:var(--mono);font-size:9px;border:1px solid var(--line);border-radius:3px;padding:1px 6px;color:var(--muted)}.fv-st-open{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 40%,var(--line))}.fv-st-landed{color:var(--green);border-color:color-mix(in srgb,var(--green) 40%,var(--line))}.fv-by{flex:none;color:var(--muted);font-size:11px}.fv-originator{flex:none;display:inline-flex;align-items:center;gap:5px;font:inherit;font-size:11px;color:var(--muted);background:transparent;border:0;border-radius:4px;padding:1px 3px}.fv-originator-dot{width:7px;height:7px;border-radius:50%;flex:none}.fv-originator-who{color:var(--ink2);font-family:var(--mono);font-size:10.5px}.fv-originator.openable{cursor:pointer;background:color-mix(in srgb,var(--green) 8%,transparent)}.fv-originator.openable:hover{background:color-mix(in srgb,var(--green) 16%,transparent)}.fv-originator.openable:focus-visible{outline:2px solid var(--green);outline-offset:2px}.fv-originator.offline{opacity:.7}.fv-chips{flex:none;display:flex;flex-wrap:wrap;gap:5px}.fvd{height:100%;min-height:0;display:flex;flex-direction:column}.fvd-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;padding:16px 22px 24px}.fvd-scroll>*{max-width:860px}.fvd-compose{flex:none;border-top:1px solid var(--line);padding:8px 16px 12px;background:var(--panel)}.fvd-compose .fv-compose{margin-top:0;padding-top:0;border-top:none;max-width:860px}.fvd-head{margin-bottom:4px}.fvd-concern{font-size:16px;font-weight:700;color:var(--ink);line-height:1.35}.fv-close-issue{flex:none;font-family:var(--mono);font-size:11px;color:var(--orange);background:var(--paper);border:1px solid color-mix(in srgb,var(--orange) 35%,var(--line));padding:4px 12px;border-radius:4px;cursor:pointer}.fv-close-issue:hover{background:var(--orange);color:var(--paper);border-color:var(--orange)}.fv-close-issue:disabled{opacity:.5;cursor:default}.fv-close-issue.fv-life-promote{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 35%,var(--line))}.fv-close-issue.fv-life-promote:hover{background:var(--blue);color:var(--paper);border-color:var(--blue)}.fvd-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:12px}.fvd-body{font-size:13px}.fv-chip{font-family:var(--mono);font-size:10.5px;color:var(--blue);background:var(--panel2);border:1px solid color-mix(in srgb,var(--blue) 22%,var(--line));padding:1px 7px;border-radius:3px;cursor:pointer;line-height:1.5}.fv-chip:hover{background:var(--blue);color:var(--paper);border-color:var(--blue)}.fv-count{color:var(--muted);font-size:10.5px}.fv-link{color:var(--blue);font-size:10.5px;text-decoration:none}.fv-link:hover{text-decoration:underline}.fv-text{font-family:var(--mono);font-size:12px;line-height:1.6;color:var(--ink2);white-space:pre-wrap}.fv-reply{margin-top:10px;padding-left:12px;border-left:2px solid var(--line);border-radius:0 4px 4px 0;transition:background .15s,border-color .15s}.fv-reply.active{border-left-color:var(--green);background:color-mix(in srgb,var(--green) 7%,transparent)}.fv-reply.sel{border-left-color:var(--orange);background:color-mix(in srgb,var(--orange) 9%,transparent)}.fv-reply.remark.open{border-left-color:var(--orange);border-left-width:3px}.fv-reply.remark.resolved{opacity:.62;border-left-color:color-mix(in srgb,var(--green) 45%,var(--line))}.fv-remark-state{font-family:var(--mono);font-size:10px;padding:0 5px;border-radius:3px;margin-left:auto}.fv-remark-state.open{color:var(--orange);background:color-mix(in srgb,var(--orange) 12%,transparent);font-weight:600}.fv-remark-state.resolved{color:var(--green);background:color-mix(in srgb,var(--green) 12%,transparent)}.fv-remark-act{font-family:var(--mono);font-size:10px;padding:0 6px;border-radius:3px;cursor:pointer;line-height:1.6;background:var(--paper)}.fv-remark-act.resolve{color:var(--green);border:1px solid color-mix(in srgb,var(--green) 40%,var(--line))}.fv-remark-act.resolve:hover{background:var(--green);color:var(--paper);border-color:var(--green)}.fv-remark-act.retract{color:var(--muted);border:1px solid var(--line)}.fv-remark-act.retract:hover{background:var(--orange);color:var(--paper);border-color:var(--orange)}.fv-remark-act:disabled{opacity:.5;cursor:default}.fv-reply-meta{display:flex;align-items:center;gap:8px;margin-bottom:3px;font-family:var(--mono);font-size:10.5px}.fv-reply-by{color:var(--blue);font-weight:600}.fv-reply-at{color:var(--muted)}.fv-anchor{font-family:var(--mono);font-size:10.5px;color:var(--orange);background:color-mix(in srgb,var(--orange) 12%,transparent);border:1px solid color-mix(in srgb,var(--orange) 35%,var(--line));border-radius:3px;padding:1px 6px;cursor:pointer}.fv-anchor:hover{background:var(--orange);color:var(--paper)}.fv-anchor.static{cursor:default}.fv-anchor.static:hover{background:color-mix(in srgb,var(--orange) 12%,transparent);color:var(--orange)}.fv-anchor.static.degraded{color:var(--muted);background:transparent;border-style:dashed;border-color:color-mix(in srgb,var(--muted) 45%,var(--line))}.fv-anchor.static.degraded:hover{color:var(--muted);background:transparent}.fv-reply-media{display:flex;flex-direction:column;gap:6px;margin-top:6px}.fv-frames{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:6px}.fv-frames .an-image,.fv-frames .eval-video{height:48px;width:auto;max-width:none}.fv-anchor-btn{display:inline-flex;align-items:center;gap:4px;font-family:var(--mono);font-size:10.5px;color:var(--orange);background:var(--panel2);border:1px solid color-mix(in srgb,var(--orange) 35%,var(--line));border-radius:4px;padding:3px 10px;cursor:pointer}.fv-anchor-btn:hover{background:var(--orange);color:var(--paper)}.fv-new-btn{display:inline-flex;align-items:center;justify-content:center;color:var(--blue);background:color-mix(in srgb,var(--blue) 10%,var(--panel2));border:1px solid color-mix(in srgb,var(--blue) 32%,var(--line));padding:3px 12px;border-radius:6px;cursor:pointer;transition:color .12s,background .12s,border-color .12s}.fv-new-btn:hover{background:var(--blue);color:var(--paper);border-color:var(--blue)}.fv-new-modal{width:min(520px,92vw);overflow:visible}.fv-new-modal .legend-body{padding:0}.fv-hint{font-family:var(--mono);font-size:10.5px;color:var(--muted)}.fv-notice{padding:6px 16px;font-family:var(--mono);font-size:11px;color:var(--green);background:color-mix(in srgb,var(--green) 8%,var(--paper));border-bottom:1px solid var(--line)}.fv-error{margin:4px 0 8px;font-family:var(--mono);font-size:11px;color:var(--red)}.fv-actions .fv-error{margin:0}.fv-compose{margin-top:12px;padding-top:10px;border-top:1px dashed var(--line)}.fv-compose .fv-textarea{resize:none;min-height:56px;max-height:38vh}.fv-new-form{display:flex;flex-direction:column;gap:8px;padding:12px 16px;border-bottom:1px solid var(--line);background:var(--panel2)}.fv-store-pick{display:flex;align-items:center;gap:8px;align-self:flex-start;font-family:var(--mono);font-size:10.5px;color:var(--muted)}.fv-store-pick select{font-family:var(--mono);font-size:11px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:4px;padding:3px 7px}.fv-input,.fv-textarea{width:100%;box-sizing:border-box;font-family:var(--mono);font-size:12px;color:var(--ink);background:var(--paper);border:1px solid var(--line);border-radius:4px;padding:6px 8px;resize:vertical}.fv-input:focus,.fv-textarea:focus{outline:none;border-color:var(--blue)}.fv-tawrap{position:relative}.fv-tawrap .fv-textarea{display:block}.fv-actions{display:flex;align-items:center;gap:10px;margin-top:6px}.fv-cancel{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--muted);background:var(--paper);border:1px solid var(--line);padding:4px 12px;border-radius:4px;cursor:pointer}.fv-cancel:hover{color:var(--orange);border-color:var(--orange)}.fv-cancel:disabled{opacity:.5;cursor:default}.fv-send{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--paper);background:var(--blue);border:1px solid var(--blue);padding:4px 14px;border-radius:4px;cursor:pointer}.fv-cancel+.fv-send{margin-left:0}.fv-send:disabled{opacity:.5;cursor:default}.si-content{flex:1;min-width:0;display:flex;flex-direction:column;background:var(--paper)}.si-content.is-new{align-items:center;justify-content:center}.si-new-center{width:min(560px,86%);display:flex;flex-direction:column;align-items:center;gap:12px;margin-bottom:12vh;animation:si-rise .18s ease}.si-avatar{font-size:30px;color:var(--blue);letter-spacing:2px}.si-ask{font-size:15px;color:var(--ink2)}.si-inputwrap{position:relative;width:100%;display:flex;border:1px solid var(--blue);border-radius:6px;background:var(--panel);padding:10px 12px;transition:height .12s ease}.mention-menu{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:5;margin:0;padding:4px;list-style:none;max-height:248px;overflow-y:auto;background:var(--paper);border:1px solid var(--blue);border-radius:4px;box-shadow:0 6px 18px #07364229;animation:si-rise .12s ease}.mention-menu.up{top:auto;bottom:calc(100% + 4px)}.mention-menu.fixed{position:fixed;top:auto;z-index:70}.mention-head{padding:3px 8px 5px;font-size:10.5px;color:var(--muted);letter-spacing:.02em;border-bottom:1px solid var(--line);margin-bottom:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mention-item{display:flex;align-items:baseline;gap:8px;padding:4px 8px;border-radius:3px;cursor:pointer;font-size:12px;white-space:nowrap}.mention-item.on{background:var(--panel2)}.mention-item.new .mention-id{color:var(--green);font-style:italic}.mention-dot{width:6px;height:6px;border-radius:50%;flex:none;align-self:center}.mention-id{color:var(--blue);flex:none}.mention-id .mention-hit{font-weight:700}.mention-path{color:var(--muted);font-size:11px;min-width:0;overflow:hidden;text-overflow:ellipsis}.slash-name{color:var(--blue);flex:none}.slash-name .mention-hit{font-weight:700}.slash-desc{color:var(--muted);font-size:11px;min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis}.slash-src{color:var(--muted);font-size:10.5px;flex:none;opacity:.7}.slash-src.src-user{color:var(--green);opacity:.85}.slash-src.src-project{color:var(--cyan);opacity:.85}.slash-src.src-skill{color:var(--magenta);opacity:.85}.slash-name.ui{color:var(--sc);font-weight:700}.slash-src.src-ui{color:var(--sc);opacity:.9}.slash-src.src-mutating{color:var(--yellow);opacity:.85}.slash-src.src-report{color:var(--cyan);opacity:.85}.si-input{flex:1;min-width:0;resize:none;background:transparent;border:none;outline:none;color:var(--ink2);font-family:var(--mono);font-size:13px;line-height:1.5;max-height:180px;overflow-y:hidden;transition:height .12s ease}.si-input::placeholder{color:var(--muted)}.si-hint{font-size:11px;color:var(--muted)}.si-hint code{color:var(--blue);background:var(--panel2);padding:0 4px;border-radius:3px}.si-agent-glyph{width:16px;height:16px;display:block;fill:currentColor}.si-launcher-picker{display:inline-flex;align-items:center;gap:6px}.si-launcher-label{font-size:11px;color:var(--ink2);text-transform:lowercase}.si-launcher-harness{display:inline-flex;align-items:center;color:var(--ink2)}.si-launcher-select{height:28px;padding:0 8px;cursor:pointer;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:999px;font-size:12px;transition:border-color .12s ease}.si-launcher-select:hover{border-color:var(--blue)}.si-content.is-session .si-term-body{animation:si-expand .22s ease}.si-content.is-session{--si-dock-h: 44px}.si-tabbar{flex:none;display:flex;align-items:stretch;gap:8px;padding:0 10px;background:var(--panel);border-bottom:1px solid var(--line)}.si-tabs{flex:none;display:flex;gap:2px}.si-tab{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:none;cursor:pointer;padding:8px 14px;font-family:var(--mono);font-size:12px;color:var(--muted);border-bottom:2px solid transparent;margin-bottom:-1px}.si-tab:hover{color:var(--ink2)}.si-tab.on{color:var(--ink2);border-bottom-color:var(--blue)}.si-th-name{flex:1;min-width:0;align-self:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono);font-size:12px;color:var(--ink2)}.si-term-body{flex:1;min-height:0;overflow:hidden;padding:6px 10px 0;margin-bottom:var(--si-dock-h);background:var(--term-bg)}.si-line{font-size:12px;line-height:1.6;color:var(--ink2);white-space:pre-wrap;word-break:break-word}.si-line.muted{color:var(--muted)}.si-op{color:var(--blue);font-weight:700}.si-bottom{position:absolute;left:0;right:0;bottom:0;z-index:2;display:flex;align-items:flex-end;gap:8px;min-height:44px;box-sizing:border-box;padding:11px 14px 12px;border-top:1px solid var(--line);background:var(--panel)}.si-prompt{color:var(--blue);flex:none;line-height:20px}.si-bottom .si-input:disabled{color:var(--muted)}.si-bottom.err{border-color:var(--orange)}.si-send-err{flex:none;color:var(--orange);font-size:11px;white-space:nowrap}.si-attach{flex:none;align-self:flex-end;display:inline-flex;align-items:center;background:transparent;border:none;cursor:pointer;color:var(--muted);line-height:1.5;padding:0 2px}.si-attach:hover:not(:disabled){color:var(--blue)}.si-attach:disabled{opacity:.5;cursor:default}.si-attach-busy{animation:si-attach-spin .8s linear infinite}@keyframes si-attach-spin{to{transform:rotate(360deg)}}.si-inputwrap.dragover,.si-bottom.dragover{border-color:var(--green)}.si-attach-err{flex:none;align-self:center;color:var(--orange);font-size:11px;white-space:nowrap}@keyframes si-rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}@keyframes si-expand{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.st-wrap{position:relative;width:100%;height:100%}.st-host{width:100%;height:100%;overflow:hidden}.st-copyhint{position:absolute;right:8px;bottom:7px;pointer-events:none;z-index:2;font-family:var(--mono);font-size:11px;line-height:1.25;letter-spacing:.2px;color:#cfe8e8;opacity:.85;background:#073642eb;border:1px solid rgba(131,148,150,.35);padding:3px 8px;border-radius:5px;transition:opacity .15s ease,color .15s ease,background .15s ease,border-color .15s ease}.st-wrap:hover .st-copyhint{opacity:1}.st-copyhint.copied{opacity:1;color:#fdf6e3;background:#859900f2;border-color:#859900f2}.st-copyhint.reconnecting{opacity:1;color:#fdf6e3;background:#cb4b16f2;border-color:#cb4b16f2;animation:st-recon-pulse 1.1s ease-in-out infinite}@keyframes st-recon-pulse{0%,to{opacity:1}50%{opacity:.55}}.st-host .xterm{width:100%!important;height:100%!important;background:var(--term-bg)!important}.st-host .xterm-screen{background:var(--term-bg)!important}.st-host .xterm-viewport{overflow-y:auto!important;overflow-x:hidden!important;background:var(--term-bg)!important}.si-bottom .si-input{color:var(--ink2);min-height:20px;line-height:20px;padding:0}.si-bottom .si-attach{height:20px;line-height:20px}.si-actions{margin-left:auto;display:flex;align-items:center;gap:6px}.si-act{background:var(--paper);border:1px solid var(--line);color:var(--ink2);border-radius:4px;padding:1px 9px;cursor:pointer;font-family:var(--mono);font-size:11px}.si-act:hover,.si-act.go{border-color:var(--blue);color:var(--blue)}.si-act.go:hover{background:var(--blue);color:var(--paper)}.si-act.big{padding:8px 18px;font-size:13px;margin-top:8px}.sc-red{--sc: var(--red)}.sc-green{--sc: var(--green)}.sc-yellow{--sc: var(--yellow)}.sc-cyan{--sc: var(--cyan)}.sc-muted{--sc: var(--muted)}.si-act.ui{border-color:var(--sc);color:var(--sc)}.si-act.ui:hover{background:var(--sc);color:var(--paper);border-color:var(--sc)}.si-act.ui.type.on{background:var(--sc);color:var(--paper)}.si-act.ui.type.suggest{animation:si-type-pulse 1.1s ease-in-out infinite}@keyframes si-type-pulse{0%,to{box-shadow:0 0 #b5890080}50%{box-shadow:0 0 0 3px #b5890000}}.si-bottom.type{border-color:var(--yellow);background:var(--panel2);cursor:pointer;gap:10px}.si-type-ind{color:var(--yellow);font-weight:700;flex:none}.si-type-help{color:var(--muted);font-size:11px}.si-offline{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;text-align:center;padding:24px}.si-offline-msg{font-size:15px;color:var(--ink2)}.si-offline-sub{font-size:12px;color:var(--muted);max-width:460px;line-height:1.6}.si-offline-sub code{color:var(--blue);background:var(--panel2);padding:0 4px;border-radius:3px}.si-offline-err{margin-top:10px;font-size:12px;color:var(--yellow);max-width:460px;line-height:1.55;border:1px solid var(--yellow);border-radius:4px;padding:8px 10px;background:var(--panel2)}.m-app{position:fixed;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;flex-direction:column;background:var(--paper);color:var(--ink);font-family:var(--mono);-webkit-tap-highlight-color:transparent}.m-main{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;overscroll-behavior:contain;padding-top:env(safe-area-inset-top,0)}.m-app .m-timeline,.m-app .m-children,.m-app .m-sesslist,.m-app .m-sessdetail,.m-app .m-pane-host .pane-doc,.m-app .m-pane-host .pane-hist,.m-app .m-pane-host .pane-issues,.m-app .m-pane-host .pane-eval,.m-app .m-pane-host .pane-edit{overflow-x:hidden}.m-app pre,.m-app code,.m-app .diff-line,.m-app .hist-diff pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;max-width:100%}.m-app .ev-diff{overflow-x:hidden}.m-app .ev-diff .dl{white-space:pre-wrap;overflow-wrap:anywhere}.m-app .m-ev-from{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.m-app img,.m-app video{max-width:100%;height:auto}.m-specs{flex:1;min-height:0;display:flex;flex-direction:column}.m-crumbs{flex:none;display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:7px 10px;border-bottom:1px solid var(--line);background:var(--paper)}.m-crumb{display:inline-flex;align-items:center;gap:2px}.m-crumb-sep{color:var(--muted)}.m-crumb-btn{background:none;border:none;padding:2px 3px;font-family:var(--mono);font-size:12px;color:var(--muted);cursor:pointer}.m-crumb-btn.cur{color:var(--ink2);font-weight:700}.m-node{flex:1;min-height:0;display:flex;flex-direction:column}.m-node-head{flex:none;display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;padding:12px 14px 8px}.m-dot{flex:none;width:9px;height:9px;border-radius:50%}.m-node-title{font-size:16px;font-weight:700;color:var(--ink2)}.m-node-ver{font-size:12px;color:var(--muted)}.m-node-status{font-size:11px;color:var(--ink);padding:1px 6px;border:1px solid var(--line);border-radius:999px}.m-node-faces{display:inline-flex;gap:2px}.m-node-desc{flex:none;padding:0 14px 10px;color:var(--ink);font-size:13px;line-height:1.5}.m-tabs{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:0 10px 8px;border-bottom:1px solid var(--line)}.m-tab{flex:none;padding:6px 12px;min-height:32px;cursor:pointer;font-family:var(--mono);font-size:12px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:999px}.m-tab.on{color:var(--paper);background:var(--blue);border-color:var(--blue)}.m-pane-host{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.m-pane-host>*{flex:1;min-height:0}.m-pane-host.fixed .pane-hist{height:100%}.m-pane-host .pane-doc{overflow:auto;-webkit-overflow-scrolling:touch;padding:14px}.m-pane-host .pane-doc>h1,.m-pane-host .pane-doc>blockquote{display:none}.m-pane-host .pane-issues,.m-pane-host .pane-edit{-webkit-overflow-scrolling:touch}.m-children{overflow:auto;-webkit-overflow-scrolling:touch}.m-row{display:flex;align-items:center;gap:9px;width:100%;min-height:52px;padding:10px 14px;text-align:left;cursor:pointer;background:var(--paper);border:none;border-bottom:1px solid var(--line);font-family:var(--mono);color:var(--ink)}.m-row:active{background:var(--panel)}.m-row[disabled]{opacity:.5}.m-row-title{flex:1;min-width:0;font-size:14px;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.m-row-faces{flex:none;display:inline-flex;gap:2px}.m-row-ver{flex:none;font-size:11px;color:var(--muted)}.m-row-kids{flex:none;font-size:11px;color:var(--blue)}.m-row-chev{flex:none;color:var(--muted);font-size:18px}.m-tabbar{flex:none;display:flex;border-top:1px solid var(--line);background:var(--panel);padding-bottom:env(safe-area-inset-bottom,0)}.m-tabbar-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;height:52px;cursor:pointer;background:none;border:none;font-family:var(--mono);font-size:13px;color:var(--muted)}.m-tabbar-btn.on{color:var(--blue);font-weight:700;box-shadow:inset 0 2px 0 var(--blue)}.m-tabbar-ico{font-size:13px}.m-tabbar-badge{min-width:16px;padding:0 4px;border-radius:999px;background:var(--orange);color:var(--paper);font-size:10px;line-height:16px;text-align:center}.m-sesslist{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch}.m-sess-row{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;width:100%;padding:12px 14px;text-align:left;cursor:pointer;font-size:14px;color:var(--ink2);background:var(--paper);border:none;border-bottom:1px solid var(--line);font-family:var(--mono)}.m-sess-row:active{background:var(--panel)}.m-sess-row .sess-id{flex:1;min-width:0;font-weight:700;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.m-sess-row .sess-meta{flex:none;width:auto;min-width:0;margin-top:0;margin-left:auto;font-size:12px}.m-sess-row .sess-glyph{font-size:13px;width:14px}.m-sess-row .sess-ops{color:var(--muted)}.m-zone{padding:10px 14px 4px;font-size:10px;font-weight:700;letter-spacing:.5px;color:var(--muted);text-transform:uppercase}.m-sessdetail{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch}.m-sessdetail.chat{display:flex;flex-direction:column;overflow:hidden}.m-sess-card{flex:none;display:flex;align-items:center;gap:10px;padding:12px 14px 12px 6px;border-bottom:1px solid var(--line)}.m-sess-back{flex:none;width:36px;height:36px;display:flex;align-items:center;justify-content:center;font-size:26px;line-height:1;color:var(--blue);background:none;border:none;cursor:pointer}.m-sess-meta{display:flex;flex-direction:column;gap:2px;min-width:0}.m-sess-name{font-size:16px;font-weight:700;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.m-sess-status{font-size:12px;color:var(--muted)}.m-sess-id8{color:var(--muted)}.m-timeline{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:10px 14px}.m-day{text-align:center;margin:10px 0 6px;font-size:10px;letter-spacing:.5px;color:var(--muted);text-transform:uppercase}.m-ev{margin:8px 0}.m-ev-head{display:flex;align-items:baseline;gap:7px;font-family:var(--mono);font-size:12px}.m-ev-glyph{flex:none}.m-ev-word{font-weight:700}.m-ev-time{margin-left:auto;flex:none;font-size:10px;color:var(--muted)}.m-ev-note{margin:4px 0 0 14px;padding:8px 10px;font-size:13px;line-height:1.55;color:var(--ink2);white-space:pre-wrap;overflow-wrap:anywhere;background:var(--panel);border-left:2px solid var(--line);border-radius:0 6px 6px 0}.m-ev-sent .m-ev-from{font-weight:700;color:var(--blue)}.m-ev-text{margin:4px 0 0 14px;padding:8px 10px;font-size:13px;line-height:1.55;color:var(--ink);white-space:pre-wrap;overflow-wrap:anywhere;background:color-mix(in srgb,var(--blue) 7%,var(--paper));border-left:2px solid color-mix(in srgb,var(--blue) 45%,var(--line));border-radius:0 6px 6px 0}.m-ev-prompt summary{font-family:var(--mono);font-size:12px;color:var(--muted);cursor:pointer}.m-offline,.m-senderr{flex:none;padding:6px 14px;font-size:12px}.m-offline{color:var(--muted);background:var(--panel);border-top:1px solid var(--line)}.m-senderr{color:var(--red)}.m-composer{flex:none;padding:8px 14px 10px;border-top:1px solid var(--line);background:var(--panel)}.m-composer-line{display:flex;align-items:flex-end;gap:8px}.m-input{flex:1;min-width:0;resize:none;padding:9px 10px;font-family:var(--mono);font-size:14px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:8px;outline:none}.m-input:focus{border-color:var(--blue)}.m-send{flex:none;height:38px;padding:0 16px;font-family:var(--mono);font-size:13px;font-weight:700;color:var(--paper);background:var(--blue);border:none;border-radius:8px;cursor:pointer}.m-send:disabled{opacity:.45;cursor:default}.m-empty{padding:18px 14px;color:var(--muted);font-size:13px}.m-empty.big{flex:1;display:flex;align-items:center;justify-content:center;text-align:center}.m-new-btn{display:flex;align-items:center;gap:8px;width:100%;padding:12px 14px;font-family:var(--mono);font-size:14px;font-weight:700;color:var(--blue);text-align:left;background:var(--paper);border:none;border-bottom:1px solid var(--line);cursor:pointer}.m-new-btn:active{background:var(--panel)}.m-new-btn-plus{flex:none;font-size:16px;line-height:1}.m-new-body{display:flex;flex-direction:column;gap:12px;padding:14px}.m-new-input{min-height:120px}.m-new-launcher{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--muted)}.m-new-launcher-select{flex:1;min-width:0;padding:8px 10px;font-family:var(--mono);font-size:14px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:8px}.m-new .m-senderr{padding:0}.m-new-go{height:42px}.pane-eval{flex:1;min-width:0;padding:16px 20px;overflow:auto}.pane-eval.empty{color:var(--muted);font-size:13px}.eval-todo-note{color:var(--muted);font-size:13px;margin:0 0 14px}.eval-declared-row .eval-expected{padding-left:23px;margin:4px 0 0}.eval-tracks{color:var(--muted);font-family:var(--mono);font-size:10px;opacity:.85}.eval-dangling-badge{color:var(--orange);margin-right:6px}.eval-dangling-name{text-decoration:line-through;color:var(--muted)}.eval-dangling-tag{font-family:var(--mono);font-size:10px;color:var(--orange);margin-left:8px;border:1px dashed color-mix(in srgb,var(--orange) 45%,var(--line));border-radius:3px;padding:0 5px}.eval-dangling-remarks{padding-left:23px;margin:6px 0 0}.eval-row{border-bottom:1px solid var(--line);padding:10px 0}.eval-row:last-child{border-bottom:0}.eval-head{display:flex;flex-direction:column;gap:4px;width:100%;text-align:left;background:none;border:0;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer}.eval-top{display:flex;align-items:center;gap:8px}.eval-caret{flex:none;color:var(--muted);font-size:11px}.eval-scenario{font-size:13px;font-weight:600;color:var(--ink2)}.eval-meta{display:flex;align-items:center;gap:10px;font-size:11px;color:var(--muted)}.eval-sha{color:var(--blue);background:var(--panel2);padding:0 4px;border-radius:3px}.eval-verdict{flex:none;font-size:10px;font-weight:700;border-radius:3px;padding:0 4px;line-height:1.5}.eval-verdict.pass{color:var(--green);border:1px solid var(--green)}.eval-verdict.fail{color:var(--orange);border:1px solid var(--orange)}.eval-verdict.note{color:var(--blue);border:1px solid var(--blue);cursor:help}.eval-verdict.legacy{color:var(--muted);border:1px solid var(--line)}.eval-shot{margin:8px 0 2px;padding-left:19px}.eval-shot img,.eval-video{display:block;max-width:100%;background:var(--panel2);border:1px solid var(--line);border-radius:4px}.eval-noimg{font-size:11px;color:var(--muted)}.eval-expected,.eval-note{font-size:12px;color:var(--ink2);margin:0 0 6px}.eval-expected-label{color:var(--muted);font-weight:600}.eval-transcript{font-family:var(--mono);font-size:11px;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:8px 10px;margin:0;max-height:360px;overflow:auto;white-space:pre-wrap;word-break:break-word}.eval-transcript.loading{color:var(--muted)}.eval-datawrap{border:1px solid var(--line);border-left:3px solid var(--acc, #6b46c1);border-radius:4px;overflow:hidden}.eval-datahead{font-family:var(--mono);font-size:10px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);background:var(--panel);padding:4px 10px;border-bottom:1px solid var(--line)}.eval-data{font-family:var(--mono);font-size:11px;color:var(--ink2);background:var(--panel2);padding:8px 10px;margin:0;max-height:360px;overflow:auto;white-space:pre-wrap;word-break:break-word}.eval-data.loading{color:var(--muted)}.ef-chipbar{display:flex;gap:6px;flex-wrap:wrap}.ef-chip{font:inherit;font-size:11px;color:var(--muted);background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:3px 10px;cursor:pointer;line-height:1.35;transition:color .12s,border-color .12s,background .12s}.ef-chip:hover{color:var(--ink2);border-color:color-mix(in srgb,var(--blue) 45%,var(--line))}.ef-chip.on{color:var(--paper);background:var(--blue);border-color:var(--blue)}.ef-close{font:inherit;color:var(--muted);background:none;border:0;cursor:pointer;padding:2px 6px}.ef-empty,.ef-miss{color:var(--muted);font-size:12px;padding:18px 8px}.ef-scenario{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;font-size:12px}.ef-node{flex:0 1 auto;max-width:58px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10.5px;opacity:.9}.ef-kind{flex:none;margin-left:auto;font-size:11px;color:var(--muted)}.ef-time{flex:none;color:var(--muted);font-size:10.5px;min-width:26px;text-align:right}.ef-okd{flex:none;color:var(--green);font-size:11px}.ef-okbtn{flex:none;font-size:10.5px;color:var(--muted);border:1px solid var(--line);border-radius:4px;padding:0 5px;line-height:1.5;opacity:0;transition:opacity .12s,color .12s,border-color .12s}.ef-row:hover .ef-okbtn,.ef-row.sel .ef-okbtn{opacity:1}.ef-okbtn:hover{color:var(--green);border-color:color-mix(in srgb,var(--green) 55%,var(--line))}.an-detail{height:100%;min-height:0;display:flex;flex-direction:column;padding:0}.an-head{flex:none;display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 16px;border-bottom:1px solid var(--line);background:var(--panel2)}.an-work{flex:1 1 auto;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,340px)}.an-stage-col{min-width:0;min-height:0;overflow-y:auto;padding:12px 16px 20px}.an-rail{min-width:0;min-height:0;display:flex;flex-direction:column;border-left:1px solid var(--line);background:var(--panel)}.an-rail .an-comments-head{flex:none;padding:10px 12px 8px;border-bottom:1px solid var(--line)}.an-rail-list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:4px 12px 8px}.an-rail-list .fv-reply{margin-top:8px}.an-rail-compose{flex:none;border-top:1px solid var(--line);padding:0 12px 10px;background:var(--panel)}.an-rail-compose .fv-compose{margin-top:8px;padding-top:0;border-top:none}@media (max-width: 900px){.an-detail{height:auto}.an-work{display:block}.an-stage-col{overflow:visible}.an-rail{border-left:none;border-top:1px solid var(--line)}.an-rail-list{overflow:visible}.an-rail-compose{position:sticky;bottom:0;z-index:2}}.an-meta{font-size:10.5px;color:var(--muted)}.an-image{display:block;max-width:100%;border:1px solid var(--line);border-radius:6px;background:var(--panel2);cursor:zoom-in}.lightbox{position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;display:flex;align-items:center;justify-content:center;background:#000000d1;cursor:zoom-out}.lightbox img{max-width:96vw;max-height:96vh;object-fit:contain;border-radius:4px;box-shadow:0 8px 48px #0009}.an-prior-note{color:var(--muted)}.an-stale{color:var(--muted);font-family:var(--mono);font-size:11px}.an-stale b{color:var(--ink2);font-weight:700}.an-stale-files{opacity:.85}.an-title{font-weight:700;font-size:14px;color:var(--ink)}.an-node{font-size:11px;color:var(--muted)}.an-expected{font-size:12px;color:var(--ink2);padding:8px 0 0}.an-comments-head{font-size:10px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--ink2)}.an-stage-col>*{max-width:1100px}.an-gallery{display:flex;flex-direction:column;gap:10px;margin:12px 0 0}.an-detail>.an-expected,.an-detail>.an-hint{padding:8px 16px 0}.an-stage{position:relative;margin:12px 0 0;cursor:crosshair}.an-video{display:block;width:100%;background:#000;border:1px solid var(--line);border-radius:6px 6px 0 0}.an-rect{position:absolute;border:2px solid var(--orange);border-radius:3px;background:#ff963c1f;pointer-events:none}.an-rect.live{border-style:dashed}.an-bigplay{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:54px;height:54px;border-radius:50%;background:#080c1280;border:1px solid rgba(255,255,255,.22);display:flex;align-items:center;justify-content:center;font-size:22px;color:#fff;pointer-events:none;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.an-bar{display:flex;align-items:center;gap:10px;padding:6px 8px;background:var(--panel2);border:1px solid var(--line);border-top:none;border-radius:0 0 6px 6px}.an-play{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:30px;height:26px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:5px;cursor:pointer}.an-play:hover{border-color:var(--blue)}.an-fs{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:30px;height:26px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:5px;cursor:pointer}.an-fs:hover{border-color:var(--blue)}.an-player:fullscreen{display:flex;flex-direction:column;align-items:center;justify-content:center;background:#000}.an-player:fullscreen .an-stage{margin:0;max-width:100vw;max-height:calc(100vh - 38px)}.an-player:fullscreen .an-video{width:auto;max-width:100vw;max-height:calc(100vh - 38px);border:none;border-radius:0}.an-player:fullscreen .an-bar{width:min(100vw,1100px);border-radius:0}.an-player:-webkit-full-screen{display:flex;flex-direction:column;align-items:center;justify-content:center;background:#000}.an-player:-webkit-full-screen .an-stage{margin:0;max-width:100vw;max-height:calc(100vh - 38px)}.an-player:-webkit-full-screen .an-video{width:auto;max-width:100vw;max-height:calc(100vh - 38px);border:none;border-radius:0}.an-player:-webkit-full-screen .an-bar{width:min(100vw,1100px);border-radius:0}.an-time{flex:0 0 auto;font-family:var(--mono);font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}.an-curstep{flex:0 0 auto;font-size:10.5px;color:var(--green);background:color-mix(in srgb,var(--green) 12%,transparent);border:1px solid color-mix(in srgb,var(--green) 35%,var(--line));border-radius:3px;padding:1px 7px;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.an-seek{flex:1 1 auto;position:relative;height:18px;display:flex;align-items:center;cursor:pointer}.an-seek-trk{position:absolute;left:0;right:0;height:5px;background:var(--line);border-radius:5px}.an-seek-play{position:absolute;left:0;height:5px;background:var(--blue);border-radius:5px;transition:width .12s linear}.an-knob{position:absolute;width:12px;height:12px;background:var(--ink);border:2px solid var(--paper);border-radius:50%;transform:translate(-50%);box-shadow:0 1px 4px #0006;transition:left .12s linear}.an-band{position:absolute;top:2px;width:2px;height:14px;background:color-mix(in srgb,var(--green) 45%,transparent);transform:translate(-50%);pointer-events:none}.an-mk{position:absolute;top:-1px;width:10px;height:20px;padding:0;transform:translate(-50%);background:none;border:none;cursor:pointer}.an-mk:after{content:"";display:block;margin:0 auto;width:3px;height:20px;border-radius:2px;background:var(--orange);box-shadow:0 0 0 1px var(--paper);transition:transform .12s,background .12s}.an-mk:hover:after{transform:scaleX(1.8)}.an-mk.active:after{background:var(--green)}.an-mk.on:after{transform:scaleX(2);background:var(--orange);box-shadow:0 0 0 1px var(--paper),0 0 8px color-mix(in srgb,var(--orange) 60%,transparent)}.an-seek-hov{position:absolute;bottom:22px;transform:translate(-50%);background:var(--panel);border:1px solid var(--line);color:var(--ink);font-family:var(--mono);font-size:10.5px;padding:1px 6px;border-radius:4px;pointer-events:none;white-space:nowrap}.an-keys{font-family:var(--mono);font-size:10px;color:var(--muted);padding:4px 0 0;letter-spacing:.2px}.an-ruler{display:flex;flex-wrap:wrap;gap:4px;padding:8px 0 0}.an-step{font:inherit;font-size:11px;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:2px 8px;cursor:pointer}.an-step.on{border-color:var(--green);color:var(--ink)}.an-hint{color:var(--muted);font-size:11px;padding:8px 0 0}.an-flash{font-size:12px;color:var(--green);padding:4px 0 0}.an-verdict-badge{font-size:12px;font-weight:700;line-height:1}.an-verdict-badge.pass{color:var(--green)}.an-verdict-badge.fail{color:var(--orange)}.an-verdict-badge.legacy{color:var(--muted)}.an-okd{font-size:11px;color:var(--green);white-space:nowrap}.an-okbtn{font:inherit;font-size:11px;color:var(--ink2);background:var(--panel);border:1px solid var(--line);border-radius:5px;padding:2px 8px;cursor:pointer;white-space:nowrap;transition:color .12s,border-color .12s}.an-okbtn:hover{color:var(--green);border-color:color-mix(in srgb,var(--green) 55%,var(--line))}.an-ab{display:flex;align-items:center;gap:8px;margin-left:auto}.an-ab-nav{display:inline-flex;align-items:center;justify-content:center;line-height:1;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:2px 6px;cursor:pointer}.an-ab-nav:disabled{opacity:.35;cursor:default}.an-ab-track{display:flex;flex-wrap:wrap;gap:3px;align-items:center}.an-ab-pip{font:inherit;font-size:11px;font-weight:700;width:20px;height:20px;display:flex;align-items:center;justify-content:center;background:var(--panel2);border:1px solid var(--line);border-radius:4px;cursor:pointer;color:var(--muted)}.an-ab-pip.pass{color:var(--green)}.an-ab-pip.fail{color:var(--orange)}.an-ab-pip.on{outline:2px solid var(--blue);outline-offset:0;border-color:var(--blue)}.an-ab-pos{font-size:10.5px;color:var(--muted);margin-left:2px}.se-pane{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--paper)}.se-gates{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 14px;border-bottom:1px solid var(--line);background:var(--panel2)}.se-gate{font-family:var(--mono);font-size:10.5px;border:1px solid var(--line);border-radius:3px;padding:1px 7px;color:var(--muted)}.se-gate.ok{color:var(--green);border-color:color-mix(in srgb,var(--green) 40%,var(--line))}.se-gate.bad{color:var(--red);border-color:color-mix(in srgb,var(--red) 40%,var(--line))}.se-export{margin-left:auto;display:inline-flex;align-items:center;color:var(--blue);text-decoration:none}.se-export:hover{color:color-mix(in srgb,var(--blue) 72%,var(--ink))}.se-blind .ef-scenario{color:var(--muted)}.se-warn{font-family:var(--mono);font-size:10px;color:var(--orange)}.ef-insession{color:var(--blue);font-size:10px;flex:none}.se-divider{display:flex;align-items:center;gap:8px;padding:5px 10px 3px;font-family:var(--mono);font-size:9px;letter-spacing:.8px;text-transform:uppercase;color:var(--muted)}.se-divider:before,.se-divider:after{content:"";flex:1;border-top:1px dashed var(--line)}.fv-filter{font-family:var(--mono);font-size:11px;line-height:1.35;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:3px 8px;cursor:pointer;transition:border-color .12s}.fv-filter:hover{border-color:color-mix(in srgb,var(--blue) 45%,var(--line))}.loading{display:flex;align-items:center;justify-content:center;gap:10px;height:100%;flex:1;color:var(--muted);font-family:var(--mono);font-size:13px}.loading.load-error{color:var(--red)}.load-retry{font-family:var(--mono);font-size:12px;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:3px 10px;cursor:pointer}.load-retry:hover{border-color:var(--muted)}.chrono-tools{display:flex;justify-content:flex-end;margin:0 0 6px}.chrono-all{font-family:var(--mono);font-size:10px;color:var(--muted);background:none;border:1px solid var(--line);border-radius:4px;padding:2px 8px;cursor:pointer}.chrono-all:hover{color:var(--ink2);border-color:var(--muted)}.pane-filter{position:sticky;top:-16px;z-index:2;margin:-8px 0 8px;padding:6px 0;background:var(--panel)}.pane-filter-input{width:100%;box-sizing:border-box;background:var(--panel2);border:1px solid var(--line);border-radius:4px;outline:none;color:var(--ink2);font-family:var(--mono);font-size:11px;padding:4px 8px}.pane-filter-input:focus{border-color:var(--muted)}.pane-filter-input::placeholder{color:var(--muted)}.pane-filter-none{color:var(--muted);font-family:var(--mono);font-size:11px;padding:6px 2px}.ui-tip{position:fixed;z-index:100;pointer-events:none;background:var(--panel2);color:var(--ink2);border:1px solid var(--line);border-radius:6px;padding:4px 9px;font-family:var(--mono);font-size:11px;line-height:1.5;max-width:320px;white-space:pre-line;overflow-wrap:break-word;box-shadow:0 4px 16px #00000038;opacity:0;transition:opacity .14s ease,transform .14s ease}.ui-tip[data-place=top]{transform:translateY(3px)}.ui-tip[data-place=bottom]{transform:translateY(-3px)}.ui-tip.show{opacity:1;transform:translateY(0)}.ui-tip-arrow{position:absolute;width:8px;height:8px;background:var(--panel2);border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.ui-tip[data-place=top] .ui-tip-arrow{bottom:-4.5px;transform:rotate(45deg)}.ui-tip[data-place=bottom] .ui-tip-arrow{top:-4.5px;transform:rotate(225deg)}
@@ -0,0 +1,6 @@
1
+ import{r as f,f as p,h as m}from"./index-BQu-oJ8J.js";import{V as d,W as y}from"./SessionWindow-JYbpPwNB.js";const $=(t,a,n)=>{const s=t.match(/^\/(\S+)\s*([\s\S]*)$/);if(!s)return t;const o=a.find(i=>i.name===s[1]);if(!o)return t;const e=[],r=s[2].replace(d,(i,h)=>(e.push(h),"")).trim(),u=e.length?e.map(i=>{const h=n.find(l=>l.id===i);return h?`- [[${h.id}]] — ${y(h.path)}`:`- [[${i}]]`}).join(`
2
+ `):"(No target was mentioned. If the prompt names the scope, use it; otherwise ask the human to define the scope before proceeding — unless this task needs no scope, in which case proceed.)",c=o.body.includes("{{targets}}")?o.body.replace("{{targets}}",u):`${o.body}
3
+
4
+ ${u}`;return r?`${c}
5
+
6
+ ${r}`:c};async function b(t,a){try{const n=await fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a?{prompt:t,launcher:a}:{prompt:t})}),s=await n.json().catch(()=>null);return{ok:n.ok,error:s==null?void 0:s.error}}catch{return{ok:!1}}}function k(){const[t,a]=f.useState([]),[n,s]=f.useState(()=>{try{return localStorage.getItem("si.launcher")||""}catch{return""}}),o=e=>{s(e);try{localStorage.setItem("si.launcher",e)}catch{}};return f.useEffect(()=>{p().then(e=>{const r=e==null?void 0:e.launchers;!Array.isArray(r)||!r.length||(a(r),s(u=>r.some(c=>c.name===u)?u:e.default&&r.some(c=>c.name===e.default)?e.default:r[0].name))}).catch(()=>{})},[]),{launchers:t,launcher:n,pickLauncher:o}}function E(){const[t,a]=f.useState([]);return f.useEffect(()=>{m().then(n=>{Array.isArray(n)&&a(n)}).catch(()=>{})},[]),t}export{E as a,$ as b,b as c,k as u};
@@ -19,8 +19,8 @@
19
19
  } catch (e) { /* localStorage/matchMedia unavailable — default (light) stands */ }
20
20
  })()
21
21
  </script>
22
- <script type="module" crossorigin src="/assets/index-Ce0wDyQS.js"></script>
23
- <link rel="stylesheet" crossorigin href="/assets/index-Cc26X4ce.css">
22
+ <script type="module" crossorigin src="/assets/index-BQu-oJ8J.js"></script>
23
+ <link rel="stylesheet" crossorigin href="/assets/index-BbMkwuix.css">
24
24
  </head>
25
25
  <body>
26
26
  <div id="root"></div>
@@ -4,7 +4,7 @@ import { repoRoot, headSha, driftIndex, stagedFiles, git } from '../../spec-cli/
4
4
  import { loadSpecs } from '../../spec-cli/src/specs.js'
5
5
  import { loadConfig, sourceExtRe } from '../../spec-cli/src/lint.js'
6
6
  import { mainBranch, envSessionId, readRawRecord } from '../../spec-cli/src/layout.js'
7
- import { evalNodes, validateScenarios, resolveEvalNode, scenarioHash, EVAL_FILE, type EvalNode } from './scenarios.js'
7
+ import { evalNodes, validateScenarios, resolveEvalNode, scenarioHash, EVAL_FILE, type EvalNode, type ScenarioTestReference } from './scenarios.js'
8
8
  import { readReadings, readSidecar, appendReading, appendRetraction, latestPerScenario, evidenceOf, isJsonBlob, type Reading, type Verdict, type Evidence, type EvidenceKind, type Retraction } from './sidecar.js'
9
9
  import { staleAxes, contentProbeFor } from './freshness.js'
10
10
  import { scenarioIndex } from './scenariofresh.js'
@@ -115,7 +115,7 @@ async function scan(args: string[] = []): Promise<number> {
115
115
  if (y) {
116
116
  // schema first: a malformed eval.md is the loudest gap — report each violation, then still scan its
117
117
  // (leniently-parsed) scenarios for stale/missing so a typo doesn't mask a real freshness gap.
118
- for (const e of validateScenarios(readFileSync(join(y.dir, EVAL_FILE), 'utf8'), cfg.scenarioTags)) {
118
+ for (const e of validateScenarios(readFileSync(join(y.dir, EVAL_FILE), 'utf8'), cfg.scenarioTags, root)) {
119
119
  malformed++
120
120
  findings.push(` • eval-schema: '${s.id}' ${e} — fix ${y.evalPath}`)
121
121
  }
@@ -428,6 +428,51 @@ async function retractCmd(args: string[]): Promise<number> {
428
428
  return 0
429
429
  }
430
430
 
431
+ // `spex eval ok` — the HUMAN sign-off on a scenario's latest reading ([[human-ok]]): appends a monotonic
432
+ // human-ok event bound to that one immutable reading (a newer reading or live-computed staleness brings the
433
+ // scenario back on its own — no un-ok verb exists). CLI parity with the dashboard's affordance (LAW L);
434
+ // identity is the surface's: 'human' here — and a GOVERNED session is REFUSED, because the sign-off is the
435
+ // human's own deliberate judgment and an agent blessing its own reading would hide it from exactly the
436
+ // review the ok certifies (the no-self-resolve analogue). Flag set closed, like every eval verb.
437
+ async function okCmd(args: string[]): Promise<number> {
438
+ const root = repoRoot()
439
+ for (let i = 0; i < args.length; i++) {
440
+ const a = args[i]
441
+ if (!a.startsWith('--')) continue
442
+ if (a === '--scenario') { i++; continue }
443
+ console.error(`spex eval ok: unknown flag '${a}' — accepts --scenario`)
444
+ return 2
445
+ }
446
+ if (envSessionId()) {
447
+ console.error('spex eval ok: refusing under a governed session — human-ok is the HUMAN\'s sign-off, never an agent\'s self-blessing. Ask the human to ok it from the dashboard or their own terminal; an agent\'s judgment on a reading is a remark (`spex remark add`).')
448
+ return 1
449
+ }
450
+ const sel = positional(args)
451
+ if (!sel || sel === '.') { console.error('spex eval ok: name a node — usage: spex eval ok <node> --scenario <name>'); return 2 }
452
+ const node = stripRefSigil(sel)
453
+ const scName = flag(args, 'scenario')
454
+ const res = resolveEvalNode(evalNodes(root), node)
455
+ if (!res.ok) { console.error(`spex eval ok: ${res.error}`); return 1 }
456
+ const declared = res.node.scenarios.map((s) => s.name)
457
+ const scenario = scName ?? (declared.length === 1 ? declared[0] : undefined)
458
+ if (!scenario) {
459
+ console.error(`spex eval ok: '${res.node.id}' declares ${declared.length} scenarios — name one with --scenario <name> (declared: ${declared.join(', ')})`)
460
+ return 1
461
+ }
462
+ const { fileHumanOk } = await import('./humanok.js')
463
+ const r = fileHumanOk(res.node.id, scenario, 'human')
464
+ if (!r.ok) { console.error(`spex eval ok: ${r.error}`); return 1 }
465
+ if (r.already) {
466
+ console.log(`spex eval ok: '${res.node.id}' scenario '${scenario}' reading @ ${r.humanOk.okTs} is already human-ok'd (by ${r.humanOk.by}, ${r.humanOk.ts}) — monotonic, nothing appended`)
467
+ return 0
468
+ }
469
+ console.log(` ☑ '${res.node.id}' scenario '${scenario}' reading @ ${r.humanOk.okTs} (${r.humanOk.okSha.slice(0, 7)}) human-ok'd`)
470
+ console.log(r.landed === 'committed'
471
+ ? 'spex eval ok: 1 sign-off filed (committed straight to the trunk)'
472
+ : 'spex eval ok: 1 sign-off filed (an appended event — commit the sidecar so the sign-off lands)')
473
+ return 0
474
+ }
475
+
431
476
  async function clean(args: string[]): Promise<number> {
432
477
  const root = repoRoot()
433
478
  const all = has(args, 'all')
@@ -467,7 +512,7 @@ function checkStaged(): number {
467
512
  for (const rel of staged.filter((p) => p === EVAL_FILE || p.endsWith('/' + EVAL_FILE))) {
468
513
  const abs = join(root, rel)
469
514
  if (!existsSync(abs)) continue // staged deletion — nothing to validate
470
- const errs = validateScenarios(readFileSync(abs, 'utf8'), tagLibrary)
515
+ const errs = validateScenarios(readFileSync(abs, 'utf8'), tagLibrary, root)
471
516
  if (!errs.length) continue
472
517
  bad = true
473
518
  console.error(`✗ SpexCode eval: ${rel} — invalid scenario schema:`)
@@ -521,7 +566,8 @@ export function formatTimeline(tl: EvalTimeline): string {
521
566
  const ev = list.length
522
567
  ? list.map((e) => e.state === 'miss' ? 'miss original file' : `${e.kind} ${(e.hash ?? '').slice(0, 12)}…`).join(', ')
523
568
  : 'no evidence'
524
- const head = ` ${r.scenario.padEnd(w)} ${verdictText(r.verdict)} ${badge} ${r.codeSha.slice(0, 7)} ${ev} ${r.ts}`
569
+ const okTag = r.humanOk ? ` ☑ human-ok (${r.humanOk.by})` : ''
570
+ const head = ` ${r.scenario.padEnd(w)} ${verdictText(r.verdict)} ${badge}${okTag} ${r.codeSha.slice(0, 7)} ${ev} ${r.ts}`
525
571
  return r.expected ? [head, ` ${' '.repeat(w)} expected: ${r.expected}`] : [head]
526
572
  })
527
573
  return [`spex eval ls: '${tl.node}' — ${tl.readings.length} eval(s), newest first`, '', ...lines, ...retractLines].join('\n')
@@ -532,6 +578,34 @@ export function formatTimeline(tl: EvalTimeline): string {
532
578
  // (the collection view); a <node>/`.` scopes to one node. --unmeasured keeps only scenarios with NO
533
579
  // effective reading — never measured, or every filing retracted — the blind-spot worklist a measuring
534
580
  // hand picks from. Flag set closed, like every eval verb.
581
+ export type ScenarioListRow = {
582
+ node: string
583
+ scenario: string
584
+ tags?: string[]
585
+ test?: ScenarioTestReference
586
+ measured: boolean
587
+ latest?: { verdict?: Verdict; ts: string }
588
+ }
589
+
590
+ export function scenarioListRows(nodes: EvalNode[], unmeasuredOnly = false): ScenarioListRow[] {
591
+ const rows: ScenarioListRow[] = []
592
+ for (const n of nodes) {
593
+ const latest = latestPerScenario(readReadings(n.sidecarPath))
594
+ for (const sc of n.scenarios) {
595
+ const r = latest.get(sc.name)
596
+ if (unmeasuredOnly && r) continue
597
+ rows.push({
598
+ node: n.id, scenario: sc.name,
599
+ ...(sc.tags?.length ? { tags: sc.tags } : {}),
600
+ ...(sc.test ? { test: sc.test } : {}),
601
+ measured: !!r,
602
+ ...(r ? { latest: { ...(r.verdict ? { verdict: r.verdict } : {}), ts: r.ts } } : {}),
603
+ })
604
+ }
605
+ }
606
+ return rows
607
+ }
608
+
535
609
  async function scenarioLs(args: string[]): Promise<number> {
536
610
  for (const a of args) {
537
611
  if (!a.startsWith('--')) continue
@@ -552,21 +626,7 @@ async function scenarioLs(args: string[]): Promise<number> {
552
626
  nodes = [res.node]
553
627
  }
554
628
  const unmeasuredOnly = has(args, 'unmeasured')
555
- type Row = { node: string; scenario: string; tags?: string[]; measured: boolean; latest?: { verdict?: Verdict; ts: string } }
556
- const rows: Row[] = []
557
- for (const n of nodes) {
558
- const latest = latestPerScenario(readReadings(n.sidecarPath))
559
- for (const sc of n.scenarios) {
560
- const r = latest.get(sc.name)
561
- if (unmeasuredOnly && r) continue
562
- rows.push({
563
- node: n.id, scenario: sc.name,
564
- ...(sc.tags?.length ? { tags: sc.tags } : {}),
565
- measured: !!r,
566
- ...(r ? { latest: { ...(r.verdict ? { verdict: r.verdict } : {}), ts: r.ts } } : {}),
567
- })
568
- }
569
- }
629
+ const rows = scenarioListRows(nodes, unmeasuredOnly)
570
630
  if (has(args, 'json')) { console.log(JSON.stringify(rows, null, 2)); return 0 }
571
631
  if (!rows.length) {
572
632
  console.log(`spex eval scenario ls: ${unmeasuredOnly ? 'no unmeasured scenarios' : 'no scenarios declared'}${sel ? ` on '${nodes[0]?.id ?? sel}'` : ''}`)
@@ -585,13 +645,14 @@ async function scenarioLs(args: string[]): Promise<number> {
585
645
 
586
646
  // the `spex eval` drawer's node-scoped verbs ([[cli-surface]]): add (file a measurement) · ls (a node's
587
647
  // reading timeline) · scenario ls (the declared contracts, --unmeasured = blind spots) · lint (the
588
- // measurement-layer lint — advisory, always exit 0) · retract · clean.
648
+ // measurement-layer lint — advisory, always exit 0) · ok (the human sign-off) · retract · clean.
589
649
  // The session-scoped read (`spex eval ls --session <SEL>`) is intercepted in cli.ts before this runs;
590
650
  // `check-staged` is hook plumbing, exported separately for `spex internal check-staged`.
591
651
  export async function runEval(args: string[]): Promise<number> {
592
652
  const sub = args[0]
593
653
  if (sub === 'lint') return scan(args.slice(1))
594
654
  if (sub === 'add') return evalCmd(args.slice(1))
655
+ if (sub === 'ok') return okCmd(args.slice(1))
595
656
  if (sub === 'retract') return retractCmd(args.slice(1))
596
657
  if (sub === 'clean') return clean(args.slice(1))
597
658
  if (sub === 'ls') return show(args.slice(1))
@@ -600,7 +661,7 @@ export async function runEval(args: string[]): Promise<number> {
600
661
  console.error('spex eval scenario: ls [<node>|.] [--unmeasured] [--json] — list declared scenarios (the measurement contracts)')
601
662
  return 2
602
663
  }
603
- console.error('spex eval: add [.|<node>] [--scenario <name>] (--pass|--fail) [--note <text>] [--image <path> …repeatable] [--result <path|->] [--video <path>] [--timeline <json>] | ls [.|<node>] [--json] | ls --session <SEL> [--export] | scenario ls [<node>|.] [--unmeasured] [--json] | lint [--changed] | retract [.|<node>] [--scenario <name>] [--last | --ts <iso>] [--note <why>] | clean [--keep-latest|--all]')
664
+ console.error('spex eval: add [.|<node>] [--scenario <name>] (--pass|--fail) [--note <text>] [--image <path> …repeatable] [--result <path|->] [--video <path>] [--timeline <json>] | ls [.|<node>] [--json] | ls --session <SEL> [--export] | scenario ls [<node>|.] [--unmeasured] [--json] | lint [--changed] | ok <node> [--scenario <name>] | retract [.|<node>] [--scenario <name>] [--last | --ts <iso>] [--note <why>] | clean [--keep-latest|--all]')
604
665
  return 2
605
666
  }
606
667
 
@@ -2,8 +2,8 @@ import { relative, dirname } from 'node:path'
2
2
  import { repoRoot, driftIndex, historyIndex, type DriftIndex, type HistoryIndex } from '../../spec-cli/src/git.js'
3
3
  import { loadSpecs } from '../../spec-cli/src/specs.js'
4
4
  import { loadEvalRemarkTracks, trackKey, type RemarkTrack, type Issue, type Reply } from '../../spec-cli/src/issues.js'
5
- import { evalNodes, type EvalNode } from './scenarios.js'
6
- import { readSidecar, applyRetractions, evidenceOf, isJsonBlob, type Verdict, type EvidenceKind, type Retraction } from './sidecar.js'
5
+ import { evalNodes, type EvalNode, type ScenarioTestReference } from './scenarios.js'
6
+ import { readSidecar, applyRetractions, evidenceOf, isJsonBlob, humanOkFor, type Verdict, type EvidenceKind, type Retraction } from './sidecar.js'
7
7
  import { staleAxes, codeDrift, contentProbeFor, type StaleAxis } from './freshness.js'
8
8
  import { scenarioIndex, type ScenarioIndex } from './scenariofresh.js'
9
9
  import { hasBlob, getBlob, MISS_BLOB } from './cache.js'
@@ -64,6 +64,10 @@ export type EvalEntry = {
64
64
  // so the eval detail pane reads its whole comment thread from the reading overlay — the counterpart to
65
65
  // splitting eval-remark threads OUT of the issue surfaces (mergedIssues). Absent until the first remark.
66
66
  thread?: Issue
67
+ // the human sign-off bound to THIS reading ([[human-ok]]) — present only on the exact reading an ok row
68
+ // anchors (by scenario + ts), so a newer reading arrives unblessed and the feed's hide releases itself.
69
+ // Rides the board fold verbatim (latestPerScenario is a filter, never a projection).
70
+ humanOk?: { by: string; ts: string }
67
71
  }
68
72
 
69
73
  // a remark overlaid onto its display host (a reading, above) → the RemarkView the surfaces read.
@@ -87,7 +91,7 @@ function toRemarkView(rm: Reply, threadId: string, dangling: boolean): RemarkVie
87
91
  // at NODE level. `scenario` is the orphaned name (rendered struck-through / gone); `remarks` are all dangling.
88
92
  export type DanglingTrack = { scenario: string; threadId: string; thread: Issue; remarks: RemarkView[] }
89
93
 
90
- export type ScenarioInfo = { name: string; expected: string; tags?: string[]; code?: string[] }
94
+ export type ScenarioInfo = { name: string; expected: string; tags?: string[]; test?: ScenarioTestReference; code?: string[] }
91
95
 
92
96
  // `hasEvalFile` distinguishes a node that declares no scenarios (no eval.md) from one that declares some but
93
97
  // has no readings yet — the tab says different things for each. `scenarios` is the declared set; `readings`
@@ -160,11 +164,12 @@ export async function evalTimeline(id: string, ctx?: EvalContext): Promise<EvalT
160
164
  const threadFor = (scenario: string): Issue | undefined => tracks.get(trackKey(id, scenario))?.thread
161
165
  const scenarios: ScenarioInfo[] = ynode.scenarios.map((s) => ({
162
166
  name: s.name, expected: s.expected,
163
- ...(s.tags?.length ? { tags: s.tags } : {}), ...(s.code?.length ? { code: s.code } : {}),
167
+ ...(s.tags?.length ? { tags: s.tags } : {}), ...(s.test ? { test: s.test } : {}),
168
+ ...(s.code?.length ? { code: s.code } : {}),
164
169
  }))
165
170
  // one raw sidecar read: the effective readings feed the scoreboard rows below; the retraction events ride
166
- // along as the undo trace (newest-first, like the readings).
167
- const { readings: rawReadings, retractions } = readSidecar(ynode.sidecarPath)
171
+ // along as the undo trace (newest-first, like the readings), the human-ok events as the sign-off overlay.
172
+ const { readings: rawReadings, retractions, oks } = readSidecar(ynode.sidecarPath)
168
173
  // the off-history content fallback ([[eval-core]]): fed to both git axes so a rebased/folded-away
169
174
  // anchor with byte-identical governed content reads fresh. Lazy — an in-history reading never probes.
170
175
  const probe = contentProbeFor(root)
@@ -183,6 +188,9 @@ export async function evalTimeline(id: string, ctx?: EvalContext): Promise<EvalT
183
188
  // first) drives the scalar compat fields for single-evidence consumers.
184
189
  const evidence: EvidenceView[] = evidenceOf(r).map((e) => ({ hash: e.hash, kind: e.kind, state: hasBlob(e.hash) ? 'present' : 'miss' }))
185
190
  const primary = evidence.find((e) => e.kind === 'video') ?? evidence[0]
191
+ // the sign-off join ([[human-ok]]): the ok binds by exact (scenario, ts), so only the very reading the
192
+ // human blessed carries it — a newer or retract-revealed reading reads unblessed.
193
+ const okRow = humanOkFor(oks, r.scenario, r.ts)
186
194
  return {
187
195
  scenario: r.scenario,
188
196
  expected: byName.get(r.scenario)?.expected ?? '',
@@ -200,6 +208,7 @@ export async function evalTimeline(id: string, ctx?: EvalContext): Promise<EvalT
200
208
  ...(drift.length ? { codeDrift: drift } : {}),
201
209
  blobState: primary ? primary.state : 'none',
202
210
  ...(threadFor(r.scenario) ? { thread: threadFor(r.scenario) } : {}),
211
+ ...(okRow ? { humanOk: { by: okRow.by, ts: okRow.ts } } : {}),
203
212
  }
204
213
  })
205
214
  readings.reverse() // newest-first
@@ -0,0 +1,43 @@
1
+ import { relative } from 'node:path'
2
+ import { repoRoot } from '../../spec-cli/src/git.js'
3
+ import { commitTrunkData } from '../../spec-cli/src/localIssues.js'
4
+ import { evalNodes, resolveEvalNode } from './scenarios.js'
5
+ import { readReadings, readSidecar, appendHumanOk, humanOkFor, type HumanOk } from './sidecar.js'
6
+
7
+ // @@@ human-ok - the human sign-off on an eval reading ([[human-ok]]). One write, both surfaces: the CLI
8
+ // (`spex eval ok`) and the HTTP route (`POST /api/specs/:id/evals/ok`, identity server-derived 'human')
9
+ // call THIS. The ok binds to ONE immutable reading — the scenario's latest effective reading at ok-time,
10
+ // anchored by its (ts, codeSha) — and is MONOTONIC: no un-ok verb exists, because a newer reading is a
11
+ // different object the ok never transfers to, and staleness is computed live; both bring the scenario back
12
+ // on their own. Durability follows the checkout: on the trunk checkout the append is committed straight to
13
+ // trunk (`--no-verify`, path-scoped, under the shared store lock — the [[local-issues]] discipline,
14
+ // commitTrunkData); on a linked worktree the append stays in that tree and the session's own ritual commit
15
+ // carries it, exactly like every other sidecar write.
16
+ export type OkResult =
17
+ | { ok: true; humanOk: HumanOk; already: boolean; landed: 'committed' | 'uncommitted' }
18
+ | { ok: false; error: string }
19
+
20
+ export function fileHumanOk(nodeId: string, scenario: string, by: string): OkResult {
21
+ const root = repoRoot()
22
+ // the same loud resolution every eval verb applies ([[eval-core]]): exact canonical id, else a unique
23
+ // bare leaf; an ambiguous leaf returns the candidate list instead of blessing an arbitrary node.
24
+ const res = resolveEvalNode(evalNodes(root), nodeId)
25
+ if (!res.ok) return { ok: false, error: res.error }
26
+ const node = res.node
27
+ if (!node.scenarios.some((s) => s.name === scenario) &&
28
+ !readSidecar(node.sidecarPath).readings.some((r) => r.scenario === scenario))
29
+ return { ok: false, error: `'${node.id}' has no scenario '${scenario}'` }
30
+ // the ok's one possible target: the latest EFFECTIVE reading — an ok is a judgment on a measurement that
31
+ // exists and currently counts; an unmeasured (or fully-retracted) scenario has nothing to bless.
32
+ const forScenario = readReadings(node.sidecarPath).filter((r) => r.scenario === scenario)
33
+ if (!forScenario.length) return { ok: false, error: `'${node.id}' scenario '${scenario}' has no effective reading — nothing to ok` }
34
+ const latest = forScenario[forScenario.length - 1]
35
+ // a duplicate ok is idempotent success (the store already IS the requested state — the local-issue
36
+ // close's `already` semantics), never an error and never a second appended row.
37
+ const existing = humanOkFor(readSidecar(node.sidecarPath).oks, scenario, latest.ts)
38
+ if (existing) return { ok: true, humanOk: existing, already: true, landed: 'committed' }
39
+ const row: HumanOk = { kind: 'human-ok', scenario, okTs: latest.ts, okSha: latest.codeSha, by, ts: new Date().toISOString() }
40
+ appendHumanOk(node.sidecarPath, row)
41
+ const landed = commitTrunkData(relative(root, node.sidecarPath), `eval(${node.id}): human-ok '${scenario}' @ ${latest.ts} by ${by}`)
42
+ return { ok: true, humanOk: row, already: false, landed: landed === 'not-primary' ? 'uncommitted' : 'committed' }
43
+ }