loki-mode 11.3.0 → 11.3.2

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 (242) hide show
  1. package/README.md +68 -650
  2. package/SKILL.md +5 -5
  3. package/VERSION +1 -1
  4. package/autonomy/completion-council.sh +2 -2
  5. package/autonomy/context-tracker.py +4 -4
  6. package/autonomy/council-v2.sh +1 -1
  7. package/autonomy/grill.sh +1 -1
  8. package/autonomy/lib/done-recognition.sh +1 -1
  9. package/autonomy/lib/efficiency_cost.py +8 -0
  10. package/autonomy/lib/engine10-push.sh +22 -8
  11. package/autonomy/lib/prd-enrich.sh +1 -1
  12. package/autonomy/lib/voter-agents.sh +1 -1
  13. package/autonomy/loki +161 -105
  14. package/autonomy/run.sh +30 -18
  15. package/autonomy/sandbox.sh +1 -1
  16. package/autonomy/tui.sh +6 -5
  17. package/autonomy/verify.sh +1 -1
  18. package/bin/loki +195 -59
  19. package/completions/_loki +8 -0
  20. package/completions/loki.bash +3 -3
  21. package/dashboard/__init__.py +1 -1
  22. package/dashboard/server.py +47 -45
  23. package/docs/AGENT-CHANGE-RECEIPT.md +2 -2
  24. package/docs/ARCHITECTURE.md +337 -0
  25. package/docs/BRANDING.md +124 -0
  26. package/docs/CLI-REFERENCE.md +11 -1
  27. package/docs/COMPONENTS.md +447 -0
  28. package/docs/DEPLOYMENT.md +9 -9
  29. package/docs/DOCKER.md +469 -0
  30. package/docs/INSTALLATION.md +67 -1045
  31. package/docs/KNOWN-ISSUES.md +11 -35
  32. package/docs/OTEL-COLLECTOR.md +24 -0
  33. package/docs/ROUTING-EVIDENCE.md +32 -0
  34. package/docs/TESTING.md +161 -0
  35. package/docs/air-gapped.md +1 -2
  36. package/docs/alternative-installations.md +14 -24
  37. package/docs/architecture/DECISIONS.md +333 -0
  38. package/docs/audit-logging.md +7 -7
  39. package/docs/authentication.md +3 -3
  40. package/docs/authorization.md +1 -1
  41. package/docs/certification/04-production-deployment/lab.md +6 -6
  42. package/docs/certification/04-production-deployment/lesson.md +4 -4
  43. package/docs/certification/README.md +1 -1
  44. package/docs/control-plane-container.md +2 -2
  45. package/docs/dev/real-run.md +34 -0
  46. package/docs/dev/release-checklist.md +2 -2
  47. package/docs/environment-variables.md +1 -0
  48. package/docs/exit-codes.md +3 -3
  49. package/docs/git-workflow.md +3 -3
  50. package/docs/metrics.md +37 -55
  51. package/docs/network-security.md +1 -1
  52. package/docs/siem-integration.md +5 -5
  53. package/docs/spec-first-intent.md +28 -0
  54. package/docs/v10/CLI-MODERN.md +7 -3
  55. package/docs/v10/DECISIONS.md +59 -0
  56. package/docs/v10/DEPS.md +22 -22
  57. package/docs/v10/ENGINE.md +1 -1
  58. package/docs/v10/FAILURE-CLASSES.md +212 -1
  59. package/docs/v10/FOUNDER-QUEUE.md +1 -0
  60. package/docs/v10/GUARDS.md +31 -0
  61. package/docs/v10/GUIDE.md +7 -5
  62. package/docs/v10/LEGACY-REMOVAL.md +4 -6
  63. package/docs/v10/METRICS.md +14 -2
  64. package/docs/v10/MODERNIZE.md +1 -1
  65. package/docs/v10/OPERATING-MODEL.md +4 -3
  66. package/docs/v10/PROGRESS.md +35 -0
  67. package/docs/v11/COST-HALF.md +352 -0
  68. package/docs/v11/INTOTO-AGENT-CHANGE-PREDICATE.md +77 -0
  69. package/docs/v11/MARKET-SLICES.md +315 -0
  70. package/docs/v11/MCP-2026-07-28.md +55 -0
  71. package/docs/v11/REGISTRIES.md +730 -0
  72. package/docs/v11/VERIFY-PR-SANDBOX.md +41 -0
  73. package/loki-ts/data/model-pricing.json +52 -24
  74. package/loki-ts/dist/loki.js +1185 -945
  75. package/mcp/__init__.py +1 -1
  76. package/mcp/v10_tools.py +29 -0
  77. package/package.json +9 -11
  78. package/packages/control-plane/dist/ask-tools-server.js +8 -2
  79. package/packages/control-plane/dist/server.js +15832 -12361
  80. package/packages/control-plane/drizzle/0006_token_sessions.sql +1 -0
  81. package/packages/control-plane/drizzle/meta/0006_snapshot.json +807 -0
  82. package/packages/control-plane/drizzle/meta/_journal.json +7 -0
  83. package/packages/control-plane/src/db/schema.ts +1 -0
  84. package/packages/control-plane/src/server/routes/cost.ts +4 -3
  85. package/packages/control-plane/src/server/runs.ts +3 -3
  86. package/packages/control-plane/ui/dist/assets/index-DbLzr87u.js +190 -0
  87. package/packages/control-plane/ui/dist/index.html +1 -1
  88. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  89. package/providers/opencode.sh +2 -3
  90. package/src/observability/genai-spans.js +104 -0
  91. package/src/observability/otel-bridge.js +49 -1
  92. package/src/observability/otel.js +2 -2
  93. package/src/protocols/mcp-client.js +6 -2
  94. package/src/protocols/mcp-server.js +191 -6
  95. package/src/protocols/transport/sse.js +23 -2
  96. package/tools/audit-docs.py +1 -1
  97. package/web-app/dist/assets/{AdminPage-D_e45uTw.js → AdminPage-DuRQZsF9.js} +1 -1
  98. package/web-app/dist/assets/{Avatar-T3nIpoaf.js → Avatar-7GtOQvsO.js} +1 -1
  99. package/web-app/dist/assets/{Badge-BKqb59pP.js → Badge-CYgTnIxj.js} +1 -1
  100. package/web-app/dist/assets/{Button-DjVlkh8_.js → Button-rYSyIu7X.js} +1 -1
  101. package/web-app/dist/assets/{CockpitPage-DXJgLGmh.js → CockpitPage-9adANY4o.js} +1 -1
  102. package/web-app/dist/assets/{ComparePage-BRP_eVc_.js → ComparePage-DEG2oA-R.js} +1 -1
  103. package/web-app/dist/assets/{ErrorBoundary-CLjpGLjC.js → ErrorBoundary-CwtvY4xs.js} +1 -1
  104. package/web-app/dist/assets/{EvidenceReceiptPanel-DX-vb4Xy.js → EvidenceReceiptPanel-DTT73pPB.js} +1 -1
  105. package/web-app/dist/assets/{GitHubIssuesPanel-214HVHtX.js → GitHubIssuesPanel-CpWZLz2x.js} +1 -1
  106. package/web-app/dist/assets/{GitHubPRsPanel-BEiZKc4t.js → GitHubPRsPanel-H293R7E2.js} +1 -1
  107. package/web-app/dist/assets/{HomePage-VvMHS5ga.js → HomePage-B11Imp9k.js} +2 -2
  108. package/web-app/dist/assets/{LoginPage-5WD-v7n3.js → LoginPage-BZBgx5Jr.js} +1 -1
  109. package/web-app/dist/assets/{MagicPage-DUQT_Jye.js → MagicPage-B27S4_X8.js} +1 -1
  110. package/web-app/dist/assets/{MetricsPage-lga-z79v.js → MetricsPage-CfbJWNDG.js} +1 -1
  111. package/web-app/dist/assets/{NotFoundPage-CGhBX3G1.js → NotFoundPage-Tp_4zMSm.js} +1 -1
  112. package/web-app/dist/assets/{ProjectPage-CdzF2GzZ.js → ProjectPage-CM4fgMsV.js} +3 -3
  113. package/web-app/dist/assets/{ProjectsPage-DRG64N8s.js → ProjectsPage-DP2Fvsrd.js} +1 -1
  114. package/web-app/dist/assets/{SettingsPage-5fzW9Frb.js → SettingsPage-C4G9-EAy.js} +1 -1
  115. package/web-app/dist/assets/{ShowcasePage-DrTdxO4y.js → ShowcasePage-Dj-n4dwu.js} +1 -1
  116. package/web-app/dist/assets/{SystemSettingsPage-C9Mqs6Rd.js → SystemSettingsPage-BqMLpuGt.js} +1 -1
  117. package/web-app/dist/assets/{TeamsPage-CUGPdSWo.js → TeamsPage-mIH8H96m.js} +1 -1
  118. package/web-app/dist/assets/{TemplatesPage-CNraDY0F.js → TemplatesPage-BagW-v4n.js} +1 -1
  119. package/web-app/dist/assets/{TerminalOutput-BIu_6Pea.js → TerminalOutput-CTQRHhnK.js} +1 -1
  120. package/web-app/dist/assets/{activity-Bk-kgqaf.js → activity-DNQiBDDL.js} +1 -1
  121. package/web-app/dist/assets/{bell-BXmUdmo8.js → bell-Coiwdad1.js} +1 -1
  122. package/web-app/dist/assets/{bot-CiuzXxUR.js → bot-Cou-rTgR.js} +1 -1
  123. package/web-app/dist/assets/{check-BEzjXppd.js → check-CsF-XtQp.js} +1 -1
  124. package/web-app/dist/assets/{chevron-left-AYWCXHf8.js → chevron-left-BJiy55Ht.js} +1 -1
  125. package/web-app/dist/assets/{circle-alert-BNg2uSRq.js → circle-alert-D6-pQQWq.js} +1 -1
  126. package/web-app/dist/assets/{clock-DTj0kkGT.js → clock-BOGqwgt9.js} +1 -1
  127. package/web-app/dist/assets/{cloud-x4nTDBLT.js → cloud-DsxhTtf5.js} +1 -1
  128. package/web-app/dist/assets/{code-xml-BVh_MLrk.js → code-xml-CxJDHUlE.js} +1 -1
  129. package/web-app/dist/assets/{database-DdfBaowG.js → database-BJePscl_.js} +1 -1
  130. package/web-app/dist/assets/{dollar-sign-XuYFmAOY.js → dollar-sign-6qWGAkZo.js} +1 -1
  131. package/web-app/dist/assets/{file-code-corner-D4cRnmeL.js → file-code-corner-DcOtYeDb.js} +1 -1
  132. package/web-app/dist/assets/{file-plus-CyoVlHc_.js → file-plus-WuRyLFo0.js} +1 -1
  133. package/web-app/dist/assets/{globe-Y6IK2RPW.js → globe-r-xCP4DE.js} +1 -1
  134. package/web-app/dist/assets/{hammer-BKzr9VZI.js → hammer-DTt0SsCB.js} +1 -1
  135. package/web-app/dist/assets/{index-B20cSx5G.js → index-BkbXyH8y.js} +3 -3
  136. package/web-app/dist/assets/{layers-DebNOhGS.js → layers-BvZhvSMY.js} +1 -1
  137. package/web-app/dist/assets/{loader-circle-DyGbrEgd.js → loader-circle-3gosTei9.js} +1 -1
  138. package/web-app/dist/assets/{lock-BKPpyt20.js → lock-CZZMxP0U.js} +1 -1
  139. package/web-app/dist/assets/{package-egF5Wi4y.js → package-C5A8CkJr.js} +1 -1
  140. package/web-app/dist/assets/{plus-OiEgKIc-.js → plus-Btv7tTIF.js} +1 -1
  141. package/web-app/dist/assets/{refresh-cw-CzKvrONq.js → refresh-cw-DsWLXgs_.js} +1 -1
  142. package/web-app/dist/assets/{rotate-ccw-Bu2mII0p.js → rotate-ccw-DOVDjway.js} +1 -1
  143. package/web-app/dist/assets/{scroll-text-CSkiyax8.js → scroll-text-smySzlrX.js} +1 -1
  144. package/web-app/dist/assets/{server-DlPthJFw.js → server-b5PHsqAU.js} +1 -1
  145. package/web-app/dist/assets/{shield-alert-CbpHWQQX.js → shield-alert-BD7FDlh-.js} +1 -1
  146. package/web-app/dist/assets/{thumbs-up-DgGpZe-Q.js → thumbs-up-CAsFzbyE.js} +1 -1
  147. package/web-app/dist/assets/{trash-2-BV3GlCB-.js → trash-2-BJ35qHgl.js} +1 -1
  148. package/web-app/dist/assets/{trending-down-Y4TJtXhD.js → trending-down-Ozrd26cZ.js} +1 -1
  149. package/web-app/dist/assets/{trending-up-DsDNoHZY.js → trending-up-BhnMcM32.js} +1 -1
  150. package/web-app/dist/assets/{upload-gkThrc_I.js → upload-JVHfdwoA.js} +1 -1
  151. package/web-app/dist/assets/{usePolling-jpNSeNAe.js → usePolling-CTzbDV-e.js} +1 -1
  152. package/web-app/dist/assets/{user-3Zez1WOK.js → user-Bks3-GYv.js} +1 -1
  153. package/web-app/dist/index.html +1 -1
  154. package/docs/ARCHITECTURE-OVERVIEW.md +0 -209
  155. package/docs/AUTONOMI-ECOSYSTEM.md +0 -107
  156. package/docs/COCKPIT-SPEC.md +0 -72
  157. package/docs/COCKPIT-UX-MANDATE.md +0 -53
  158. package/docs/COMPETITIVE-ANALYSIS.md +0 -303
  159. package/docs/COMPETITIVE-NEXT-10.md +0 -316
  160. package/docs/COMPETITIVE-RALPH-ZEROSHOT-2026-07-08.md +0 -81
  161. package/docs/DASHBOARD-9.12-EVIDENCE.md +0 -97
  162. package/docs/DASHBOARD-ARCHITECTURE.md +0 -425
  163. package/docs/DASHBOARD_V2_CHECKLIST.md +0 -243
  164. package/docs/DEMOS.md +0 -63
  165. package/docs/ENTERPRISE-IDENTITY-ROADMAP.md +0 -206
  166. package/docs/EVIDENCE-RECEIPT-BUILD-APPLICABILITY.md +0 -79
  167. package/docs/FEAT-PRDREUSE-DOCKER-PLAN.md +0 -144
  168. package/docs/GATE-FAILURE-TRIAGE.md +0 -254
  169. package/docs/HANDOFF-2026-08-03.md +0 -439
  170. package/docs/LOOP-CANDIDATE-PROPOSAL-v1.md +0 -167
  171. package/docs/LOOP-HARNESS-AUDIT.md +0 -684
  172. package/docs/MERGE-DEDUP-MAP.md +0 -420
  173. package/docs/MERGE-ROUTE-MAP.md +0 -139
  174. package/docs/MODEL-REGISTRY-PLAN.md +0 -155
  175. package/docs/OUTCOME-FRONTIER.md +0 -536
  176. package/docs/P2-SPEC-ROBUSTNESS-PLAN.md +0 -192
  177. package/docs/PLANS-INDEX.md +0 -32
  178. package/docs/PRD-REUSE-DONE-RECOGNITION-PLAN.md +0 -536
  179. package/docs/PROMPT-ABLATION-RESULT.md +0 -97
  180. package/docs/R4-TRUST-TRAJECTORY-DESIGN.md +0 -127
  181. package/docs/R5-AUTO-WIKI-DESIGN.md +0 -137
  182. package/docs/R6-ROLLBACK-CHECKPOINT-PLAN.md +0 -107
  183. package/docs/R7-ZERO-CONFIG-FIRST-RUN-PLAN.md +0 -137
  184. package/docs/R8-SHAREABLE-TEAM-ASSETS-PLAN.md +0 -129
  185. package/docs/R9-OPEN-CORE-HOOKS-PLAN.md +0 -113
  186. package/docs/RARV-C-100X-PLAN.md +0 -286
  187. package/docs/RARV-C-LOOP-EFFICIENCY-PLAN.md +0 -470
  188. package/docs/SHAREABLE-PROOF-PLAN.md +0 -194
  189. package/docs/SONNET5-DEFAULT-PLAN.md +0 -157
  190. package/docs/SPEED-AND-FIRST-PASS-PLAN.md +0 -281
  191. package/docs/STALE-STATE-AUDIT.md +0 -174
  192. package/docs/SYNERGY-TASKS.md +0 -143
  193. package/docs/TASKLIST-ACCURACY-PLAN.md +0 -355
  194. package/docs/TOP-100-BACKLOG.md +0 -159
  195. package/docs/V8-ACCEPTANCE-TRIAGE-2026-07-24.md +0 -114
  196. package/docs/V8-COMPLEXITY-AUDIT-2026-07-24.md +0 -45
  197. package/docs/V8-MAJOR-RELEASE-PLAN.md +0 -137
  198. package/docs/V8-OVERNIGHT-PLAN-2026-07-25.md +0 -82
  199. package/docs/V8-SDK-RESEARCH-RAW.md +0 -129
  200. package/docs/WELCOME-OPENER-PLAN.md +0 -106
  201. package/docs/adoption-baseline-2026-07-31.md +0 -84
  202. package/docs/architecture/bmad-integration-epic.md +0 -271
  203. package/docs/architecture/bmad-integration-review.md +0 -86
  204. package/docs/architecture/bmad-integration-validation.md +0 -249
  205. package/docs/architecture/bmad-loki-voice-agent-council-analysis.md +0 -61
  206. package/docs/auto-claude-comparison.md +0 -250
  207. package/docs/bug-fixes/agent-01-cli-fixes.md +0 -101
  208. package/docs/bug-fixes/agent-02-purplelab-fixes.md +0 -88
  209. package/docs/bug-fixes/agent-03-dashboard-fixes.md +0 -119
  210. package/docs/bug-fixes/agent-04-memory-fixes.md +0 -105
  211. package/docs/bug-fixes/agent-05-provider-fixes.md +0 -86
  212. package/docs/bug-fixes/agent-06-integration-fixes.md +0 -101
  213. package/docs/bug-fixes/agent-07-dash-run-fixes.md +0 -101
  214. package/docs/bug-fixes/agent-08-docker-fixes.md +0 -164
  215. package/docs/bug-fixes/agent-09-e2e-build-fixes.md +0 -69
  216. package/docs/bug-fixes/agent-10-e2e-fullstack-fixes.md +0 -102
  217. package/docs/bug-fixes/agent-11-e2e-session-fixes.md +0 -70
  218. package/docs/bug-fixes/agent-12-scenario-fixes.md +0 -120
  219. package/docs/bug-fixes/agent-13-enterprise-fixes.md +0 -143
  220. package/docs/bug-fixes/agent-14-uat-newuser-fixes.md +0 -88
  221. package/docs/bug-fixes/agent-15-uat-poweruser-fixes.md +0 -132
  222. package/docs/bug-fixes/agent-19-code-review.md +0 -316
  223. package/docs/bug-fixes/agent-20-architecture-review.md +0 -331
  224. package/docs/competitive/bolt-new-analysis.md +0 -579
  225. package/docs/competitive/emergence-others-analysis.md +0 -605
  226. package/docs/competitive/replit-lovable-analysis.md +0 -622
  227. package/docs/dashboard-guide.md +0 -487
  228. package/docs/enterprise/migration.md +0 -265
  229. package/docs/history/one-run-audit.md +0 -304
  230. package/docs/plans/FORGE-AUTONOMOUS-QUEUE.md +0 -312
  231. package/docs/plans/ULTRAPLAN-FORGE-BAAS.md +0 -612
  232. package/docs/plans/UT2-6-LSP-DIAGNOSTIC-BROADCAST.md +0 -394
  233. package/docs/prd-purple-lab-ide.md +0 -152
  234. package/docs/prd-purple-lab-platform-v2.md +0 -223
  235. package/docs/prd-purple-lab-platform.md +0 -319
  236. package/docs/retrospectives/v7.5.15-fleet-postmortem.md +0 -325
  237. package/docs/retrospectives/v7.5.15-honesty-audit.md +0 -136
  238. package/docs/retrospectives/v7.5.15-llm-failure-modes.md +0 -49
  239. package/docs/show-hn-post.md +0 -46
  240. package/docs/test-scenarios/enterprise-scenarios.md +0 -732
  241. package/docs/thick2thin.md +0 -173
  242. package/packages/control-plane/ui/dist/assets/index-DcBJXjCJ.js +0 -190
@@ -1,81 +0,0 @@
1
- # Competitive Analysis: Loki vs ralph-tui + zeroshot (2026-07-08, honest)
2
-
3
- Source: fanned-out research agent, verified against competitor repos/docs + the
4
- Loki repo. Lead with the uncomfortable truths.
5
-
6
- ## The uncomfortable truth
7
- zeroshot (github.com/the-open-engine/zeroshot) has the SAME trust thesis as Loki
8
- -- "the verifier stays independent from the model that wrote the code": blind
9
- validators, fail-closed gates, actionable rejections, complexity tiers, SQLite
10
- resume -- implemented MORE LEGIBLY and more focused. Loki is NOT uniquely "the
11
- verification tool." Claiming blind-review / independent-verifier as a Loki-only
12
- differentiator would be fabrication (the founder's #1 forbidden outcome).
13
-
14
- ralph-tui (github.com/subsy/ralph-tui) is a different animal: a terminal-UI
15
- orchestrator for Geoff Huntley's "Ralph" loop (`while :; do cat PROMPT.md |
16
- claude-code; done`). It beats Loki on ERGONOMICS + SIMPLICITY + community
17
- momentum, NOT verification (Ralph pushes verification onto the operator).
18
-
19
- ## What they do BETTER than Loki (honest)
20
- - zeroshot: trust story is TIGHTER + more legible ("blind validation + fail-closed
21
- + actionable rejections" = one sentence a skeptic trusts). Loki's 8+3+1 gates +
22
- council + RARV-C is more thorough but far harder to explain/audit.
23
- - zeroshot: EVIDENCE STALENESS -- fails closed when evidence is stale / older than
24
- IMPLEMENTATION_READY / lacks usable evidence, with published status/timestamp/
25
- command/exitCode/output. Better guarantee than Loki's "gates passed."
26
- - zeroshot: COMPLEXITY-PROPORTIONAL validators (TRIVIAL=0, SIMPLE=1, STANDARD=2,
27
- CRITICAL=5) -- only pays for verification the task warrants. Cost/speed win.
28
- - zeroshot: per-run ISOLATION dial (none/worktree/--docker); native GitLab/Jira/
29
- Azure backends; honest scope discipline ("not for 'make it faster'").
30
- - ralph-tui: the TUI cockpit, multi-machine WebSocket fleet control, radical
31
- legibility, 7 agent CLIs, viral community (awesome-ralph, ~2.4k stars).
32
-
33
- ## What Loki genuinely has that they LACK (verified, not overclaimed)
34
- - Spec-to-DEPLOYED-PRODUCT scope (both competitors stop at task/issue level).
35
- - Persistent cross-project MEMORY (episodic/semantic/procedural + RAG). Neither
36
- has a learning layer (zeroshot's SQLite is run-state, not learned knowledge).
37
- - Anti-sycophancy Devil's-Advocate re-review triggered by unanimity.
38
- - Semantic anti-fake-green DETECTORS (mock-integrity, test-mutation) -- dedicated
39
- deterministic catchers for tautological/fitted tests.
40
- - Legacy-healing mode (brownfield archaeology/stabilize/modernize).
41
- NOTE: breadth is also a LIABILITY -- spec-to-product implies Loki handles
42
- everything, a bigger promise; an over-confident run that fabricates progress on an
43
- ill-defined task is the WORST outcome for a trust brand.
44
-
45
- ## Honest gaps (where a user would prefer a competitor)
46
- 1. Legibility: a skeptic can trust zeroshot/Ralph in minutes; Loki's surface is
47
- hard to audit. A trust product that is itself hard to trust-verify is a real
48
- problem.
49
- 2. Cost/speed proportionality: zeroshot scales 0-5 validators; Loki runs a lot
50
- for every change.
51
- 3. Terminal ergonomics: ralph-tui's cockpit.
52
- 4. Isolation ergonomics: zeroshot's clean --docker dial.
53
- 5. Community/momentum: Loki is comparatively invisible in the public conversation.
54
-
55
- ## Prioritized recommendations (impact-ranked; moat-safe)
56
- 1. [HIGHEST, S-M, zero moat cost] Make the trust layer LEGIBLE + PROVABLE in one
57
- screen: a one-sentence "why you can trust this" + `loki verify --explain` that
58
- prints exactly which gates ran, their evidence, freshness, pass/fail. Loki has
59
- MORE verification than zeroshot but COMMUNICATES/PROVES it worse. This
60
- STRENGTHENS the moat by making it demonstrable.
61
- 2. [M, moat REINFORCEMENT] Evidence STALENESS + tool-neutral handoff contract:
62
- timestamp gate evidence, invalidate on state change, fail closed when stale /
63
- older than the ready-marker / lacks usable evidence.
64
- 3. [M, careful] Complexity-proportional verification: wire detect_complexity() to
65
- scale the gate/council battery like zeroshot's 0/1/2/5 -- but ONLY ADD
66
- validators for hard tasks, NEVER drop below a hard floor for anything
67
- shippable. Cost/speed win with the floor as the guard.
68
- 4. [M, no moat cost] Per-run isolation dial incl. Docker (match none/worktree/docker).
69
- 5. [S, moat reinforcement] Scope honesty: when "done" is undescribable, say so +
70
- degrade to inconclusive (never fabricate). Make it first-class + visible.
71
- 6. [L, nice-to-have] A real terminal cockpit / strong TUI status view.
72
- 7. [M] Broaden issue backends (GitLab/Jira/Azure) for team parity.
73
-
74
- ## Single most important move + the one thing NOT to copy
75
- - DO: make the trust layer provable to a skeptic in 60 seconds (recs 1+2). Loki
76
- likely has more verification than zeroshot but demonstrates it worse; in a
77
- market where buyers are skeptical, the tool that PROVES trust fastest wins.
78
- - DO NOT COPY: Ralph's "operator supplies verification / senior babysits / ~90%
79
- then human takes over." It is simpler + viral but the OPPOSITE of Loki's thesis.
80
- Pushing verification back onto the user deletes the moat to chase a crowd that
81
- was never Loki's.
@@ -1,97 +0,0 @@
1
- # Dashboard 9.12: what was measured before any code was written
2
-
3
- Every number here was measured on 2026-08-03 against the working tree at
4
- `76e42773`. Commands are included so each can be re-run rather than trusted.
5
-
6
- ## The premise, corrected
7
-
8
- An earlier pass in this session measured "is the dashboard rendering fabricated
9
- data" and answered mostly no: 38 of 43 components call a real API, and only two
10
- files contain `Math.random`. That measurement was sound and it answered the
11
- wrong question.
12
-
13
- The founder directive does not ask whether data is fabricated. It asks whether
14
- **every metric shows source, freshness, unknown, and error state**. That is a
15
- different property, and it is where the work is.
16
-
17
- ## The finding
18
-
19
- The Python readers were built with strict envelope discipline: an empty result
20
- carries a `reason`, an unmeasured number reads `None` rather than `0`, and
21
- `source` names the files consulted. The UI discards most of that.
22
-
23
- | Envelope field | Components consuming it | What its absence means |
24
- |---|---|---|
25
- | `freshness_s` | **0 of 43** | A 40-minute-old poll renders identically to a fresh one |
26
- | `measured` | **0 of 43** | A real cost observation is indistinguishable from an absent one |
27
- | `reason` | 6 of 43 | "no runs" and "could not read runs" render the same in 37 components |
28
- | `source` | 10 of 43 | Most rows cannot be audited back to the file they came from |
29
-
30
- ```bash
31
- cd legacy-ui
32
- grep -l "freshness_s" components/*.js core/*.js | wc -l # 0
33
- grep -l "measured" components/*.js core/*.js | wc -l # 0
34
- ```
35
-
36
- ### Absent rendered as zero
37
-
38
- ```bash
39
- grep -o '|| 0' components/*.js | wc -l # 116
40
- grep -l '|| 0' components/*.js | wc -l # 23 of 43 components
41
- grep -o '?? 0' components/*.js | wc -l # 8
42
- ```
43
-
44
- 116 occurrences across 23 components. This is the exact violation
45
- `record_is_measured()` exists to prevent, expressed in the UI layer: an absent
46
- measurement rendered as the number zero. From an operator's chair it is
47
- indistinguishable from fabricated data, because a fabricated zero and an
48
- unmeasured zero look the same.
49
-
50
- ### No staleness signal exists
51
-
52
- `stale` appears 112 times but collapses to a SINGLE distinct usage:
53
-
54
- ```bash
55
- grep -h -o ".\{45\}stale.\{25\}" components/*.js | sort -u
56
- # >${st.status === 'running' || st.status === 'stale' ? ...
57
- ```
58
-
59
- It is a server-supplied status enum value, never a client-side computation
60
- over data age. There are no `isStale`, `staleness`, or `stale_s` identifiers.
61
- Nothing in the UI derives freshness from a timestamp.
62
-
63
- ## The one genuine fabrication
64
-
65
- `components/loki-session-timeline.js:_buildPhasesFromStatus` synthesizes a
66
- timeline and renders it as history. Its own comment says so:
67
-
68
- ```js
69
- // Simulate a multi-phase timeline based on iteration count
70
- const phaseOrder = ['planning', 'building', 'testing', 'reviewing'];
71
- const duration = segmentDuration * (0.8 + Math.random() * 0.4);
72
- ```
73
-
74
- Phase NAMES come from a fixed rotation and durations are randomized. Neither is
75
- measured. An operator reads plausible phase boundaries for work that never
76
- happened at those times.
77
-
78
- The other `Math.random` (`loki-log-stream.js:267`) is benign: a unique key for
79
- a real log entry.
80
-
81
- **Fix direction** follows `version_is_ahead`: not a better estimate, an explicit
82
- "phase history not recorded". Whether real phase transitions can be read from
83
- `trust-events.jsonl` must be checked against that file rather than inferred
84
- from the component.
85
-
86
- ## What this means for scope
87
-
88
- The redesign is not a rewrite of 31,924 lines of working component code. It is
89
- making the UI carry the honesty contract the backend already has:
90
-
91
- 1. Every metric renders `unknown` rather than `0` when unmeasured.
92
- 2. Every panel shows data age, and marks itself stale past a threshold.
93
- 3. Every empty state states its `reason` and names its `source`.
94
- 4. Nothing is synthesized and presented as measured.
95
-
96
- Items 1-3 are the founder's four required states (source, freshness, unknown,
97
- error) applied to surfaces that already fetch real data. Item 4 is one bug.
@@ -1,425 +0,0 @@
1
- # Dashboard Architecture: Real-Data, Near-Realtime
2
-
3
- > **Historical:** this describes the classic dashboard, removed in Loki Mode 10.8. The Control Plane is the only UI; see [control-plane-migration.md](control-plane-migration.md).
4
-
5
- Design only. No implementation in this document.
6
-
7
- Status: proposal. Measured against the worktree at
8
- `/Users/lokesh/git/lokimode-anthropic/.claude/worktrees/pre-push-scoped-pytest`
9
- on 2026-08-03.
10
-
11
- ## 0. Executive finding
12
-
13
- The dashboard's problem is not styling and not the UI framework. It is that
14
- several required domains have **no read path from the engine to the browser**.
15
-
16
- But the brief's framing ("six domains have ZERO API surface") is wrong in a way
17
- that makes the work cheaper and changes its ranking. The precise situation is
18
- three different failure modes, and they need three different fixes:
19
-
20
- | Failure mode | Domains | Fix shape | Cost |
21
- |---|---|---|---|
22
- | **A. Route exists, store is never written** | runs | Wire an engine-side writer | Small |
23
- | **B. Writer exists in code, no route reads it** | receipts, tests, artifacts, prompts (partial), releases | Add a read route over the file that writer produces | Small each |
24
- | **C. No source of truth at all** | prompts (main-loop) | Do not build. Mark NOT AVAILABLE | Zero |
25
-
26
- **Precision on mode B.** What is verified is that a *writer exists in source* at
27
- a cited `file:line`. It is **not** verified that an instance exists on disk: this
28
- worktree has never completed a run, `.loki/proofs/` and `.loki/quality/` do not
29
- exist here, and `ls -R .loki/proofs` returns empty. Claiming "the data is
30
- already there" would be exactly the fabrication this document forbids (see R8).
31
- The design conclusion is unchanged, because a read route is written against the
32
- writer's contract, not against a sample file. But the two facts are tracked
33
- separately throughout, and every mode-B row below says which one it is.
34
-
35
- None of these is a UI rewrite. The ranked work is **writer-side and
36
- route-side**. Section 4 concludes that the existing 46-component UI is kept
37
- almost entirely, and that a rewrite is not supportable on the evidence.
38
-
39
- ### Measurement honesty
40
-
41
- Every number below was produced by a command recorded next to it. Where the
42
- brief's number and my measurement disagree, both are shown.
43
-
44
- One correction against myself: my first pass reported `0` aria attributes in the
45
- built artifact and I nearly wrote that up as a shipped accessibility regression.
46
- It was a measurement error. `legacy-ui-static/index.html` has lines long enough
47
- (577 chars plus minified runs) that `grep` treated the file as binary and
48
- suppressed output. With `grep -a` the brief's numbers are confirmed. This is the
49
- `grep-absence false-green` failure mode: an empty result was an absent
50
- measurement, not evidence of absence. Any count in this document taken over a
51
- built artifact uses `grep -a`.
52
-
53
- ## 1. DATA CONTRACT
54
-
55
- ### 1.1 How route counts were measured
56
-
57
- ```
58
- grep -oE '^@app\.(get|post|put|delete|patch|websocket)\("([^"]+)"' \
59
- dashboard/server.py | sed 's/^@app\.\([a-z]*\)("/\1 /' | sort -u # 156
60
- grep -cE '^@app\.(get|post|put|delete|patch|websocket)' dashboard/server.py # 165
61
- ```
62
-
63
- - 165 route decorators in `dashboard/server.py`
64
- - 156 unique (method, path) pairs
65
- - 140 unique paths
66
- - Plus 24 routes in `dashboard/api_v2.py` under prefix `/api/v2`
67
- (`dashboard/api_v2.py:39`), mounted at `dashboard/server.py:1010-1011`
68
-
69
- The brief's "138 routes" is close to the 140 unique-path figure but is not
70
- reproducible from any single command I ran. **The api_v2 router was not counted
71
- in the brief at all**, which is how the runs domain got misclassified.
72
-
73
- `dashboard/server.py` is 12,466 lines.
74
-
75
- ### 1.2 The 15 domains
76
-
77
- Freshness classes used below:
78
-
79
- - **PUSH** - broadcast by `_push_loki_state_loop` (`dashboard/server.py:596`),
80
- 2s while a run is active, 30s idle, 5s with no clients connected
81
- - **MTIME** - file modification time is available and should be returned
82
- - **ON-READ** - computed per request, freshness equals request time
83
- - **NONE** - no freshness signal exists
84
-
85
- | # | Domain | API today | Source of truth on disk | Freshness | Verdict |
86
- |---|---|---|---|---|---|
87
- | 1 | memory | 17 routes | `.loki/memory/` | MTIME | Keep |
88
- | 2 | tasks | 8 routes | `.loki/queue/{pending,in-progress,completed,failed}.json` | PUSH + MTIME | Keep |
89
- | 3 | council | 8 routes | `.loki/quality/reviews/<id>/` (`autonomy/run.sh:12736`) | MTIME | Keep |
90
- | 4 | agents | 5 routes | `.loki/state/agents.json` | PUSH | Keep |
91
- | 5 | cost | 3 routes | `.loki/metrics/budget.json` | PUSH on transition (`server.py:626-633`) | Keep |
92
- | 6 | logs | 2 routes | `.loki/logs/` | MTIME | Keep |
93
- | 7 | health | 3 routes | live process probe | ON-READ | Keep |
94
- | 8 | events | 1 route | `.loki/events.jsonl` (`server.py:7080`) | MTIME + byte offset | Keep, extend |
95
- | 9 | models | 3 routes | `.loki/state/model-override` | MTIME | Keep |
96
- | 10 | providers | 1 route | `.loki/state/{provider,cli-provider}` | MTIME | Keep |
97
- | 11 | **runs** | **5 routes exist** (`/api/v2/runs*`) | **SQL `Run` table, never written by the engine** | NONE | **Fix writer** |
98
- | 12 | **receipts** | 0 routes | `.loki/proofs/<runId>/proof.json` (`loki-ts/src/runner/proof.ts:117`) | MTIME | **Add route** |
99
- | 13 | **tests** | 0 routes | `.loki/quality/test-results.json` (`autonomy/run.sh:4781`), `.loki/verification/playwright-results.json` | MTIME | **Add route** |
100
- | 14 | **artifacts** | 0 routes | `.loki/app-runner/state.json` (`run.sh:4196`), `first-preview.json` (`run.sh:4220`) | MTIME | **Add route** |
101
- | 15 | **releases** | 0 routes | git tags (780 present), `VERSION`, `CHANGELOG.md` | ON-READ | **Add route** |
102
- | - | **prompts** | 0 routes | **Review prompts only**: `.loki/quality/reviews/<id>/<reviewer>-prompt.txt` (`run.sh:14700`). Main-loop prompt is constructed in memory and never persisted. | MTIME (review only) | **Partial. Main loop NOT AVAILABLE** |
103
-
104
- ### 1.3 The six domains, corrected
105
-
106
- This is the table the brief asked for, with the brief's claim next to what the
107
- code says.
108
-
109
- | Domain | Brief says | Measured | Root cause | Work |
110
- |---|---|---|---|---|
111
- | runs | ZERO routes | **5 routes exist**, `api_v2.py` `/runs`, `/runs/{id}`, `/cancel`, `/replay`, `/timeline`; mounted `server.py:1010` | Only writer is `api_v2.py:461 create_run`. Nothing in `autonomy/` writes the `Run` table (grep over `autonomy/*.sh` finds only sqlalchemy dependency checks). Store is structurally empty for every real run. | **P0. Engine-side writer.** Not a new API. |
112
- | receipts | ZERO routes | 0 routes. Confirmed. | Writer at `proof.ts:117` produces `.loki/proofs/<runId>/proof.json`. No instance in this worktree. | **P1. One read route.** |
113
- | tests | ZERO routes | 0 routes. Confirmed. | Writer at `run.sh:4781` produces `.loki/quality/test-results.json`. No instance in this worktree. | **P1. One read route.** |
114
- | artifacts | ZERO routes | 0 routes. Confirmed. | Writer at `run.sh:4196` produces `.loki/app-runner/state.json`. No instance in this worktree. | **P1. One read route.** |
115
- | releases | ZERO routes | 0 routes. Confirmed. | Source is git itself (780 tags present, verified) plus `VERSION`. Only domain whose source is verified to exist. | **P2. One read route.** |
116
- | prompts | ZERO routes | 0 routes. Confirmed. | Review prompts persisted at `run.sh:14700`. Main-loop prompt built in memory, never written. | **P2 for review prompts. Main loop = NOT AVAILABLE.** |
117
-
118
- **The single most important line in this document:** runs is not a missing API.
119
- It is a route reading an empty table. Building a second runs API would produce a
120
- second empty surface.
121
-
122
- ### 1.4 Contract rules for every new endpoint
123
-
124
- Existing precedent to follow, not replace: `/api/events` reads
125
- `.loki/events.jsonl` directly (`dashboard/server.py:7070-7092`), with an
126
- existence check at `:7081` and a size cap at `:7089`. New file-backed domains
127
- copy this shape.
128
-
129
- Every endpoint returns an envelope:
130
-
131
- ```
132
- {
133
- "data": <payload> | null,
134
- "source": "<absolute-ish path or 'git' or 'computed'>",
135
- "observed_at": "<RFC3339, server clock at read>",
136
- "source_mtime": "<RFC3339>" | null,
137
- "state": "ok" | "empty" | "stale" | "unavailable" | "error",
138
- "detail": "<human-readable reason>" | null
139
- }
140
- ```
141
-
142
- Rules, all binding:
143
-
144
- 1. `source` is mandatory. A payload with no stated source is a defect.
145
- 2. An unmeasured value is `null` with `state: "unavailable"`. **Never `0`**, and
146
- never a plausible-looking sample. A zero cost and an unmeasured cost must not
147
- render identically.
148
- 3. `state` distinguishes four failure shapes that the current code collapses:
149
- - `empty` - source exists, has no records (a run with no receipts yet)
150
- - `unavailable` - source file absent (engine never ran)
151
- - `stale` - source older than the domain's freshness budget
152
- - `error` - source present but unparseable
153
- 4. `source_mtime` is `null` only for ON-READ domains.
154
- 5. HTTP status stays 200 for `empty` / `stale`, 503 for `unavailable`, 500 for
155
- `error`. The UI must be able to distinguish these without parsing prose.
156
-
157
- The existing 33 honesty markers in `server.py`
158
- (`grep -oaE '"(unknown|unavailable|not_measured|UNKNOWN|not available)"' | wc -l`)
159
- are the seed of this convention. The brief's "54" is not reproducible by that
160
- command; I report 33 and the command that produced it. Either way, the markers
161
- are ad hoc string literals today. The envelope makes the convention
162
- machine-checkable instead of a naming habit.
163
-
164
- ## 2. REALTIME MODEL
165
-
166
- ### 2.1 What exists
167
-
168
- - `/ws` at `dashboard/server.py:2499`. Auth by query parameter (`:2523-2540`)
169
- because browsers cannot set headers on WS upgrade.
170
- - `ConnectionManager` at `:478`. `MAX_CONNECTIONS` default 100 (`:482`).
171
- - `broadcast` at `:512` fans out concurrently with a per-client
172
- `SEND_TIMEOUT_SECONDS = 5.0` (`:510`) and **drops** any client that times out
173
- (`:535-539`). Backpressure is already handled by disconnection, not buffering.
174
- - Liveness: server pings after 30s of silence, closes after 2 missed pongs
175
- (`:2554-2588`).
176
- - `_push_loki_state_loop` at `:596`. Reads `dashboard-state.json`; broadcasts
177
- only when mtime changed (`:670-672`). Budget (`:626`) and trust (`:646`)
178
- transitions bypass the mtime gate.
179
- - Other realtime surfaces: `/ws/collab`, `/api/managed/events`,
180
- `/api/health/processes`.
181
-
182
- Built artifact realtime constructs: 9
183
- (`grep -oaE "new WebSocket|EventSource|setInterval" legacy-ui-static/index.html | wc -l`).
184
- The brief's "34 realtime constructs" is not reproducible by any pattern I tried;
185
- I mark that figure **not independently verified** rather than repeat it.
186
-
187
- ### 2.2 The three gaps
188
-
189
- **Gap 1 - silence is ambiguous.** `if mtime != last_mtime` (`:670`) means an
190
- unchanged file produces no broadcast at all. A client cannot distinguish
191
- "nothing changed" from "the writer died" from "my socket is half-open." This is
192
- the exact defect the staleness requirement exists to prevent.
193
-
194
- *Design answer:* every broadcast payload carries `source_mtime` and
195
- `observed_at`. The client renders **age**, not just value. Past a per-domain
196
- budget the panel flips to STALE. A panel showing a number with no age is
197
- non-compliant. Silence is never evidence of freshness.
198
-
199
- **Gap 2 - reconnect is blind.** The `connected` message (`:2547`) carries no
200
- state snapshot. A reconnecting client waits for the next mtime change, which on
201
- an idle run may never come.
202
-
203
- *Design answer:* reconnect is REST-first. On open, the client fetches the
204
- snapshot for each mounted panel over HTTP, then applies WS deltas on top. The
205
- socket is an invalidation channel, not the source of truth. This also means a
206
- total WS failure degrades to polling rather than to a blank dashboard.
207
-
208
- **Gap 3 - no ordering or gap detection.** `broadcast` sends bare dicts with no
209
- sequence number. A client cannot detect a dropped message, and since slow
210
- clients are dropped mid-fan-out (`:535-539`), drops are an expected condition,
211
- not an edge case.
212
-
213
- *Design answer:* a monotonic `seq` per connection on every broadcast. Client
214
- tracks last seen; on a gap it re-fetches the REST snapshot rather than trusting
215
- its accumulated state. Ordering is per-connection and total; there is no
216
- cross-domain ordering guarantee and the design does not pretend to one.
217
-
218
- ### 2.3 Staleness budgets
219
-
220
- | Domain class | Budget | Rationale |
221
- |---|---|---|
222
- | Active run state (tasks, agents) | 6s | Push loop runs at 2s; 3 missed cycles |
223
- | Cost / budget | 60s | Transition-pushed, not periodic |
224
- | Receipts, tests, artifacts | 120s | Written at phase boundaries, not continuously |
225
- | Releases | 1h | Git tags change on release only |
226
- | Health | 15s | ON-READ, staleness means the probe itself is failing |
227
-
228
- On disconnect: panels do not clear and do not freeze silently. The header shows
229
- a single global disconnected state, every panel dims and shows its last
230
- `observed_at` age. Old numbers are never presented as current.
231
-
232
- ## 3. INFORMATION ARCHITECTURE
233
-
234
- The bundle is 779,725 bytes raw, 150,341 gzipped
235
- (`dashboard/server.py:982`, `GZipMiddleware(minimum_size=1024)` at `:1001`).
236
- gzip stays; nothing in this design touches it.
237
-
238
- **Progressive disclosure here cannot mean code-splitting.**
239
- `legacy-ui/scripts/build-standalone.js` produces a single self-contained
240
- file with zero runtime dependencies, written to both `legacy-ui-static/` and
241
- `dist/`. That property is why the dashboard works offline and inside Docker
242
- without a CDN. Splitting the bundle would trade a real capability for a
243
- transfer-size win that gzip has already mostly taken (5.2x). So disclosure is
244
- implemented as **deferred render and deferred fetch**, not deferred download.
245
- The bytes arrive once; the work does not.
246
-
247
- Concretely: a panel below the fold does not fetch, does not subscribe, and does
248
- not render until it is revealed. The cost being managed is request fan-out and
249
- DOM work against a 12k-line server, not kilobytes.
250
-
251
- **First paint (new user, zero config).** One question answered: is anything
252
- running, and is it healthy? Run status, current phase, budget, and a single
253
- verdict tile. If no run has ever happened, this is an empty state that says so
254
- and links to `loki start`. It must not render zeros.
255
-
256
- **One click deep.** Task board, council reviews, cost breakdown, test results,
257
- receipts, logs. Each fetches on reveal.
258
-
259
- **Expert only.** Memory browser and graph, prompt optimizer, migration
260
- dashboard, audit viewer, tenant switcher, raw event stream. These are the
261
- heaviest panels and the least often needed; they stay behind explicit
262
- navigation.
263
-
264
- ## 4. MIGRATION
265
-
266
- Measured on the shipped artifact with `grep -a`:
267
-
268
- | Property | Count | Command |
269
- |---|---|---|
270
- | Custom elements registered | 39 | `grep -oa "customElements.define" legacy-ui-static/index.html \| wc -l` |
271
- | `aria-*` attributes | 100 | `grep -oa "aria-[a-z]*=" ... \| wc -l` |
272
- | `aria-` occurrences | 113 | `grep -oa "aria-" ... \| wc -l` |
273
- | `role="` | 38 | `grep -oa 'role="' ... \| wc -l` |
274
- | `@media` breakpoints | 13 | `grep -oa "@media" ... \| wc -l` |
275
- | Component source files | 43 | `ls legacy-ui/components \| grep -v vendor \| wc -l` |
276
- | Registrations in source | 44 | `grep -roha "customElements.define" components core \| wc -l` |
277
-
278
- The brief's 113 aria and 41 role are consistent with mine within pattern choice
279
- (113 counts the bare `aria-` prefix, 100 counts full attributes). The component
280
- figure is 43 files carrying 44 registrations, not 46.
281
-
282
- **One gap worth noting:** source registers 44 custom elements, the shipped
283
- artifact registers 39. Five components exist in `legacy-ui/` and do not reach
284
- the browser. This is out of scope for this document and I did not chase it, but
285
- it is recorded here because it means the source tree is not a reliable proxy for
286
- what ships. All acceptance criteria in section 5 therefore measure the **built
287
- artifact**, not source.
288
-
289
- ### Decision: keep
290
-
291
- **Nothing is deleted. Nothing is rewritten.** The justification the brief asked
292
- for cuts the other way: the accessibility work is real and shipped (100 aria
293
- attributes, 38 roles across 39 elements), the zero-dependency build is a genuine
294
- operational asset, and the responsive work exists. A rewrite would spend its
295
- entire budget re-earning properties the codebase already has, while the actual
296
- defect (no data) stayed untouched. A dashboard that is beautifully rebuilt and
297
- still shows nothing real is a worse outcome than the current one.
298
-
299
- | Component set | Action | Reason |
300
- |---|---|---|
301
- | All 39 registered elements | **Keep** | Working, accessible, zero-dependency |
302
- | 138-156 existing routes | **Keep** | Working. No breaking changes |
303
- | `/api/v2/runs*` (5 routes) | **Keep, fix writer** | Route is correct; store is empty |
304
- | Envelope fields on responses | **Add, additive** | New optional keys; existing clients unaffected |
305
- | `seq` on WS broadcasts | **Add, additive** | Unknown keys are ignored by current clients |
306
- | Panels for the 5 new domains | **Add** | New elements alongside existing ones |
307
- | gzip middleware | **Keep untouched** | 5.2x on the wire |
308
-
309
- Every change is additive. The migration has no step at which the current
310
- dashboard is worse than it is today.
311
-
312
- ### Ranked work
313
-
314
- | Rank | Work | Domain | Why first |
315
- |---|---|---|---|
316
- | P0 | Engine appends run lifecycle records at phase boundaries | runs | Unblocks 5 existing routes and the run-manager component. Highest value per unit of work in the entire plan |
317
- | P1 | Read route over `.loki/proofs/<runId>/proof.json` | receipts | Trust core; writer already exists |
318
- | P1 | Read route over `.loki/quality/test-results.json` | tests | Writer already exists |
319
- | P1 | Read route over `.loki/app-runner/state.json` | artifacts | Writer already exists; drives preview |
320
- | P2 | Read route over git tags + `VERSION` | releases | ON-READ, no writer needed, source verified present |
321
- | P2 | Read route over `.loki/quality/reviews/<id>/*-prompt.txt` | prompts (review only) | Partial coverage, honestly labelled |
322
- | P3 | Envelope + `seq` + staleness rendering | all | Correctness of everything above |
323
-
324
- #### Why P0 needs a writer at all (alternative considered and rejected)
325
-
326
- The cheaper option would be a read route over existing files, matching every P1
327
- item and requiring no engine change. It was checked and does not work:
328
-
329
- - `.loki/state/orchestrator.json` holds **current** phase and cumulative metrics
330
- only (`autonomy/run.sh:6225-6226`, read at `:6438-6439`). It is overwritten in
331
- place; there is no per-run history.
332
- - `.loki/dashboard-state.json` is derived from that same file
333
- (`autonomy/run.sh:6543-6556`), so it inherits the same limitation.
334
- - `.loki/sessions/<id>/` contains only `loki.pgid` and `loki.pid`. Process
335
- identity, not lifecycle.
336
- - `.loki/checkout-runs/` contains a single numeric directory and is not a
337
- general run store.
338
-
339
- So current-run state is well served by files, and **run history has no file
340
- source**. A history view cannot be built by reading what exists. That is why
341
- this one item is a writer and everything else is a route.
342
-
343
- Two constraints on that writer, both to bound R1. It appends at **phase
344
- boundaries only**, never inside the iteration inner loop, so the hot path is
345
- untouched. And it is **append-only**, so a crashed run leaves a truncated record
346
- rather than a corrupt one. If phase-boundary granularity later proves too
347
- coarse, the upgrade path is a finer trigger, not a different store.
348
-
349
- Main-loop prompt capture is **not** on this list. It has no source of truth, and
350
- inventing one is a change to the engine's hot path for a dashboard feature. It
351
- renders NOT AVAILABLE.
352
-
353
- ## 5. RISK REGISTER
354
-
355
- | # | Risk | Evidence | Mitigation | Detection |
356
- |---|---|---|---|---|
357
- | R1 | Run writer double-writes or races the engine hot path | `run.sh` is 20k lines; state writes are scattered | Writer is append-only at phase boundaries; never in the iteration inner loop | Run duration before/after must not regress beyond noise |
358
- | R2 | New file reads block the event loop on a large file | `/api/events` already caps reads (`server.py:7089`); learning aggregation reads up to 10MB (`:6904`) | Same size cap and existence check as the events precedent | Response time per new endpoint |
359
- | R3 | `state: "empty"` renders as zero in a panel | Current code has 33 ad hoc markers, no shared convention | Envelope is mandatory; panels bind to `state` before `data` | A panel rendering a number while `state != "ok"` is a test failure |
360
- | R4 | Slow-client drops read as data loss | `broadcast` drops on 5s timeout (`:535-539`) | `seq` gap triggers REST re-fetch | Client-side gap counter |
361
- | R5 | 100-connection ceiling reached | `MAX_CONNECTIONS` (`:482`) | Already returns close code 1013; documented, not raised speculatively | Rejected-connection count |
362
- | R6 | Bundle grows past the point gzip saves it | 779KB raw / 150KB gzipped today | Deferred render, not new dependencies. Zero-dependency property is a hard constraint | Raw and gzipped size per build |
363
- | R7 | Additive envelope breaks a current consumer | Unknown | Fields are added, never renamed or removed | Existing route tests must pass unchanged |
364
- | R8 | Measurement error is mistaken for a defect | Happened during this analysis: `grep` without `-a` reported 0 aria in a file with 113 | All artifact measurements use `grep -a`; empty results are treated as absent measurements | Any claim of "zero X" requires a positive control |
365
-
366
- ## ACCEPTANCE MATRIX
367
-
368
- Every row is measurable by a command. No aspirational entries.
369
-
370
- | # | Criterion | Measurement | Pass condition |
371
- |---|---|---|---|
372
- | A1 | No route regression | `grep -cE '^@app\.(get\|post\|put\|delete\|patch\|websocket)' dashboard/server.py` | >= 165 |
373
- | A2 | api_v2 routes intact | `grep -cE '@router\.(get\|post\|put\|delete)' dashboard/api_v2.py` | >= 24 |
374
- | A3 | Accessibility not regressed | `grep -oa "aria-" legacy-ui-static/index.html \| wc -l` | >= 113 |
375
- | A4 | Roles not regressed | `grep -oa 'role="' legacy-ui-static/index.html \| wc -l` | >= 38 |
376
- | A5 | Custom elements not regressed | `grep -oa "customElements.define" legacy-ui-static/index.html \| wc -l` | >= 39 |
377
- | A6 | Breakpoints not regressed | `grep -oa "@media" legacy-ui-static/index.html \| wc -l` | >= 13 |
378
- | A7 | gzip still active | `grep -c "GZipMiddleware" dashboard/server.py` | >= 1 |
379
- | A8 | Zero runtime dependencies | Built artifact contains no external script/CDN src | 0 external hosts |
380
- | A9 | Runs store is written by the engine | Drive the lifecycle writer directly (source `run.sh`, call the phase-boundary function), then `GET /api/v2/runs`. No provider call, no spend | Returns >= 1 record with a real run id |
381
- | A10 | Every new endpoint states source | Each new route's response | `source` key present and non-empty |
382
- | A11 | Every new endpoint states freshness | Each new route's response | `observed_at` present; `source_mtime` present or explicitly null |
383
- | A12 | Every new endpoint has an error state | Delete or corrupt the source file, call the route | Returns `state` in {`unavailable`,`error`}, never a 200 with zeros |
384
- | A13 | No fabricated zeros | Call each new route with no `.loki/` present | No numeric field is `0`; all are null with `state: "unavailable"` |
385
- | A14 | Staleness is visible | Freeze the source file past its budget | Panel shows STALE and an age |
386
- | A15 | Disconnect is visible | Kill the WS server with the UI open | Every panel dims and shows last-updated age within 10s |
387
- | A16 | Reconnect recovers without a push | Reconnect while source files are unchanged | Panels repopulate from REST |
388
- | A17 | Gap detection works | Drop a broadcast | Client detects `seq` gap and re-fetches |
389
- | A18 | Existing tests pass unchanged | Existing dashboard test suite | 0 failures, 0 modified assertions |
390
-
391
- ### Not accepted as criteria
392
-
393
- Deliberately excluded because they are not measurable as stated: "feels fast",
394
- "enterprise-grade", "near-realtime" without a number, "clean UI", and any
395
- coverage percentage (coverage is not measured in this release per
396
- `skills/quality-gates.md`).
397
-
398
- ## Appendix: contradictions to the brief
399
-
400
- 1. **runs has 5 routes, not zero.** `dashboard/api_v2.py` `/runs`, `/runs/{id}`,
401
- `/runs/{id}/cancel`, `/runs/{id}/replay`, `/runs/{id}/timeline`, mounted at
402
- `dashboard/server.py:1010-1011`. The brief's route grep missed the `api_v2`
403
- router because it uses `@router.` not `@app.`. The real defect is that the
404
- only writer is `api_v2.py:461`; the engine never populates the table.
405
- 2. **prompts is partially available.** Review prompts are persisted at
406
- `autonomy/run.sh:14700` under `.loki/quality/reviews/<id>/<reviewer>-prompt.txt`.
407
- Only the main-loop prompt is unavailable.
408
- 3. **The other four domains have writers in code.** receipts
409
- (`loki-ts/src/runner/proof.ts:117`), tests (`autonomy/run.sh:4781`),
410
- artifacts (`autonomy/run.sh:4196`), releases (git, 780 tags verified
411
- present). "No API" is true; "nothing produces this data" is false. This is
412
- what changes the work from backend construction to route exposure. Note the
413
- limit of the claim: for the first three, the writer is verified in source and
414
- no output instance exists in this worktree.
415
- 4. **Route count is 165 decorators / 156 unique method+path / 140 unique paths**
416
- in `server.py`, plus 24 in `api_v2.py`. Not 138.
417
- 5. **Honesty markers measure 33**, not 54, by
418
- `grep -oaE '"(unknown|unavailable|not_measured|UNKNOWN|not available)"'`.
419
- 6. **"34 realtime constructs" is not reproducible.** I measure 9 in the built
420
- artifact. Marked not independently verified rather than repeated.
421
- 7. **Component count is 43 files / 44 source registrations / 39 shipped
422
- elements**, not 46/39. The source-to-shipped gap of 5 is unexplained and
423
- out of scope here.
424
- 8. The brief's aria (113) and breakpoint (13) figures **are confirmed**, but
425
- only with `grep -a`. Without it the file greps as binary and reports zero.