loki-mode 11.3.0 → 11.3.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.
- package/README.md +68 -650
- package/SKILL.md +4 -4
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +2 -2
- package/autonomy/context-tracker.py +4 -4
- package/autonomy/council-v2.sh +1 -1
- package/autonomy/grill.sh +1 -1
- package/autonomy/lib/done-recognition.sh +1 -1
- package/autonomy/lib/prd-enrich.sh +1 -1
- package/autonomy/lib/voter-agents.sh +1 -1
- package/autonomy/loki +99 -75
- package/autonomy/run.sh +30 -18
- package/autonomy/sandbox.sh +1 -1
- package/autonomy/tui.sh +6 -5
- package/autonomy/verify.sh +1 -1
- package/bin/loki +166 -59
- package/completions/_loki +2 -0
- package/completions/loki.bash +2 -2
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +25 -31
- package/docs/ARCHITECTURE.md +337 -0
- package/docs/BRANDING.md +124 -0
- package/docs/CLI-REFERENCE.md +7 -1
- package/docs/COMPONENTS.md +447 -0
- package/docs/DEPLOYMENT.md +9 -9
- package/docs/DOCKER.md +469 -0
- package/docs/INSTALLATION.md +67 -1045
- package/docs/KNOWN-ISSUES.md +11 -35
- package/docs/TESTING.md +161 -0
- package/docs/air-gapped.md +1 -2
- package/docs/alternative-installations.md +14 -24
- package/docs/architecture/DECISIONS.md +333 -0
- package/docs/audit-logging.md +7 -7
- package/docs/authentication.md +3 -3
- package/docs/authorization.md +1 -1
- package/docs/certification/04-production-deployment/lab.md +6 -6
- package/docs/certification/04-production-deployment/lesson.md +4 -4
- package/docs/certification/README.md +1 -1
- package/docs/control-plane-container.md +2 -2
- package/docs/dev/real-run.md +34 -0
- package/docs/dev/release-checklist.md +2 -2
- package/docs/exit-codes.md +3 -3
- package/docs/git-workflow.md +3 -3
- package/docs/metrics.md +37 -55
- package/docs/network-security.md +1 -1
- package/docs/siem-integration.md +5 -5
- package/docs/v10/CLI-MODERN.md +4 -3
- package/docs/v10/DECISIONS.md +44 -0
- package/docs/v10/DEPS.md +22 -22
- package/docs/v10/ENGINE.md +1 -1
- package/docs/v10/FAILURE-CLASSES.md +118 -1
- package/docs/v10/FOUNDER-QUEUE.md +1 -0
- package/docs/v10/GUARDS.md +13 -0
- package/docs/v10/LEGACY-REMOVAL.md +4 -6
- package/docs/v10/METRICS.md +2 -2
- package/docs/v10/MODERNIZE.md +1 -1
- package/docs/v10/OPERATING-MODEL.md +2 -1
- package/docs/v10/PROGRESS.md +29 -0
- package/docs/v11/COST-HALF.md +352 -0
- package/docs/v11/MARKET-SLICES.md +315 -0
- package/docs/v11/REGISTRIES.md +730 -0
- package/loki-ts/data/model-pricing.json +52 -24
- package/loki-ts/dist/loki.js +1036 -957
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/packages/control-plane/dist/server.js +2467 -736
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/src/protocols/mcp-client.js +6 -2
- package/src/protocols/mcp-server.js +13 -3
- package/tools/audit-docs.py +1 -1
- package/web-app/dist/assets/{AdminPage-D_e45uTw.js → AdminPage-CMkHNJbF.js} +1 -1
- package/web-app/dist/assets/{Avatar-T3nIpoaf.js → Avatar-BJyjV7Zi.js} +1 -1
- package/web-app/dist/assets/{Badge-BKqb59pP.js → Badge-D572l0uq.js} +1 -1
- package/web-app/dist/assets/{Button-DjVlkh8_.js → Button-T4BnOZgl.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-DXJgLGmh.js → CockpitPage-Cj_GFsNn.js} +1 -1
- package/web-app/dist/assets/{ComparePage-BRP_eVc_.js → ComparePage-A8m4ZXak.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-CLjpGLjC.js → ErrorBoundary-hugCg5yg.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-DX-vb4Xy.js → EvidenceReceiptPanel-CuJKDC2G.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-214HVHtX.js → GitHubIssuesPanel-BYGKjarC.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-BEiZKc4t.js → GitHubPRsPanel-c7sFIJlf.js} +1 -1
- package/web-app/dist/assets/{HomePage-VvMHS5ga.js → HomePage-A1MIkG2P.js} +2 -2
- package/web-app/dist/assets/{LoginPage-5WD-v7n3.js → LoginPage-8KQMRhtR.js} +1 -1
- package/web-app/dist/assets/{MagicPage-DUQT_Jye.js → MagicPage-C05QW2Lx.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-lga-z79v.js → MetricsPage-Oukv5hme.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-CGhBX3G1.js → NotFoundPage-DKIwsawz.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-CdzF2GzZ.js → ProjectPage-DwFRPLCG.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DRG64N8s.js → ProjectsPage-CgY4YYDX.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-5fzW9Frb.js → SettingsPage-BpfcSjUA.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-DrTdxO4y.js → ShowcasePage-BqBakC4K.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-C9Mqs6Rd.js → SystemSettingsPage-CZLu5VB0.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-CUGPdSWo.js → TeamsPage-D55S1jKz.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CNraDY0F.js → TemplatesPage-D3SydvxN.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-BIu_6Pea.js → TerminalOutput-hNg8G1-e.js} +1 -1
- package/web-app/dist/assets/{activity-Bk-kgqaf.js → activity-D2C0VagO.js} +1 -1
- package/web-app/dist/assets/{bell-BXmUdmo8.js → bell-CMvYi36w.js} +1 -1
- package/web-app/dist/assets/{bot-CiuzXxUR.js → bot-BvtQ6lmo.js} +1 -1
- package/web-app/dist/assets/{check-BEzjXppd.js → check-B13xkXed.js} +1 -1
- package/web-app/dist/assets/{chevron-left-AYWCXHf8.js → chevron-left-emu6r49E.js} +1 -1
- package/web-app/dist/assets/{circle-alert-BNg2uSRq.js → circle-alert-CExlwjuy.js} +1 -1
- package/web-app/dist/assets/{clock-DTj0kkGT.js → clock-D7Uas3oL.js} +1 -1
- package/web-app/dist/assets/{cloud-x4nTDBLT.js → cloud-CkB4S86C.js} +1 -1
- package/web-app/dist/assets/{code-xml-BVh_MLrk.js → code-xml-SL9IIWIn.js} +1 -1
- package/web-app/dist/assets/{database-DdfBaowG.js → database-BQFkd7t1.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-XuYFmAOY.js → dollar-sign-qHmnNYsU.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-D4cRnmeL.js → file-code-corner-C2YUDP0K.js} +1 -1
- package/web-app/dist/assets/{file-plus-CyoVlHc_.js → file-plus-C_ooIkDe.js} +1 -1
- package/web-app/dist/assets/{globe-Y6IK2RPW.js → globe-CQ-GL485.js} +1 -1
- package/web-app/dist/assets/{hammer-BKzr9VZI.js → hammer-U05L_D7V.js} +1 -1
- package/web-app/dist/assets/{index-B20cSx5G.js → index-CooHd7I-.js} +3 -3
- package/web-app/dist/assets/{layers-DebNOhGS.js → layers-Buo17rjW.js} +1 -1
- package/web-app/dist/assets/{loader-circle-DyGbrEgd.js → loader-circle-15vK3oV1.js} +1 -1
- package/web-app/dist/assets/{lock-BKPpyt20.js → lock-BeXLebuJ.js} +1 -1
- package/web-app/dist/assets/{package-egF5Wi4y.js → package-MVh_6-6R.js} +1 -1
- package/web-app/dist/assets/{plus-OiEgKIc-.js → plus-Dh_nd9vS.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CzKvrONq.js → refresh-cw-C9cBJEAT.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-Bu2mII0p.js → rotate-ccw-D3PAtzST.js} +1 -1
- package/web-app/dist/assets/{scroll-text-CSkiyax8.js → scroll-text-DZCoXQjo.js} +1 -1
- package/web-app/dist/assets/{server-DlPthJFw.js → server-OCOLI5J_.js} +1 -1
- package/web-app/dist/assets/{shield-alert-CbpHWQQX.js → shield-alert-oVhmf1_h.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DgGpZe-Q.js → thumbs-up-B-kbX4vN.js} +1 -1
- package/web-app/dist/assets/{trash-2-BV3GlCB-.js → trash-2-EL6moveW.js} +1 -1
- package/web-app/dist/assets/{trending-down-Y4TJtXhD.js → trending-down-Cl3kjpsx.js} +1 -1
- package/web-app/dist/assets/{trending-up-DsDNoHZY.js → trending-up-DST7I6M2.js} +1 -1
- package/web-app/dist/assets/{upload-gkThrc_I.js → upload-CUOT780G.js} +1 -1
- package/web-app/dist/assets/{usePolling-jpNSeNAe.js → usePolling-Djmt90eb.js} +1 -1
- package/web-app/dist/assets/{user-3Zez1WOK.js → user-BRBohA6J.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/docs/ARCHITECTURE-OVERVIEW.md +0 -209
- package/docs/AUTONOMI-ECOSYSTEM.md +0 -107
- package/docs/COCKPIT-SPEC.md +0 -72
- package/docs/COCKPIT-UX-MANDATE.md +0 -53
- package/docs/COMPETITIVE-ANALYSIS.md +0 -303
- package/docs/COMPETITIVE-NEXT-10.md +0 -316
- package/docs/COMPETITIVE-RALPH-ZEROSHOT-2026-07-08.md +0 -81
- package/docs/DASHBOARD-9.12-EVIDENCE.md +0 -97
- package/docs/DASHBOARD-ARCHITECTURE.md +0 -425
- package/docs/DASHBOARD_V2_CHECKLIST.md +0 -243
- package/docs/DEMOS.md +0 -63
- package/docs/ENTERPRISE-IDENTITY-ROADMAP.md +0 -206
- package/docs/EVIDENCE-RECEIPT-BUILD-APPLICABILITY.md +0 -79
- package/docs/FEAT-PRDREUSE-DOCKER-PLAN.md +0 -144
- package/docs/GATE-FAILURE-TRIAGE.md +0 -254
- package/docs/HANDOFF-2026-08-03.md +0 -439
- package/docs/LOOP-CANDIDATE-PROPOSAL-v1.md +0 -167
- package/docs/LOOP-HARNESS-AUDIT.md +0 -684
- package/docs/MERGE-DEDUP-MAP.md +0 -420
- package/docs/MERGE-ROUTE-MAP.md +0 -139
- package/docs/MODEL-REGISTRY-PLAN.md +0 -155
- package/docs/OUTCOME-FRONTIER.md +0 -536
- package/docs/P2-SPEC-ROBUSTNESS-PLAN.md +0 -192
- package/docs/PLANS-INDEX.md +0 -32
- package/docs/PRD-REUSE-DONE-RECOGNITION-PLAN.md +0 -536
- package/docs/PROMPT-ABLATION-RESULT.md +0 -97
- package/docs/R4-TRUST-TRAJECTORY-DESIGN.md +0 -127
- package/docs/R5-AUTO-WIKI-DESIGN.md +0 -137
- package/docs/R6-ROLLBACK-CHECKPOINT-PLAN.md +0 -107
- package/docs/R7-ZERO-CONFIG-FIRST-RUN-PLAN.md +0 -137
- package/docs/R8-SHAREABLE-TEAM-ASSETS-PLAN.md +0 -129
- package/docs/R9-OPEN-CORE-HOOKS-PLAN.md +0 -113
- package/docs/RARV-C-100X-PLAN.md +0 -286
- package/docs/RARV-C-LOOP-EFFICIENCY-PLAN.md +0 -470
- package/docs/SHAREABLE-PROOF-PLAN.md +0 -194
- package/docs/SONNET5-DEFAULT-PLAN.md +0 -157
- package/docs/SPEED-AND-FIRST-PASS-PLAN.md +0 -281
- package/docs/STALE-STATE-AUDIT.md +0 -174
- package/docs/SYNERGY-TASKS.md +0 -143
- package/docs/TASKLIST-ACCURACY-PLAN.md +0 -355
- package/docs/TOP-100-BACKLOG.md +0 -159
- package/docs/V8-ACCEPTANCE-TRIAGE-2026-07-24.md +0 -114
- package/docs/V8-COMPLEXITY-AUDIT-2026-07-24.md +0 -45
- package/docs/V8-MAJOR-RELEASE-PLAN.md +0 -137
- package/docs/V8-OVERNIGHT-PLAN-2026-07-25.md +0 -82
- package/docs/V8-SDK-RESEARCH-RAW.md +0 -129
- package/docs/WELCOME-OPENER-PLAN.md +0 -106
- package/docs/adoption-baseline-2026-07-31.md +0 -84
- package/docs/architecture/bmad-integration-epic.md +0 -271
- package/docs/architecture/bmad-integration-review.md +0 -86
- package/docs/architecture/bmad-integration-validation.md +0 -249
- package/docs/architecture/bmad-loki-voice-agent-council-analysis.md +0 -61
- package/docs/auto-claude-comparison.md +0 -250
- package/docs/bug-fixes/agent-01-cli-fixes.md +0 -101
- package/docs/bug-fixes/agent-02-purplelab-fixes.md +0 -88
- package/docs/bug-fixes/agent-03-dashboard-fixes.md +0 -119
- package/docs/bug-fixes/agent-04-memory-fixes.md +0 -105
- package/docs/bug-fixes/agent-05-provider-fixes.md +0 -86
- package/docs/bug-fixes/agent-06-integration-fixes.md +0 -101
- package/docs/bug-fixes/agent-07-dash-run-fixes.md +0 -101
- package/docs/bug-fixes/agent-08-docker-fixes.md +0 -164
- package/docs/bug-fixes/agent-09-e2e-build-fixes.md +0 -69
- package/docs/bug-fixes/agent-10-e2e-fullstack-fixes.md +0 -102
- package/docs/bug-fixes/agent-11-e2e-session-fixes.md +0 -70
- package/docs/bug-fixes/agent-12-scenario-fixes.md +0 -120
- package/docs/bug-fixes/agent-13-enterprise-fixes.md +0 -143
- package/docs/bug-fixes/agent-14-uat-newuser-fixes.md +0 -88
- package/docs/bug-fixes/agent-15-uat-poweruser-fixes.md +0 -132
- package/docs/bug-fixes/agent-19-code-review.md +0 -316
- package/docs/bug-fixes/agent-20-architecture-review.md +0 -331
- package/docs/competitive/bolt-new-analysis.md +0 -579
- package/docs/competitive/emergence-others-analysis.md +0 -605
- package/docs/competitive/replit-lovable-analysis.md +0 -622
- package/docs/dashboard-guide.md +0 -487
- package/docs/enterprise/migration.md +0 -265
- package/docs/history/one-run-audit.md +0 -304
- package/docs/plans/FORGE-AUTONOMOUS-QUEUE.md +0 -312
- package/docs/plans/ULTRAPLAN-FORGE-BAAS.md +0 -612
- package/docs/plans/UT2-6-LSP-DIAGNOSTIC-BROADCAST.md +0 -394
- package/docs/prd-purple-lab-ide.md +0 -152
- package/docs/prd-purple-lab-platform-v2.md +0 -223
- package/docs/prd-purple-lab-platform.md +0 -319
- package/docs/retrospectives/v7.5.15-fleet-postmortem.md +0 -325
- package/docs/retrospectives/v7.5.15-honesty-audit.md +0 -136
- package/docs/retrospectives/v7.5.15-llm-failure-modes.md +0 -49
- package/docs/show-hn-post.md +0 -46
- package/docs/test-scenarios/enterprise-scenarios.md +0 -732
- package/docs/thick2thin.md +0 -173
|
@@ -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.
|