loki-mode 11.74.2 → 11.75.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +2 -3
  3. package/VERSION +1 -1
  4. package/autonomy/completion-council.sh +10 -10
  5. package/autonomy/council-v2.sh +4 -4
  6. package/autonomy/grill.sh +1 -1
  7. package/autonomy/lib/browser-open.sh +1 -1
  8. package/autonomy/lib/chat.sh +1 -1
  9. package/autonomy/lib/cockpit-render.sh +1 -1
  10. package/autonomy/lib/done-recognition.sh +4 -4
  11. package/autonomy/lib/efficiency_cost.py +1 -1
  12. package/autonomy/lib/engine10-push.sh +2 -2
  13. package/autonomy/lib/prd-enrich.sh +1 -1
  14. package/autonomy/lib/proof-check.sh +1 -1
  15. package/autonomy/lib/proof-generator.py +3 -3
  16. package/autonomy/lib/proof-pr.sh +1 -1
  17. package/autonomy/lib/skill-link-heal.sh +1 -1
  18. package/autonomy/lib/voter-agents.sh +1 -1
  19. package/autonomy/loki +26 -37
  20. package/autonomy/prd-checklist.sh +1 -1
  21. package/autonomy/receipt_jwt.py +1 -1
  22. package/autonomy/run.sh +28 -28
  23. package/autonomy/trigger-schedule.py +6 -414
  24. package/autonomy/tui.sh +1 -1
  25. package/autonomy/verify.sh +4 -4
  26. package/completions/loki.bash +0 -5
  27. package/dashboard/__init__.py +1 -1
  28. package/docs/ARCHITECTURE.md +1 -2
  29. package/docs/CLI-REFERENCE.md +1 -1
  30. package/docs/COMPONENTS.md +1 -1
  31. package/docs/INSTALLATION.md +1 -1
  32. package/docs/ROUTING-EVIDENCE.md +1 -2
  33. package/docs/V10-RESEARCH.md +1 -1
  34. package/docs/dev/architecture-reference.md +3 -7
  35. package/docs/dev/project-structure.md +1 -4
  36. package/docs/dev/release-checklist.md +13 -31
  37. package/docs/research-2026-07/OBSERVABILITY-TOOL-SPEC.md +1 -1
  38. package/docs/v11/MARKET-SLICES.md +1 -1
  39. package/docs/v11/REGISTRIES.md +1 -1
  40. package/docs/v11/ROUTER-1.md +2 -2
  41. package/loki-ts/dist/loki.js +2 -2
  42. package/mcp/__init__.py +1 -1
  43. package/package.json +2 -2
  44. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  45. package/skills/extending.md +2 -3
  46. package/skills/factory-operations.md +11 -18
  47. package/skills/release-cadence.md +2 -2
  48. package/web-app/dist/assets/{AdminPage-jkjilfbl.js → AdminPage-xKgtIhk2.js} +1 -1
  49. package/web-app/dist/assets/{Avatar-bd5JWkSJ.js → Avatar-Q14AHox9.js} +1 -1
  50. package/web-app/dist/assets/{Badge-DVRit2Xw.js → Badge-yk5M-lip.js} +1 -1
  51. package/web-app/dist/assets/{Button-BNAB-pVZ.js → Button-ynznk0At.js} +1 -1
  52. package/web-app/dist/assets/{CockpitPage-DpG29h_w.js → CockpitPage-C8B765za.js} +1 -1
  53. package/web-app/dist/assets/{ComparePage-Dasg5V41.js → ComparePage-DqzmWKnf.js} +1 -1
  54. package/web-app/dist/assets/{ErrorBoundary-DJojse6b.js → ErrorBoundary-DdBPd_Lq.js} +1 -1
  55. package/web-app/dist/assets/{EvidenceReceiptPanel-Blxw7_u1.js → EvidenceReceiptPanel-jb4Tp38b.js} +1 -1
  56. package/web-app/dist/assets/{GitHubIssuesPanel-BBvSFj80.js → GitHubIssuesPanel-xJQiLcxs.js} +1 -1
  57. package/web-app/dist/assets/{GitHubPRsPanel-BCIKeSBg.js → GitHubPRsPanel-84W3Dhk_.js} +1 -1
  58. package/web-app/dist/assets/{HomePage-C7d__oNG.js → HomePage-St1Xl2tu.js} +2 -2
  59. package/web-app/dist/assets/{LoginPage-BXljL3yY.js → LoginPage-jRt0NUHb.js} +1 -1
  60. package/web-app/dist/assets/{MagicPage-Xjkts_yE.js → MagicPage-CQLFe7LR.js} +1 -1
  61. package/web-app/dist/assets/{MetricsPage-kQvVp-j2.js → MetricsPage-zzX9eTC5.js} +1 -1
  62. package/web-app/dist/assets/{NotFoundPage-D-4ZlEaO.js → NotFoundPage-DDu023SY.js} +1 -1
  63. package/web-app/dist/assets/{ProjectPage-DfzR5RXP.js → ProjectPage-D60UURaH.js} +3 -3
  64. package/web-app/dist/assets/{ProjectsPage-DFDsWzkV.js → ProjectsPage-DqVsKxlt.js} +1 -1
  65. package/web-app/dist/assets/{SettingsPage-CLPIoYT3.js → SettingsPage-G7pPKvo0.js} +1 -1
  66. package/web-app/dist/assets/{ShowcasePage-PZKkyGeY.js → ShowcasePage-CrU_aKgS.js} +1 -1
  67. package/web-app/dist/assets/{SystemSettingsPage-BsBlcnzo.js → SystemSettingsPage-ClKGQFsB.js} +1 -1
  68. package/web-app/dist/assets/{TeamsPage-BSjgNhoU.js → TeamsPage-B530vSNe.js} +1 -1
  69. package/web-app/dist/assets/{TemplatesPage-W2KeAzbg.js → TemplatesPage-COmv_Cqd.js} +1 -1
  70. package/web-app/dist/assets/{TerminalOutput-BjroTiCC.js → TerminalOutput-CufG9YzF.js} +1 -1
  71. package/web-app/dist/assets/{activity-BXZ5YYes.js → activity-Ct1Updr6.js} +1 -1
  72. package/web-app/dist/assets/{bell-D1urp5pa.js → bell-rcvhQuGr.js} +1 -1
  73. package/web-app/dist/assets/{bot-Chedf2bF.js → bot-DzeqEI-b.js} +1 -1
  74. package/web-app/dist/assets/{check-DhaCuOCV.js → check-zacnZHyC.js} +1 -1
  75. package/web-app/dist/assets/{chevron-left-D7BpQyA6.js → chevron-left-Lxnkl8Qg.js} +1 -1
  76. package/web-app/dist/assets/{circle-alert-DnGvQ_AQ.js → circle-alert-DoqSdQdB.js} +1 -1
  77. package/web-app/dist/assets/{clock-CqfxWhHM.js → clock-B7bJcVIC.js} +1 -1
  78. package/web-app/dist/assets/{cloud-BKxp5AWB.js → cloud-DdA9Bu6q.js} +1 -1
  79. package/web-app/dist/assets/{code-xml-BDQ6GBtt.js → code-xml-CAZEZ9T8.js} +1 -1
  80. package/web-app/dist/assets/{database-BmpEx1y_.js → database-Da2GkEIN.js} +1 -1
  81. package/web-app/dist/assets/{dollar-sign-DYNY9R_C.js → dollar-sign-DjDiG-37.js} +1 -1
  82. package/web-app/dist/assets/{file-code-corner-BJi0IBFd.js → file-code-corner-CSL-Jij-.js} +1 -1
  83. package/web-app/dist/assets/{file-plus-Dqyb2bcQ.js → file-plus-02DRA8pV.js} +1 -1
  84. package/web-app/dist/assets/{globe-DPh4RWfM.js → globe-CGyB90kK.js} +1 -1
  85. package/web-app/dist/assets/{hammer-B3xnuoA1.js → hammer-BaQB96TG.js} +1 -1
  86. package/web-app/dist/assets/{index-BpX8DcTr.js → index-lcoI1_Kk.js} +3 -3
  87. package/web-app/dist/assets/{layers-BRCmyCW8.js → layers-BWJBvoeg.js} +1 -1
  88. package/web-app/dist/assets/{loader-circle-n07Pky0M.js → loader-circle-CdcI0FeS.js} +1 -1
  89. package/web-app/dist/assets/{lock-BWqOuQmS.js → lock-BBkw5NJx.js} +1 -1
  90. package/web-app/dist/assets/{package-BJlB_Z-Z.js → package-BpPRO3LM.js} +1 -1
  91. package/web-app/dist/assets/{plus-oH17BZu2.js → plus-D7XbyK29.js} +1 -1
  92. package/web-app/dist/assets/{refresh-cw-CPQ476LL.js → refresh-cw-DLG3hyAi.js} +1 -1
  93. package/web-app/dist/assets/{rotate-ccw-7hLvOcnS.js → rotate-ccw-Bqau4R_S.js} +1 -1
  94. package/web-app/dist/assets/{scroll-text-BCmLauQn.js → scroll-text-BtIL3cNk.js} +1 -1
  95. package/web-app/dist/assets/{server-DfTfvW84.js → server-tZCmZ4yH.js} +1 -1
  96. package/web-app/dist/assets/{shield-alert-CaeGRq19.js → shield-alert-K5JXzFKg.js} +1 -1
  97. package/web-app/dist/assets/{thumbs-up-Dz-l2GPC.js → thumbs-up-CHCVyJdw.js} +1 -1
  98. package/web-app/dist/assets/{trash-2-B-OsDmSo.js → trash-2-DncNxVq0.js} +1 -1
  99. package/web-app/dist/assets/{trending-down-CXTFOxzq.js → trending-down-D8Bp0azX.js} +1 -1
  100. package/web-app/dist/assets/{trending-up-FQEdqr3v.js → trending-up-AixETdKR.js} +1 -1
  101. package/web-app/dist/assets/{upload-CacWSNTg.js → upload-VCbjFn2H.js} +1 -1
  102. package/web-app/dist/assets/{usePolling-cIjZ9qd2.js → usePolling-CQvKD2Jj.js} +1 -1
  103. package/web-app/dist/assets/{user-DAouWFHi.js → user-SK-YguQ6.js} +1 -1
  104. package/web-app/dist/index.html +1 -1
  105. package/autonomy/company-seats.json +0 -82
  106. package/docs/COMPETITIVE-INTEL-2026-09.md +0 -444
  107. package/docs/FOUNDER-DECISIONS-TRUST-SEMANTICS-2026-08-01.md +0 -145
  108. package/docs/FOUNDER-STATUS-AND-DECISIONS-2026-07-01.md +0 -132
  109. package/docs/LOKI-10-BUILD-PROMPT.md +0 -350
  110. package/docs/STRATEGY-2026-2028.md +0 -124
  111. package/docs/V10-VISION.md +0 -161
  112. package/docs/v10/ADOPTION-PLAN.md +0 -375
  113. package/docs/v10/ARCHITECT-CUTS.md +0 -744
  114. package/docs/v10/BACKLOG.md +0 -231
  115. package/docs/v10/BOARD.md +0 -1403
  116. package/docs/v10/CONTROL-PLANE.md +0 -120
  117. package/docs/v10/CONTROL.md +0 -36
  118. package/docs/v10/CP-ASK-PLAN.md +0 -85
  119. package/docs/v10/CP-ENTERPRISE-UI.md +0 -537
  120. package/docs/v10/D51-PHASE-B.md +0 -120
  121. package/docs/v10/D61-SPEED.md +0 -105
  122. package/docs/v10/D63-CARDS.md +0 -354
  123. package/docs/v10/DECISIONS.md +0 -915
  124. package/docs/v10/E2E-D65.md +0 -123
  125. package/docs/v10/ENGINE-LAWS.md +0 -163
  126. package/docs/v10/ENGINE-MEASURE.md +0 -69
  127. package/docs/v10/ENGINE.md +0 -743
  128. package/docs/v10/FAILURE-CLASSES.md +0 -2026
  129. package/docs/v10/FC21B-FC22-PLAN.md +0 -107
  130. package/docs/v10/FIRST-RUN-GATE.md +0 -39
  131. package/docs/v10/FOUNDER-QUEUE.md +0 -29
  132. package/docs/v10/GAP-RESEARCH-1003.md +0 -32
  133. package/docs/v10/GUARDS.md +0 -883
  134. package/docs/v10/L0-WAVE1.md +0 -81
  135. package/docs/v10/LEGACY-REMOVAL.md +0 -408
  136. package/docs/v10/MEDIUM-ANALYSIS.md +0 -346
  137. package/docs/v10/METRICS.md +0 -645
  138. package/docs/v10/MODERNIZE.md +0 -437
  139. package/docs/v10/OPENCODE-PARITY-PLAN.md +0 -84
  140. package/docs/v10/OPERATING-MODEL.md +0 -60
  141. package/docs/v10/PLAN-V12.md +0 -896
  142. package/docs/v10/PROGRESS.md +0 -2735
  143. package/docs/v10/RELEASE-11.md +0 -77
  144. package/docs/v10/RETRO-2026.md +0 -650
  145. package/docs/v10/ROADMAP-D63.md +0 -35
  146. package/docs/v10/SCALE.md +0 -140
  147. package/docs/v10/SCORECARD-PLAN.md +0 -250
  148. package/docs/v10/SWARM-PROMPT.md +0 -98
  149. package/docs/v10/SWARM.md +0 -293
  150. package/docs/v10/cp-redesign/PARITY.md +0 -188
  151. package/docs/v10/cp-redesign/legacy/INDEX.md +0 -1167
  152. package/docs/v10/roadmap-10/01-measurement.md +0 -113
  153. package/docs/v10/roadmap-10/02-cost.md +0 -75
  154. package/docs/v10/roadmap-10/03-speed.md +0 -65
  155. package/docs/v10/roadmap-10/04-first-run.md +0 -61
  156. package/docs/v10/roadmap-10/05-deploy.md +0 -61
  157. package/docs/v10/roadmap-10/06-stability.md +0 -64
  158. package/docs/v10/roadmap-10/07-quality.md +0 -57
  159. package/docs/v10/roadmap-10/08-proof.md +0 -48
  160. package/docs/v10/roadmap-10/09-safety.md +0 -74
  161. package/docs/v10/roadmap-10/README.md +0 -1464
  162. package/skills/sdlc-fleet.md +0 -170
  163. package/tools/cockpit/SPEC.md +0 -275
  164. package/tools/cockpit/index.html +0 -146
  165. package/tools/cockpit/roi-basis.json +0 -33
  166. package/tools/cockpit/server.py +0 -946
@@ -1,120 +0,0 @@
1
- # D51 Phase B: workspaces, cross-repo runs, combined integration evidence
2
-
3
- Architect design, 2026-10-01, base ee67eaf16. Flag: `LOKI_WORKSPACES=0` disables (on by default since D63 C8).
4
-
5
- Status (D63 C8): `autonomy/lib/workspace.py` runs repos with no pending `after` in parallel (bounded by `concurrency`, default 2), has `list`, `show`, `run` and `status`, exits 3 when the only non-ok outcomes are budget stops, and handles SIGINT like SIGTERM. Still design only: B03 to B07, B09 to B15 (worktree_prep hardening, group.json store, dashboard, `clean`). The bash gate in `autonomy/loki` flips in C3.
6
-
7
- ## Goal (user-visible)
8
- 1. Name a group of repos once in loki.yaml (`workspaces.shop: [acme/api, acme/web]`) and run one issue across all of them: `loki workspace run shop acme/api#12`.
9
- 2. Each repo gets its own isolated worktree with working dependencies, its own engine run, its own Seal and its own PR; the user's checkouts are never touched.
10
- 3. One integration command runs against all the run branches together; its evidence (head SHAs, exit code, log hash) is attached to every PR and shown in the dashboard.
11
-
12
- ## What exists today (evidence)
13
- - Two duplicate worktree paths. Headless: `autonomy/lib/backlog.py:232-241` (`worktree add -B loki/backlog-N <top>-backlog/issue-N HEAD`). UI: `dashboard/api_start.py:159-184` (clone to ~/.loki/repos/<slug>, fetch, `worktree add` off origin/HEAD under ~/.loki/worktrees). Neither copies or installs dependencies, and neither locks the shared base repo against concurrent fetch or worktree add.
14
- - Missing dependencies are a live problem. `loki-ts/src/engine10/stages/verify.ts:39` resolves `.venv/bin/<bin>` inside repoDir, and `testmap.ts:45` does the same for the interpreter. A fresh worktree has no node_modules or .venv, and the D50-F1b opus review hit exactly this: its VERIFIED fixture stalled (BOARD.md row D50-F1b).
15
- - Relaunch destroys evidence. `backlog.py:234-235` force-removes an existing issue worktree before relaunching, which is the open D51-A3b follow-up.
16
- - The engine is single-repo and runs in its cwd. Intake refuses a dirty tracked tree (`intake.ts:65-67`); text tasks enter through `LOKI_E10_TASK_TEXT` (`intake.ts:74`), but only when no issue.json exists (`intake.ts:80`). The backstop stages with `git add -A` (`supervisor.ts:95`), so anything in the worktree that is not ignored gets committed.
17
- - Run state in the UI is in memory only (`api_start.py:13`). `knowledge_sources` is parsed but nothing reads it (schema description). `loki.yaml` sets `additionalProperties: false` (`schemas/loki-yaml.schema.json:7`).
18
- - D33 amendment (1): e10ext never runs tests or computes pass or fail. Integration checks therefore cannot live in loki-ts; they belong in autonomy/lib (Python), outside the Seal.
19
- - Precedent for partial failure: `tests/test-multi-repo-orchestrates.sh` uses continue-and-report with a non-zero aggregate exit.
20
-
21
- ## Design
22
- Principle: engine10 core is untouched. A workspace run calls the existing launcher once per repo, in a prepared worktree, exactly the way backlog.py does today. Phase B therefore adds no TypeScript, has no dist overlap and puts nothing new into the 5,000-line cap.
23
-
24
- Config (loki.yaml, the only place workspaces are defined; everything else keeps working without a config file):
25
- ```
26
- workspaces:
27
- shop:
28
- repos:
29
- - {repo: acme/api, path: ~/src/api, setup: "npm ci"} # path optional, else clone; setup optional
30
- - {repo: acme/web, after: [acme/api]} # after: runs once api finishes
31
- integration: {command: "docker compose up --abort-on-container-exit", timeout_s: 900}
32
- integration_files: [.env] # opt-in, integration step only, never the engine worktree
33
- ```
34
- The schema accepts the `workspaces` key unconditionally, so a config written for a newer version does not fail validation on an older one; the flag gates only runtime behavior.
35
-
36
- Worktree substrate (`autonomy/lib/worktree_prep.py`, one helper used by backlog, the UI and workspaces):
37
- - Start point: fetch, then `origin/HEAD`, falling back to HEAD. Uncommitted user edits are never carried, never stashed and never snapshotted. The base SHA is recorded, and one line reports how many uncommitted changes in the source checkout were left out.
38
- - Concurrency: an fcntl lock per base repo (`~/.loki/repos/<slug>.lock`) held around fetch and worktree add.
39
- - Dependencies, in priority order:
40
- 1. A declared `setup` command, run in the worktree.
41
- 2. A copy-on-write copy (`cp -c` on APFS, `cp --reflink=auto` on Linux) of ignored top-level dependency directories: node_modules, .venv, venv, vendor.
42
- 3. None, recorded as `deps: none`.
43
- - Before a copied directory is accepted, it must pass two checks, or the copy is refused and the run ends FAILED with "deps: copy unsafe, declare setup":
44
- - `git check-ignore` at the start ref, so `add -A` can never commit it;
45
- - no escape into the source checkout: no absolute symlink, no editable `.pth` or `__editable__` finder, and no shebang pointing at the source path.
46
- - Never symlink dependencies.
47
- - Secrets: .env and similar files are never placed in an engine worktree, because issue text is untrusted and moat 9 forbids secrets in that step.
48
- - A relaunch never deletes a kept worktree. It creates a new stamped one, and old ones are removed only by `clean`.
49
-
50
- Group state lives on disk with one writer: `~/.loki/workspaces/<ws>/<group-id>/group.json`, written atomically by the runner process only. It holds:
51
- - the workspace and the issue ref;
52
- - per repo: base SHA, branch, worktree, run_id, outcome, pr_url, deps mode, and started and ended timestamps;
53
- - integration: status, evidence path.
54
-
55
- The CLI and the dashboard both read this file, so state survives a dashboard restart. The timestamps feed the D51 item 3 PRs-per-hour metric.
56
-
57
- Cross-repo run (`autonomy/lib/workspace.py`):
58
- - The issue is fetched once.
59
- - Per-repo task text goes to each run through `LOKI_E10_TASK_TEXT` (no issue.json is written into those runs). By default it is the issue's `## repo: owner/name` section when present, otherwise the full issue. The text also carries a fixed preamble naming the sibling worktree paths as read-only context.
60
- - Repos with no `after` run in parallel up to `concurrency`. A repo with `after` starts when its predecessors finish, with their worktrees at their final state.
61
- - A repo that needs nothing ends ALREADY_SATISFIED with no PR, which is existing engine behavior.
62
-
63
- Integration (`autonomy/lib/workspace_integration.py`):
64
- - Runs after every repo run has finished, in `<group>/integration/`: worktrees of each repo at its run branch head, or at base if there was no change, plus deps through the same substrate, plus the opt-in integration_files.
65
- - The command runs with a timeout and a killpg on timeout.
66
- - It writes evidence.json: command, exit code, duration, the sha256 of the log, per-repo head SHAs, and the literal statement `"seal": false, "note": "integration evidence is not part of the Seal"`.
67
- - One PR comment per PR links the group and states the result. If any head SHA no longer matches the PR head, the dashboard shows the evidence as stale.
68
-
69
- Surface:
70
- - CLI: `loki workspace list | show <ws> | run <ws> <ref> [--repos a,b] [--dry-run] | status [<group>] | clean <group>`.
71
- - Dashboard: endpoints `GET /api/workspaces`, `POST /api/workspaces/{ws}/run`, `GET /api/workspaces/groups/{id}`, and a group card in start.html (per-repo row with outcome and PR link, plus an integration row).
72
- - Exit codes: 0 when all repos are ok and integration passed; 3 for budget stops only; 1 otherwise. This is continue-and-report.
73
-
74
- Failure modes:
75
- - Clone or fetch fails: that repo is FAILED, and the others continue.
76
- - Lock contention: wait with a bound of 120s, then FAILED.
77
- - Setup fails: FAILED with the last stderr line.
78
- - Unsafe copy: FAILED, with the reason given.
79
- - A predecessor fails: dependents become SKIPPED (predecessor failed) and are not run.
80
- - SIGTERM: killpg every child, mark the group interrupted, and keep the worktrees.
81
- - Integration timeout: FAILED (timeout), never passed.
82
- - Integration not configured: status `not_configured`, which never reads as passed.
83
- - Dashboard restarts mid-group: the runner is a detached process; the UI re-reads group.json.
84
-
85
- ## Slices (all behind LOKI_WORKSPACES; test parts register through one runner entry owned by B01)
86
- | ID | Goal | File set | Wall check | Tier |
87
- |---|---|---|---|---|
88
- | B01 | Test harness: register `tests/test-workspace.sh`, which sources `tests/workspace/*.sh` parts (each later slice adds only its own part) | tests/test-workspace.sh, tests/run-all-tests.sh, scripts/local-ci.sh, tests/workspace/00-smoke.sh | runner lists the suite; an empty part dir passes 1/0; shellcheck clean | LOW |
89
- | B02 | Schema plus validator for `workspaces` (repos, path, setup, after, integration, integration_files); `after` cycle and unknown-repo rejection | schemas/loki-yaml.schema.json, autonomy/lib/loki_yaml.py, tests/workspace/10-schema.sh | a valid sample passes; a cycle, an unknown `after` and a non-ignored integration file are each rejected with a named error | MEDIUM |
90
- | B03 | worktree_prep core: start ref, per-base lock, left-out-edits line, stamped dirs, never delete a kept tree | autonomy/lib/worktree_prep.py, tests/workspace/20-prep.sh | a dirty source fixture yields a worktree tree equal to base; 4 parallel preps on one base all succeed; a relaunch keeps the old dir | HIGH |
91
- | B04 | Dependency strategy: setup, CoW copy, ignore check, escape detection | autonomy/lib/worktree_deps.py, tests/workspace/30-deps.sh | editable-install venv fixture: copy refused, setup path imports worktree code; absolute-symlink node_modules refused; a non-ignored dir is never copied | HIGH |
92
- | B05 | Adopt worktree_prep in backlog.py behind the flag (also closes A3b relaunch) | autonomy/lib/backlog.py, tests/test-backlog.sh | flag off: backlog 47/0 unchanged; flag on: a JS fixture issue reaches VERIFIED with node_modules present | MEDIUM |
93
- | B06 | Adopt worktree_prep in api_start._prepare_workdir behind the flag | dashboard/api_start.py, tests/dashboard/test_api_start_prep.py | flag on: the UI path uses the same lock and deps; flag off: byte-identical behavior | MEDIUM |
94
- | B07 | group.json store: atomic write, single writer, reader API | autonomy/lib/workspace_state.py, tests/workspace/40-state.sh | a killed writer never leaves a partial file; a second writer pid is refused | MEDIUM |
95
- | B08 | Runner: fan-out per repo, `after` ordering, SKIPPED on predecessor failure, killpg on SIGTERM, exit codes | autonomy/lib/workspace.py, tests/workspace/50-run.sh | stub launcher: parallel repos overlap, the dependent starts after the predecessor, a failing predecessor marks the dependent SKIPPED, SIGTERM leaves no child alive | HIGH |
96
- | B09 | Per-repo task text: section split, preamble, `LOKI_E10_TASK_TEXT` only (no issue.json in the run) | autonomy/lib/workspace_task.py, tests/workspace/60-task.sh | a 2-section issue gives each repo only its section; no issue.json exists in either run dir; untrusted text is never placed in argv | HIGH |
97
- | B10 | Integration runner plus evidence.json (not a Seal) | autonomy/lib/workspace_integration.py, tests/workspace/70-integration.sh | a passing compose stub gives exit 0 and matching SHAs; a timeout is FAILED, never passed; integration_files absent from every engine worktree | HIGH |
98
- | B11 | PR comment with the integration result, via the existing gh token path (no LLM in the step) | autonomy/lib/workspace_comment.py, tests/workspace/80-comment.sh | stub gh receives one comment per PR; the token appears in no log or argv | MEDIUM |
99
- | B12 | CLI `loki workspace` subcommand (list, show, run, status, clean) | autonomy/loki, tests/workspace/90-cli.sh | flag off: the command says it is disabled; `clean` never removes a worktree whose branch has an open PR | MEDIUM |
100
- | B13 | Dashboard endpoints reading group.json; run launches a detached runner | dashboard/api_workspaces.py, dashboard/server.py, tests/dashboard/test_api_workspaces.py | the group survives an app restart; non-loopback Host gets 403; control scope required for run | HIGH |
101
- | B14 | Dashboard group card (per-repo rows, integration row, stale-evidence badge) | legacy-ui-static/start.html | Playwright: a fixture group renders 2 repo rows plus integration; a mismatched head SHA shows stale | LOW |
102
- | B15 | 10x metric: PRs per wall-clock hour and per attention minute from group.json timestamps | autonomy/lib/workspace_metrics.py, tests/workspace/95-metrics.sh | a fixture group gives the hand-computed rate; unmeasured runs are reported, not counted as zero | LOW |
103
- | B16 | Docs plus flag flip after the whole-feature Wall: a 2-repo fixture (lib and app) ends with 2 PRs and integration passed | docs/WORKSPACES.md, tests/workspace/99-e2e.sh | the e2e part passes on CI with the stub provider; one real run is recorded in METRICS.md | MEDIUM |
104
-
105
- Dependencies:
106
- - B01 comes before every slice that adds a test part.
107
- - Parallel chains:
108
- - B02 and B03, then B04, then B05 and B06.
109
- - B07, then B08 (which also needs B03 and B04), then B09 and B10, then B11.
110
- - B12 needs B08. B13 needs B07 and B08, then B14. B15 needs B07.
111
- - B16 needs everything.
112
- - File sets do not overlap. `autonomy/loki` belongs to B12 only, `api_start.py` to B06 only, `start.html` to B14 only, and the runner files to B01 only.
113
-
114
- ## Open questions (CTO or founder)
115
- 1. CTO: should integration evidence ever join the Seal or be signed with the Seal key? The default here is no: it is labelled as not part of the Seal.
116
- 2. Founder: may opt-in integration_files such as .env be carried even into the model-free integration step, or is the declared `setup` the only path? The engine worktree is a hard no either way (moat 9).
117
- 3. CTO: where does per-repo task text come from? Options: the issue's `## repo:` sections (the default here), user edits in the UI, or a planner model call. A full multi-repo requirement in every repo makes the Wall write checks that no single repo can pass.
118
- 4. CTO: can a provider CLI session read sibling worktrees outside its cwd? If sandboxing blocks it, `after` ordering only orders work and gives no context, and `knowledge_sources` (no consumer today) should become that channel.
119
- 5. CTO: what size ceiling applies to a non-CoW full copy on Linux without reflink? The proposal is to refuse above 2 GB and require `setup`.
120
- 6. Founder: should a failed integration convert the group's PRs to draft? Doing so needs a credentialed write outside engine10-push.sh, which bears on the Rule of Two.
@@ -1,105 +0,0 @@
1
- # D61: speed for users (design)
2
-
3
- Architect design, 2026-10-02, base fbbd31299, v10.6.6. Design only; nothing below is built unless marked "exists". Build flag `LOKI_SPEED=1` while slices land; it flips to default-on (and the flag is deleted) when slice 17's targets pass. Users never set a flag or write config.
4
-
5
- ## 1. What the user sees (from the terminal outward)
6
- - First run in a repo: `loki "fix the off-by-one in pager.py"`. Loki prints one start line, then streams stage lines, then the PR:
7
- `loki: small task, lean path, sonnet, 3 impacted tests, cap $0.50, warm in 0.4s (first run here: maps built in 2.1s)`
8
- Second and later runs say `warm in 0.1s`. No daemon setup, no prompt, no config: the warm engine starts itself in the background after the first run and is used if alive, ignored if not.
9
- - Big work: `loki ./spec.md` or `loki "add CSV export to api, web and cli"`. Loki prints its decision before spending a token on code:
10
- `loki: 5 units, 3 in parallel (decomposable: disjoint write sets, 1 shared file serialized), est 6-9 min`
11
- `loki: live at http://127.0.0.1:57375/g/<group>` (or the Control Plane URL when LOKI_CONTROL_URL is set)
12
- then a compact unit table repainted every 2s on a TTY (one line per state change otherwise): unit, files, stage, elapsed, tokens.
13
- - When the work does not split: `loki: sequential (reason: units share 4 of 6 files)`. It never forces parallel.
14
- - The end: one PR with a unit table (goal, files, tests, tokens, model per unit), one combined receipt, and a final line `done in 7m12s, 412k tokens (5 units, 1 fix unit), PR <url>`.
15
- - `loki backlog` keeps per-issue parallelism (exists) and shares one slot pool with any issue that decomposes.
16
-
17
- ## 2. What exists (evidence) and the gap
18
- | Piece | Where | Status |
19
- |---|---|---|
20
- | Lean path (skip Plan and Wall for small tasks with relevant tests) | engine10/sizing.ts:44-46, stages/plan.ts:74-77, stages/wall.ts:145 | exists; gap: only fires when the task names a file (sizing.ts:10 namedFiles) |
21
- | Byte-stable prefix | e10ext/lean_prefix.ts (LOKI_E10_PREFIX=lean) | exists, opt-in; no guard that every stage prompt starts with identical bytes |
22
- | Repo map and test map cached by HEAD^{tree} | engine10/cache.ts, repomap.ts, testmap.ts | exists on disk; rebuilt per process, no in-memory warm copy |
23
- | Impacted tests only | testmap.ts:201 impactedRefs, stages/verify.ts:251 | exists |
24
- | Repo context pack (20 files plus impacted tests) | e10ext/context.ts | exists |
25
- | Scope control (out-of-scope edits reverted, listed NOT PROVEN) | e10ext/scope.ts, e10ext/commit_filter.ts | exists; reusable as a unit's write-set fence |
26
- | Sonnet-then-escalate cascade | sizing.ts:54 cascadeEnabled | exists |
27
- | Cheap-model already-done check on the critical path | already_done.ts:167-182 (wallModel) | exists; cost on the small path not yet measured |
28
- | Per-session child process | session.ts:1-2 (self-respawn through cli.ts) | exists; cold bun start per session |
29
- | Parallel worktree runner | autonomy/lib/backlog.py:180-257 (worktree add -B loki/backlog-N, concurrency, daily cap) | exists; issue refs only, no dependency prep, in-memory queue |
30
- | Worktree dependency prep (`worktree_prep`) | none (grep finds nothing); D51-PHASE-B.md notes fresh worktrees lack node_modules/.venv | MISSING |
31
- | Decomposer, integrator, group run | none | MISSING |
32
- | Local dashboard (SSE, folds .loki/runs) | engine10/dashboard/server.ts (port 57375) | exists; one repo, no group view |
33
- | Control Plane v0 and live shipping | loki-ts/src/commands/control.ts, packages/control-plane/, e10ext/ship_hook.ts | exists, preview (LOKI_CONTROL=1); no group_id column |
34
- | Eval tiers and token capture | eval/loki10/harness.py:53 TIERS includes large, :1157 _v10_tokens; stage-profile.py | exists; 0 lg- tasks, no parallel or sequential arm split |
35
-
36
- ## 3. Design
37
- **A. Small path (seconds).** (1) Warm engine: the engine10 dashboard process (already a long-lived bun server on 127.0.0.1) also serves a Unix socket `~/.loki/run/engine.sock`; it holds repo map and test map in memory keyed by (repoKey, HEAD^{tree}, dirty-file hash) and runs intake and sizing in-process. The CLI tries the socket for 50ms, else runs cold exactly as today and spawns the daemon detached for next time (fail-safe: no daemon never changes a result). (2) Pre-model overhead budget 2s, measured per stage from argv to the first provider byte and printed as `warm in Xs`. (3) Lean prefix becomes the default with a byte-identity guard. (4) Wider lean eligibility: a task that names no file uses context.ts keyword selection over the cached map, still requiring impacted tests (fail-safe keeps Wall). (5) The already-done model check moves off the critical path: it runs only when deterministic hits exist, concurrently with implement, and cancels implement on a confirmed hit.
38
-
39
- **B. Big work (minutes, in parallel).** Decomposer (engine10/decompose.ts, deterministic first): split the task or spec into requirement items (numbered or bulleted lines, headings, issue checklists); map each to a write set via context.ts selection plus module boundaries (top-level dirs, package workspaces, test map ownership); union units whose write sets overlap; mark shared files (lockfiles, package manifests, barrels, CHANGELOG) as serialized. A cheap model (wallModel) may only confirm or merge proposed units against a strict JSON schema; invalid output means the deterministic DAG stands. Decomposability check: parallel only if at least 2 units, write-set overlap ratio below 0.2, no unit reads a symbol another unit creates, and each unit sizes small or normal. Otherwise one sequential run, reason printed. Runner: backlog.py's launch loop generalised to units (same worktree layout, branches loki/unit-<group>-<n>), each unit a full v10 run with `--no-pr`, its write set enforced by scope.ts, its own context pack, cheapest capable model via the cascade (escalation only on a failed check, research section 3). Integrator (engine10/integrate.ts): merge unit branches in DAG order onto loki/group-<id>; conflicts become targeted fix units (conflict hunks only); run the full suite once; failures map back to units through the test map and become fix units (at most 2 rounds); Seal once on the combined diff; one PR. Stacked PRs are a later option, not v1.
40
-
41
- **C. Tokens at or below one sequential session.** No transcript sharing between units; each unit gets only its context pack and the shared lean prefix (cache hits across units); per-unit token budget = sequential estimate / units x 1.1, enforced by the existing budget path; deterministic search and test selection, no model-driven exploration in the decomposer. Every run prints tokens; the eval compares against sequential and raw.
42
-
43
- **D. Live view.** run.started gains `group_id`, `unit_id`, `deps`; a group event file records the DAG and integrator stages. The dashboard adds /g/<group> (unit grid); the Control Plane runs table gains group_id so the same grid works there.
44
-
45
- ## 4. Slices (numbered; each ships with its METRICS row)
46
- | # | Goal | File set | Wall check | Budget | Tier |
47
- |---|---|---|---|---|---|
48
- | 1 | Pre-model stage timer: argv to first provider byte, per stage, in run.completed data and the start line | engine10/supervisor.ts, engine10/output.ts, tests engine10/premodel_timing.test.ts | test asserts the timing fields exist and sum within 50ms of the span; stage-profile.py prints a pre_model column | 30m | MEDIUM |
49
- | 2 | Lean prefix default plus byte-identity guard across implement, fix, wall, plan prompts | e10ext/lean_prefix.ts, engine10/stages/implement.ts, stages/fix.ts, tests e10ext/prefix_identity.test.ts | guard fails when any stage prompt's first 200 bytes differ; second run shows cache_read tokens over 0 in the cost event | 30m | MEDIUM |
50
- | 3 | Lean eligibility without a named file (context.ts selection), fail-safe to Wall | engine10/sizing.ts, tests engine10/sizing_lean.test.ts | fixtures: unnamed-file small task goes lean only when impacted tests exist; no-runner repo stays wall | 30m | MEDIUM |
51
- | 4 | Already-done check off the critical path (deterministic hits gate it, runs concurrent with implement) | engine10/already_done.ts, engine10/stages/intake.ts, tests already_done.test.ts | no-hit task makes zero cheap-model calls; augmentiq #52 fixture still ALREADY_SATISFIED | 30m | HIGH |
52
- | 5 | Warm engine socket in the dashboard daemon: in-memory maps keyed by tree plus dirty hash | engine10/dashboard/server.ts, new engine10/warm.ts, tests engine10/warm.test.ts | warm intake under 300ms on the fixture repo; dirty edit invalidates; daemon killed mid-run gives the same verdict cold | 30m | MEDIUM |
53
- | 6 | CLI client: 50ms socket try, cold fallback, detached daemon start after first run, `warm in Xs` text | engine10/cli.ts, new engine10/warm_client.ts, tests warm_client.test.ts | no daemon: identical events to today; LOKI_NO_BROWSER honoured; no test binds 57374-57399 | 30m | MEDIUM |
54
- | 7 | Worktree dependency prep: APFS/reflink clone of node_modules, .venv reuse, else install | new autonomy/lib/worktree_prep.py, tests/test-worktree-prep.sh | fresh worktree runs the fixture's tests with no network; copy-on-write used on darwin | 30m | MEDIUM |
55
- | 8 | Decomposer, deterministic DAG (items, write sets, union on overlap, shared files serialized) | new engine10/decompose.ts, tests engine10/decompose.test.ts | 6 fixture specs: expected unit count and write sets; same input gives byte-identical DAG | 30m | MEDIUM |
56
- | 9 | Decomposability check plus optional cheap-model confirm under a strict schema | engine10/decompose.ts (check fn only after 8 merges), tests decompose_check.test.ts | sequential fixtures return sequential with reason; malformed model JSON keeps the deterministic DAG | 30m | MEDIUM |
57
- | 10 | Unit runner: backlog.py launch loop accepts a DAG, branches loki/unit-<g>-<n>, deps respected, one slot pool shared with issues | autonomy/lib/backlog.py, tests/test-backlog-units.sh | LOKI_BACKLOG_LAUNCHER stub: max concurrency never exceeded, dependent unit starts only after its parent passes | 30m | MEDIUM |
58
- | 11 | Unit run mode: write set as scope fence, per-unit context pack and token budget, no transcript in | e10ext/scope.ts, e10ext/context.ts, tests e10ext/unit_mode.test.ts | an edit outside the write set is reverted and listed NOT PROVEN; brief contains only pack files | 30m | HIGH |
59
- | 12 | Integrator merge and fix units (conflict hunks, failing tests mapped to units, 2 rounds max) | new engine10/integrate.ts, tests engine10/integrate.test.ts | fixture with one planted conflict and one cross-unit failure ends VERIFIED with exactly 2 fix units | 30m | HIGH |
60
- | 13 | One Seal and combined receipt (per-unit sub-receipts) plus PR body unit table | engine10/stages/seal.ts, engine10/types.ts, engine10/pr_body.ts, tests seal_group.test.ts | `loki verify` passes on the combined receipt; tampering one unit's events fails it | 30m (60m review) | HIGH |
61
- | 14 | Group events and terminal unit table (TTY repaint 2s, plain lines otherwise) | engine10/events.ts, engine10/output.ts, tests output_group.test.ts | snapshot tests for TTY and non-TTY; validateEnvelope accepts group fields | 30m | MEDIUM |
62
- | 15 | Live grid: dashboard /g/<group> and Control Plane group_id column | engine10/dashboard/page.ts, packages/control-plane/src/server/runs.ts, packages/control-plane/src/db/, tests | ingesting a 3-unit fixture shows 3 rows with stage and elapsed; missing data says "no data ingested" | 30m | MEDIUM |
63
- | 16 | Route `loki "<task>"` and `loki <file>` through decomposer behind LOKI_SPEED | engine10/cli.ts (route only), engine10/supervisor.ts (group entry), tests route_group.test.ts | flag off: byte-identical behaviour; flag on: small task never decomposes | 30m | HIGH |
64
- | 17 | Speed eval tier: 8 spd- tasks (4 decomposable, 4 sequential by design), arms v10-parallel, v10-seq, raw-claude, tokens per completed for every arm | eval/loki10/tasks/spd-*, eval/loki10/harness.py, eval/loki10/summarize, eval/loki10/test-harness.sh | D38 provenance check passes; summarize prints wall, completion, tokens per arm; a capped arm is never completed | 30m x2 (tasks, harness) | MEDIUM |
65
- | 18 | METRICS D61 section and the flag flip decision | docs/v10/METRICS.md | rows cite results.jsonl paths; losses shown; flip only if section 5 targets hold | 15m | LOW |
66
-
67
- Order: 1, 2, 7, 8 start now (disjoint files); 3-6 and 9-11 next; 12-16 after 8-11; 17 in parallel from the start; 18 last. HIGH slices get opus reviewers (D12).
68
-
69
- ## 5. Targets (measured on the eval, never claimed)
70
- - A. Small tier (29 tasks): Loki p50 time to PR at or below raw `claude -p` (39s baseline, METRICS gate report) on the same model; tokens per completed at or below raw; pre-model overhead p50 under 2s warm. Losses published, including the D50 sonnet loss.
71
- - B. Speed tier (authored spd- tasks, D67): on decomposable tasks, parallel wall time at most 0.5x sequential with completion not lower; on sequential-by-design tasks the check must choose sequential (a parallel run there counts as a loss).
72
- - C. Parallel total tokens at or below v10-seq on the same task; report tokens per completed task for all three arms.
73
- - D. Every slice adds its METRICS row (what changed, command, result file, exit code). Nothing flips to default on a fixture result alone (feedback: fixture-green is not verification).
74
- - E. The `LOKI_SPEED` default flips only if 5 B and 5 C hold on the speed tier AND, on the real medium pub- tasks (plus any D34 large tasks on main), `LOKI_SPEED=1` shows completion not lower and p50 wall and tokens per completed not worse than `LOKI_SPEED=0`, at 2 runs per arm.
75
-
76
- ## 6. Risks and non-goals
77
- - Research section 4: parallel lost 39-70% on sequential work and multi-agent runs cost about 15x tokens. Defence: conservative check, no transcript sharing, single writer per file, one central integrator.
78
- - Warm daemon must never be load-bearing: cold path stays the reference; slice 6 asserts identical events.
79
- - Not in v1: stacked PRs, cross-repo groups (D51 Phase B), remote runners.
80
-
81
- ## Slice 2 status: stage prefix (LOKI_SPEED=1)
82
-
83
- With `LOKI_SPEED=1` (default off), the implement, fix, wall and plan stage prompts all begin with the same fixed block (`STAGE_PREFIX` in `loki-ts/src/e10ext/lean_prefix.ts`), so the provider prompt cache can reuse it across stages. With the flag off, prompts are unchanged byte for byte. `loki-ts/tests/e10ext/prefix_identity.test.ts` asserts the first 200 bytes match across stages and that flag-off output is unprefixed.
84
-
85
- ## Slice 4 status: already-done check off the critical path (LOKI_SPEED=1)
86
-
87
- With `LOKI_SPEED=1` (default off), intake no longer waits on the cheap-model already-done check. Implement starts at once; the check runs only when deterministic hits exist, and its confirmation session runs in a pinned copy of the base tree extracted to a temporary directory outside the repo (`git archive -o` then `tar -x`, each exit code checked), so in-flight edits cannot satisfy it. A run stops as ALREADY_SATISFIED only when that session cites files unchanged since base; a bad base SHA or any extract failure fails closed (no session, not satisfied). Code: `loki-ts/src/features/speed/already_done_async.ts`, tests in `loki-ts/tests/engine10/already_done.test.ts`. Known follow-up (D61-04-F): the temporary copy can outlive a SIGTERM exit, and four race tests leave one copy each.
88
-
89
- ## D82-FLAGS status: default on
90
-
91
- Per the D82 founder directive, `LOKI_SPEED` is on unless set to `0` (decomposer routing, warm engine on a unix socket, stage prefix, deferred already-done check, lean keyword fallback, unit mode when `LOKI_UNIT_SPEC` is set). `LOKI_SPEED=0` restores the previous behaviour byte for byte. The other D82 opt-outs are `LOKI_VISUAL_EVIDENCE=0`, `LOKI_CONTRACT=0`, `LOKI_SLACK_INBOUND=0` (Slack stays inert until its credential is configured), `LOKI_CONTROL=0` and `LOKI_WORKSPACES=0`. Test: `loki-ts/tests/features/d82_default_on.test.ts`.
92
-
93
- ## Missions and group Seal (v11.8.0)
94
-
95
- Missions: a spec with two or more milestones runs milestone by milestone. Format: `## Milestone N[: title]` headings (the body is everything up to the next milestone heading), or a top-level `milestones:` list of `- title` lines. Fewer than 2 milestones is not a mission and takes the normal path unchanged. Parsing is `parseMilestones` in `loki-ts/src/features/decompose.ts`; the loop is in `maybeRunGroup` (`loki-ts/src/features/speed/route.ts`), reached from `loki start` and from chat `/run` through the same supervisor call.
96
-
97
- Each milestone prints `loki: milestone k/N: <title>`, is decomposed on its own, and runs as its own group with its own frozen contract and group Seal. One-unit groups and sequential providers are allowed for milestones (the normal fewer-than-2-units and slot fallbacks do not apply). The mission stops at the first non-zero result, prints `loki: milestone k/N failed (exit X); rerun the same loki start to resume`, and returns that code; later milestones never start. All milestones exit 0 returns 0.
98
-
99
- Group exit codes: 0 = every unit VERIFIED and the group receipt VERIFIED; 4 = NOT PROVEN (integrated, but no checked group receipt); 1 = failures.
100
-
101
- Resume: rerun the same `loki start`. Each milestone group is found again by the hash of its body, so finished milestones re-verify and return 0 fast and the run continues at the failed one. No extra state is kept. `LOKI_GROUP_RESUME=0` disables auto-resume and starts every group fresh. Check any group receipt with `loki verify .loki/groups/<gid>/receipt.json`.
102
-
103
- Test command precedence: `LOKI_GROUP_TEST_CMD` (whitespace split) wins over the repo's own test script. Test: `loki-ts/tests/engine10/route_group.test.ts`.
104
-
105
- Fix units: when group integration stops on a merge conflict or a failing suite, one bounded fix unit runs through the same unit launcher (same scoped env, one slot) from the group branch, then integration restarts once. A conflict fix takes the conflicting unit's place; a suite fix goes last and receives the failing test names and output tail. Max one fix per conflict and 2 per group; `LOKI_GROUP_FIX_UNITS=0` restores stop-and-name. The suite, every unit receipt and the group Seal still decide the verdict, and a failed fix keeps the stop-and-name output (exit 1). A fix unit is stored in `dag.json` and resumes like any unit. Code: `loki-ts/src/features/speed/fix_units.ts`; tests: `loki-ts/tests/engine10/seal_group.test.ts`.
@@ -1,354 +0,0 @@
1
- # D63 remaining slice cards (Architect, main 23298d2eb)
2
-
3
- Scope: Control Plane default, Slack, Jira and Linear, multi-repo, MCP, containers, and the stub-provider E2E. Visual and contract flips and the D61 speed slices are carded elsewhere. These cards are a design; nothing here is implemented.
4
-
5
- ## Findings
6
-
7
- Control Plane
8
- - `loki control` is gated on `LOKI_CONTROL=1` (`loki-ts/src/commands/control.ts:71`).
9
- - `LOKI_CONTROL_DEFAULT` exists only in bash `cmd_dashboard` (`autonomy/loki:7330`). Bare `loki` still runs `cmd_ui`, which starts the old dashboard.
10
- - Runs ship only when `LOKI_CONTROL_URL` is set (`engine10/supervisor.ts:189`). The `instance.json` discovery described in `docs/v10/CONTROL-PLANE.md:52` does not exist.
11
- - The BLOCKED answer UI exists (`packages/control-plane/ui/src/App.tsx:168-216`, `src/server/answer.ts`). It only writes `~/.loki/control/answers/<src>/<run>.answer.txt`; nothing reads that file to resume the run.
12
-
13
- Security gap (HIGH)
14
- - `docs/v10/CONTROL-PLANE.md:43` says a non-loopback bind refuses without `LOKI_CONTROL_TOKEN` and that `/v1` needs a Bearer token. Neither is implemented: `app.ts` has no auth middleware and `serve.ts` binds whatever `LOKI_CONTROL_HOST` says.
15
- - `Dockerfile.control-plane`, the Helm chart and the ECS task all set `0.0.0.0`, so the shipped container exposes unauthenticated ingest and answer-write on every interface.
16
- - On loopback there is no Host check (DNS rebinding).
17
-
18
- Routing (`bin/loki`)
19
- - `slack` is missing from the Bun allowlist at `bin/loki:570`, so the documented `loki slack serve` is unreachable from an installed CLI.
20
- - `jira:PROJ-1` and `linear:ENG-42` are one word and fall to the legacy engine.
21
- - Linear URLs (`/issue/`, singular) match neither `*/issues/*` nor `*/browse/*` and also go legacy. Only Jira `https://...atlassian.net/browse/` URLs reach v10.
22
-
23
- Slack
24
- - The default launcher spawns `process.execPath process.argv[1] <task>`, which is `cli.ts`, not `bin/loki`; `cli.ts` has no task route, so the launch fails.
25
- - The launcher passes `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET` into the child run's env.
26
- - Outbound with no webhook is already a clean no-op (`engine10/adapters/slack.ts:19`).
27
-
28
- Jira and Linear
29
- - Already on with no flag. A missing credential throws an error naming the variable, before any LLM call. Self-hosted Jira URLs are not parsed.
30
-
31
- Multi-repo
32
- - `autonomy/lib/workspace.py` is a 290-line compact version: repos run sequentially; no `show`, `status` or `clean`; no exit code 3; SIGINT not handled. The gate exists twice (`workspace.py:50`, `autonomy/loki:12926`).
33
-
34
- MCP
35
- - The three v10 tools are always registered. `mcp/tests/test_v10_tools.py` is not run by any runner, workflow or local-ci.
36
-
37
- Containers
38
- - Files exist; the only check is `packages/control-plane/test/deploy/container.test.ts`. The Dockerfile comment says "no authentication of its own". The token is optional in Helm.
39
-
40
- Stub provider (BUG-2 in E2E-D65.md)
41
- - A usage gap, not missing code: `LOKI_E10_INVOKER=cli` plus `LOKI_CLAUDE_CLI=<stub>` already drives v10 with a stub (`scripts/first-run-gate.sh:98`, `docs/v10/FIRST-RUN-GATE.md:11`).
42
-
43
- ## File-set rules for every card
44
- - CHANGELOG: each card supplies its sentence; the Release Manager writes `CHANGELOG.md` at the train. No card edits it.
45
- - README: each card edits only the README section it names.
46
- - Headless tests: `LOKI_NO_BROWSER=1`, a throwaway `HOME`, `--port 0` or a stub server, never a port in 57374-57399. Stop only recorded PIDs; never kill by name or pattern.
47
- - Every spawn under `loki-ts/src` passes an explicit env. No stored credentials are read.
48
- - No new `tests/*.sh` except in C5, which owns `tests/run-all-tests.sh` and `tests/shard-durations.tsv`. Other cards extend existing suites.
49
- - Caps: core engine10 has 64 lines left (D71), e10ext 33 lines left. New code goes in `loki-ts/src/features` or `packages/`.
50
-
51
- ---
52
-
53
- ## C1 CP-AUTH: Control Plane auth enforced as documented
54
- - Tier: HIGH (auth, security). Budget: 30 min.
55
- - Goal:
56
- - With `LOKI_CONTROL_TOKEN` set, every `/v1/*` call needs `Authorization: Bearer <token>`, compared with `timingSafeEqual`. `/health` and `/ready` stay open.
57
- - A non-loopback `LOKI_CONTROL_HOST` without a token refuses to start: exit 2 with a line naming `LOKI_CONTROL_TOKEN`. Override: `LOKI_CONTROL_ALLOW_INSECURE_BIND=1`.
58
- - On loopback, a request whose Host is not `127.0.0.1`, `localhost` or `[::1]` (any port) gets 403.
59
- - The UI accepts `#token=` once, keeps it in sessionStorage and sends it on each call.
60
- - Files:
61
- - `packages/control-plane/src/server/auth.ts` (new), `packages/control-plane/src/server/app.ts`, `packages/control-plane/src/server/serve.ts`
62
- - `packages/control-plane/ui/src/api.ts`, `packages/control-plane/ui/dist/**` (rebuild)
63
- - `packages/control-plane/test/server/auth.test.ts` (new)
64
- - `docs/v10/CONTROL-PLANE.md` (Auth section only)
65
- - Wall checks:
66
- - With a token: no header 401, wrong token 401, right token 200 on GET `/v1/runs`, POST `/v1/ingest`, POST `.../answer`.
67
- - No token on loopback: behaviour unchanged, existing tests green.
68
- - `serve.ts` with `LOKI_CONTROL_HOST=0.0.0.0` and no token exits 2 without binding (explicit env, port 0).
69
- - `Host: evil.example` on loopback gives 403.
70
- - Mutation: removing the middleware turns the 401 tests red.
71
- - Commands: `cd packages/control-plane && bun test ./test/ && bun run typecheck && bun run build:all`
72
- - CHANGELOG: "The Control Plane now enforces `LOKI_CONTROL_TOKEN` on every API call and refuses to listen on a non-loopback address without one."
73
- - Docs: `docs/v10/CONTROL-PLANE.md` Auth section.
74
- - Dependencies: none. Blocks C10.
75
-
76
- ## C2 CP-DEFAULT: Control Plane on by default; local runs ship to it
77
- - Tier: MEDIUM. Budget: 30 min.
78
- - Goal:
79
- - `LOKI_CONTROL` defaults on; `LOKI_CONTROL=0` prints one "off" line and exits 0.
80
- - `serve` reads the child's "listening on" line and writes `~/.loki/control/instance.json` `{pid, port, url, version, install_path, db}` (mode 0600), removed when the CLI exits.
81
- - The ship hook ships when `LOKI_CONTROL_URL` is set, or when `instance.json` names a live pid (`kill -0`) whose `/health` answers `service=loki-control` within 300 ms. It never starts a server. `LOKI_CONTROL=0` disables discovery.
82
- - Files:
83
- - `loki-ts/src/commands/control.ts`
84
- - `packages/control-plane/src/shipper/discover.ts` (new)
85
- - `loki-ts/src/e10ext/ship_hook.ts` (at most 8 lines)
86
- - `loki-ts/src/engine10/supervisor.ts` (line 189 only, 1-line condition change)
87
- - `loki-ts/tests/commands/control_default.test.ts` (new)
88
- - `tests/test-control-plane.sh` (update the "ungated" assertion)
89
- - `docs/v10/CONTROL-PLANE.md` (sections 5 and 6)
90
- - README "Control Plane" section
91
- - Wall checks:
92
- - No `instance.json` gives zero fetch calls (injected fetch).
93
- - A stale pid is ignored; a live stub `/health` with the wrong service is ignored; a valid instance gives one ship.
94
- - `LOKI_CONTROL=0` gives no discovery.
95
- - `instance.json` is mode 0600 and gone after serve exits.
96
- - `budget.test.ts` passes (core under 5000, e10ext under 1500).
97
- - Commands: `cd loki-ts && bun test tests/commands/control_default.test.ts tests/engine10/budget.test.ts && bunx tsc --noEmit`; `bash tests/test-control-plane.sh`
98
- - CHANGELOG: "The Control Plane is on by default: `loki control serve` needs no flag, and runs on this machine appear in it automatically while it is running."
99
- - Docs: `docs/v10/CONTROL-PLANE.md` sections 5 and 6, README Control Plane section.
100
- - Dependencies: none. C3 builds against its `instance.json` contract.
101
-
102
- ## C3 BASH-FLIPS: bare `loki` opens the Control Plane; workspace gate flips
103
- - Tier: MEDIUM. Budget: 30 min.
104
- - Goal:
105
- - `cmd_ui` (bare `loki`) and `cmd_dashboard ""|start|open` use the Control Plane by default; `LOKI_CONTROL_DEFAULT=0` restores the old dashboard.
106
- - Reuse a live `instance.json` URL; otherwise start `loki control serve` detached, record its PID and print the URL.
107
- - Headless (`LOKI_HEADLESS=1`, `LOKI_NO_BROWSER=1` or `--no-open`): print only, never open.
108
- - No bun: the old dashboard plus one line naming bun.
109
- - `cmd_workspace`: the gate becomes "`LOKI_WORKSPACES=0` disables".
110
- - Help lines 1633 and 1786 drop "preview" and "needs".
111
- - Files:
112
- - `autonomy/loki` (`cmd_ui`, `cmd_dashboard` head, `cmd_workspace`, the two help lines only)
113
- - `tests/test-ui-bare-loki.sh` (pin the legacy leg to `LOKI_CONTROL_DEFAULT=0`; add a Control Plane leg with a python stub answering `/health` with `service=loki-control`)
114
- - `docs/dashboard.md` or the nearest dashboard doc (one paragraph)
115
- - Wall checks:
116
- - Control Plane leg: bare `loki` with `LOKI_NO_BROWSER=1` and a stub `instance.json` prints the stub URL; the logging `open` stub has zero calls.
117
- - Legacy leg output unchanged.
118
- - No port in 57374-57399 bound (lsof on recorded PIDs).
119
- - shellcheck on the changed test; `bash -n autonomy/loki`.
120
- - Commands: `bash tests/test-ui-bare-loki.sh; bash -n autonomy/loki; shellcheck tests/test-ui-bare-loki.sh`
121
- - CHANGELOG: "`loki` with no arguments now opens the Control Plane (set `LOKI_CONTROL_DEFAULT=0` for the previous dashboard)."
122
- - Docs: dashboard doc paragraph.
123
- - Dependencies: merges on the same train as C2 or one later.
124
-
125
- ## C4 BLOCKED-RESUME: `loki answer` closes the BLOCKED loop
126
- - Tier: MEDIUM. Budget: 30 min.
127
- - Goal:
128
- - New `loki answer [<run-id>] [--text "..."]`; default run is the newest BLOCKED run.
129
- - Reads the run's task and question from events, and the answer from `--text` or the Control Plane answer file.
130
- - Launches `bin/loki "<task>\n\nClarification answering \"<q>\": <a>"` with an explicit env (`LOKI_NO_BROWSER=1`; no `SLACK_*` and no `LOKI_CONTROL_TOKEN`) and prints the new run id.
131
- - Refuses a run that is not BLOCKED (exit 2, says why).
132
- - The Control Plane `writeAnswer` resume string becomes `loki answer <run>`.
133
- - Files:
134
- - `loki-ts/src/features/blocked_answer.ts` (new, at most 120 lines)
135
- - `loki-ts/src/cli.ts` (`answer` case plus the `control` help line)
136
- - `packages/control-plane/src/server/answer.ts` (resume string only)
137
- - `loki-ts/tests/features/blocked_answer.test.ts` (new)
138
- - `packages/control-plane/test/server/answer.test.ts` (resume assertion)
139
- - `docs/v10/CONTROL-PLANE.md` ("Answering a BLOCKED run" section)
140
- - README "Answering a BLOCKED run" paragraph
141
- - Wall checks:
142
- - A fixture BLOCKED run plus an answer file gives a spawn argv containing the task, question and answer (spawn injected).
143
- - The env lacks `SLACK_BOT_TOKEN` and has `LOKI_NO_BROWSER=1`.
144
- - A non-BLOCKED run gives exit 2.
145
- - The features budget test passes.
146
- - Commands: `cd loki-ts && bun test tests/features/blocked_answer.test.ts tests/engine10/budget.test.ts && bunx tsc --noEmit`; `cd packages/control-plane && bun test test/server/answer.test.ts`
147
- - CHANGELOG: "`loki answer` resumes a BLOCKED run with the answer typed in the Control Plane (or `--text`)."
148
- - Docs: CONTROL-PLANE.md section and README paragraph above.
149
- - Dependencies: CLI reachability via C5 (unit tests independent).
150
-
151
- ## C5 ROUTING: shim entry points for D65 features
152
- - Tier: MEDIUM. Budget: 20 min.
153
- - Goal:
154
- - The `bin/loki:570` Bun allowlist gains `slack|answer`.
155
- - v10 routing (top level and the `start` case) gains `jira:*|linear:*|https://linear.app/*/issue/*`.
156
- - Files:
157
- - `bin/loki`
158
- - `tests/test-d65-routing.sh` (new, `timeout -k`)
159
- - `tests/run-all-tests.sh` (one `run_test` row)
160
- - `tests/shard-durations.tsv` (one row)
161
- - Wall checks:
162
- - With a probe bun that echoes argv: `loki slack serve` and `loki answer x` reach `cli.ts`; `loki jira:ABC-1`, `loki linear:ENG-2` and a Linear URL reach `engine10`; one-word `loki foo` stays legacy.
163
- - Real-CLI leg: `loki jira:ABC-1` with no `JIRA_*` env exits non-zero in under 5 s, names `JIRA_BASE_URL`, and the stub claude logs zero calls.
164
- - `tests/test-shard-coverage.sh` and shellcheck pass.
165
- - Commands: `bash tests/test-d65-routing.sh; bash tests/test-shard-coverage.sh; shellcheck bin/loki tests/test-d65-routing.sh`
166
- - CHANGELOG: "`loki jira:KEY`, `loki linear:KEY`, Linear issue URLs and `loki slack serve` now reach the Loki 10 engine directly."
167
- - Docs: none beyond C6 and C7 docs.
168
- - Dependencies: none. Merge early; C4, C6, C7, C11a, C11b CLI-level checks rely on it.
169
-
170
- ## C6 SLACK: two-way Slack polished and on by default
171
- - Tier: MEDIUM. Budget: 25 min.
172
- - Goal:
173
- - Inbound defaults on; `LOKI_SLACK_INBOUND=0` disables.
174
- - With no `SLACK_BOT_TOKEN` or `SLACK_SIGNING_SECRET`: one line naming both, exit 2, nothing bound.
175
- - The launcher spawns `REPO_ROOT/bin/loki` (not `argv[1]`) with an explicit env that strips both Slack secrets and sets `LOKI_NO_BROWSER=1`.
176
- - The poster logs an HTTP failure as one redacted line.
177
- - Outbound unchanged: no webhook, no call.
178
- - Files:
179
- - `loki-ts/src/features/slack_inbound.ts`
180
- - `loki-ts/tests/features/slack_inbound.test.ts`
181
- - `docs/slack.md`
182
- - README Slack paragraph (around line 173)
183
- - Wall checks:
184
- - No credentials: exit 2 and `Bun.serve` never called (injected).
185
- - Spawn argv[0] ends in `bin/loki`; env lacks both Slack secrets.
186
- - Signed `url_verification` returns the challenge; a bad signature returns 401.
187
- - BLOCKED (exit 4) posts the question; a thread reply relaunches with the clarification.
188
- - `LOKI_SLACK_INBOUND=0` gives exit 2 with the disabled line.
189
- - Commands: `cd loki-ts && bun test tests/features/slack_inbound.test.ts && bunx tsc --noEmit`
190
- - CHANGELOG: "Two-way Slack is on by default: with a bot token and signing secret, `@loki <task>` in a thread starts a run and a BLOCKED question is answered in the same thread."
191
- - Docs: `docs/slack.md`, README Slack paragraph.
192
- - Dependencies: CLI reachability via C5.
193
-
194
- ## C7 TRACKERS: Jira and Linear intake polish
195
- - Tier: MEDIUM. Budget: 20 min.
196
- - Goal:
197
- - `LOKI_TRACKER_INTAKE=0` kill switch (`parseTrackerRef` returns null).
198
- - Self-hosted Jira: `<JIRA_BASE_URL>/browse/KEY` parses when its origin equals `JIRA_BASE_URL`.
199
- - Error text names the exact variables per tracker (the one fix).
200
- - Linear: `errors[]` with no data, or HTTP 401, names `LINEAR_API_KEY`.
201
- - Intake only; no sync claims.
202
- - Files:
203
- - `loki-ts/src/features/tracker_intake.ts`
204
- - `loki-ts/tests/engine10/tracker_intake.test.ts`
205
- - `docs/trackers.md` (new)
206
- - README "Issue intake" paragraph
207
- - Wall checks:
208
- - Injected fetch only; zero network.
209
- - Self-hosted URL parses only when `JIRA_BASE_URL` matches, else null.
210
- - Kill switch gives null.
211
- - Missing credentials: message names the variable.
212
- - ADF fixture output unchanged.
213
- - Commands: `cd loki-ts && bun test tests/engine10/tracker_intake.test.ts && bunx tsc --noEmit`
214
- - CHANGELOG: "Jira (cloud and self-hosted) and Linear issues start a Loki 10 run with `loki jira:KEY` or `loki linear:KEY`, using credentials from the environment only."
215
- - Docs: `docs/trackers.md`, README Issue intake paragraph.
216
- - Dependencies: CLI reachability via C5.
217
-
218
- ## C8 WORKSPACES: multi-repo parity and the flag flipped
219
- - Tier: MEDIUM. Budget: 30 min.
220
- - Goal:
221
- - `enabled()` defaults on; `LOKI_WORKSPACES=0` disables.
222
- - Repos with no pending `after` run in parallel, bounded by `concurrency` (default 2).
223
- - Add `show <ws>` and `status [<run>]`, reading `integration.json`.
224
- - Exit 3 when the only non-ok outcomes are budget stops (child exit 3); 1 otherwise.
225
- - SIGINT handled like SIGTERM (killpg on recorded PIDs).
226
- - Files:
227
- - `autonomy/lib/workspace.py`
228
- - `tests/test_workspace.py`
229
- - `tests/workspace/50-run.sh` (new part, auto-sourced by the registered `tests/test-workspace.sh`)
230
- - `docs/WORKSPACES.md`
231
- - `docs/v10/D51-PHASE-B.md` (status block)
232
- - `tests/docs-drift-allowlist.tsv` (remove rows 44-46; the commands now exist)
233
- - Wall checks (stub launcher):
234
- - Two independent repos overlap in time (start timestamps).
235
- - The `after` dependent starts after its predecessor; a failed predecessor makes it SKIPPED.
236
- - All children exiting 3 gives group exit 3.
237
- - `status` prints per-repo rows.
238
- - `LOKI_WORKSPACES=0` gives exit 2.
239
- - The docs drift guard passes.
240
- - Commands: `python3 -m pytest tests/test_workspace.py -q && bash tests/test-workspace.sh` plus the suite that reads `docs-drift-allowlist.tsv`
241
- - CHANGELOG: "Multi-repo workspaces are on by default: `loki workspace run <ws> <ref>` runs each repo in parallel, respects `after` ordering and records integration evidence."
242
- - Docs: `docs/WORKSPACES.md`, `docs/v10/D51-PHASE-B.md`.
243
- - Dependencies: the bash gate flip is in C3; `workspace.py` is gated independently, so either order works.
244
-
245
- ## C9 MCP: v10 tools wired into CI and documented
246
- - Tier: MEDIUM. Budget: 20 min.
247
- - Goal:
248
- - `mcp/tests/test_v10_tools.py` runs in CI through the existing `tests/test-mcp-server.sh` (no new runner row).
249
- - `_env()` forces `LOKI_NO_BROWSER=1` and drops `LOKI_CONTROL_TOKEN` and the Slack secrets.
250
- - `v10_status` returns `blocked_question` when the verdict is BLOCKED.
251
- - `v10_run` passes `jira:` and `linear:` refs through; the leading-dash guard stays.
252
- - Files:
253
- - `mcp/v10_tools.py`
254
- - `mcp/tests/test_v10_tools.py`
255
- - `tests/test-mcp-server.sh`
256
- - `docs/mcp.md` (or the existing MCP doc)
257
- - README MCP section (with the `claude mcp add` line)
258
- - Wall checks:
259
- - pytest green.
260
- - The suite counts the v10 tests (positive control: one deliberately failing assert, run locally, turns the suite red).
261
- - Child env has no secrets.
262
- - A BLOCKED fixture returns the question.
263
- - `tests/test-mcp-tool-surface-packaged.sh` still lists all three tools.
264
- - Commands: `python3 -m pytest mcp/tests/test_v10_tools.py -q && bash tests/test-mcp-server.sh && bash tests/test-mcp-tool-surface-packaged.sh`
265
- - CHANGELOG: "The MCP server's `loki_v10_run`, `loki_v10_status` and `loki_v10_verify` tools are tested in CI and report a BLOCKED run's question."
266
- - Docs: MCP doc, README MCP section.
267
- - Dependencies: none.
268
-
269
- ## C10 CONTAINERS: image, Helm and ECS aligned with token auth
270
- - Tier: MEDIUM (the reviewer reads C1). Budget: 20 min.
271
- - Goal:
272
- - Dockerfile: drop the "no authentication" comment; document that `LOKI_CONTROL_TOKEN` is required on `0.0.0.0`.
273
- - Helm: `controlToken.existingSecret` required, failing at template time when unset (`required`), unless `allowInsecureBind: true` sets `LOKI_CONTROL_ALLOW_INSECURE_BIND=1`.
274
- - ECS: already maps the token; README states it.
275
- - Files:
276
- - `Dockerfile.control-plane`
277
- - `deploy/helm/control-plane/**`
278
- - `deploy/ecs/**`
279
- - `docs/control-plane-container.md`
280
- - `packages/control-plane/test/deploy/container.test.ts`
281
- - Wall checks (static, no helm or docker needed):
282
- - The deployment template references the `LOKI_CONTROL_TOKEN` `secretKeyRef` and a `required` guard.
283
- - The ECS task carries `LOKI_CONTROL_TOKEN` via `valueFrom`.
284
- - Dockerfile has `USER loki`, a HEALTHCHECK on `/ready`, and no "no authentication" text.
285
- - If `helm` is on PATH: `helm template` without the secret fails and with it succeeds. Otherwise the test prints SKIP (helm absent) and never passes silently.
286
- - Commands: `cd packages/control-plane && bun test test/deploy/`
287
- - CHANGELOG: "The Control Plane container, Helm chart and ECS task now require an access token whenever the service is exposed beyond localhost."
288
- - Docs: `docs/control-plane-container.md`, `deploy/helm/README.md`, `deploy/ecs/README.md`.
289
- - Dependencies: ships on the same train as C1 or later, never before.
290
-
291
- ## C11a E2E-HARNESS: hermetic `npm pack` E2E, legs 1-4
292
- - Tier: MEDIUM. Budget: 30 min.
293
- - Goal: `scripts/e2e-d65.sh`
294
- - `npm pack`, install into a temp prefix under a run-owned dir (`loki_run_tmp_create` pattern) with a throwaway `HOME` and `npm_config_cache`.
295
- - Stub `claude` on PATH (reuse the `first-run-gate.sh` stub shape; writes only on a real `-p` call).
296
- - `LOKI_E10_INVOKER=cli`, `LOKI_CLAUDE_CLI=<stub>`, `LOKI_NO_BROWSER=1`, `GIT_CONFIG_GLOBAL=/dev/null`; no `*_API_KEY` or `*_TOKEN` (`env -i` plus an allowlist).
297
- - Sources `scripts/e2e-d65/legs/*.sh`; prints one `PASS|FAIL|SKIP(<reason>)` per leg; exit 0 if and only if there is no FAIL.
298
- - `E2E_FORCE_FAIL=<leg>` is a positive control.
299
- - Legs:
300
- - 1 D61 speed: `LOKI_SPEED=1` on a 2-file fixture task; receipt exists.
301
- - 2 Visual evidence: no Playwright gives a NOT PROVEN line, not a failure.
302
- - 3 Control Plane: `serve --port 0` (recorded PID); a run ships via `instance.json`; GET `/v1/runs` has at least one row; POST `answer` on the BLOCKED fixture returns 200.
303
- - 4 Slack: `serve --port 0` with fake secrets; signed `url_verification` returns the challenge; bad signature returns 401; nothing outbound.
304
- - Files:
305
- - `scripts/e2e-d65.sh` (new)
306
- - `scripts/e2e-d65/legs/01-speed.sh`, `02-visual.sh`, `03-control.sh`, `04-slack.sh` (new)
307
- - `scripts/e2e-d65/stub-claude.sh` (new)
308
- - Wall checks:
309
- - shellcheck clean.
310
- - A run on the current tree exits 0, or each FAIL names a bug.
311
- - `E2E_FORCE_FAIL=3` gives exit 1.
312
- - Afterwards no recorded PID is alive and the run dir is removed.
313
- - Commands: `bash scripts/e2e-d65.sh` (timeout 10 min); `shellcheck scripts/e2e-d65.sh scripts/e2e-d65/*.sh scripts/e2e-d65/legs/*.sh`
314
- - CHANGELOG: none (tooling); C11b documents it in `docs/v10/E2E-D65.md`.
315
- - Dependencies: final run after C1-C10 merge; leg 3 needs C2, leg 4 needs C5 and C6.
316
-
317
- ## C11b E2E-LEGS: legs 5-9 and the results doc
318
- - Tier: MEDIUM. Budget: 30 min.
319
- - Legs:
320
- - 5 Jira: `jira:ABC-1` against a local fake `JIRA_BASE_URL=http://127.0.0.1:<port0>` gives a run whose `issue.json` has `source=jira`; with no credentials it exits fast naming the variable.
321
- - 6 Spec to contract: a `.loki/contract.json` fixture gives a receipt containing the contract section.
322
- - 7 Multi-repo: two local git repos in `loki.yaml` give two runs plus `integration.json`.
323
- - 8 MCP: `python3` imports `mcp.v10_tools` from the installed package; `v10_status` then `v10_verify` on leg 6's repo.
324
- - 9 Containers: deploy files exist in the repo; if `docker` is on PATH and the daemon answers, build `Dockerfile.control-plane` and probe `/ready` on a random host port; otherwise SKIP(docker unavailable), never PASS.
325
- - Results doc: append section 6 "Stub E2E (`scripts/e2e-d65.sh`)" to `docs/v10/E2E-D65.md` with version, date, per-leg table (exit codes, seconds), the note that BUG-2 is resolved by `LOKI_E10_INVOKER=cli`, and which legs SKIP and why.
326
- - Files:
327
- - `scripts/e2e-d65/legs/05-jira.sh`, `06-contract.sh`, `07-workspaces.sh`, `08-mcp.sh`, `09-containers.sh` (new)
328
- - `docs/v10/E2E-D65.md`
329
- - Wall checks: as C11a, plus every leg is cited with its rc line in the doc.
330
- - Commands: `bash scripts/e2e-d65.sh` (with the C11a harness)
331
- - CHANGELOG: none (tooling).
332
- - Docs: `docs/v10/E2E-D65.md`.
333
- - Dependencies: C11a harness; leg 5 needs C5 and C7, leg 7 needs C8, leg 8 needs C9, leg 9 needs C10.
334
-
335
- ## C12 STUB-INVOKER (optional; drop if fix-e2e-bugs already owns BUG-2)
336
- - Tier: MEDIUM. Budget: 15 min.
337
- - Goal: setting `LOKI_CLAUDE_CLI` implies `LOKI_E10_INVOKER=cli`, so a stub path alone drives v10. Core edit at most 3 lines.
338
- - Files:
339
- - `loki-ts/src/engine10/session.ts` (around line 39 only)
340
- - `loki-ts/tests/engine10/session_invoker.test.ts` (new)
341
- - `docs/v10/FIRST-RUN-GATE.md` (one line)
342
- - Wall checks:
343
- - `LOKI_CLAUDE_CLI` set: env has no `LOKI_SDK_LOOP`.
344
- - Unset: unchanged (`LOKI_SDK_LOOP=1`).
345
- - Budget test passes.
346
- - Commands: `cd loki-ts && bun test tests/engine10/session_invoker.test.ts tests/engine10/budget.test.ts`
347
- - CHANGELOG: "Setting `LOKI_CLAUDE_CLI` now selects the Claude CLI invoker, so a stub or custom `claude` binary drives Loki 10 directly."
348
- - Docs: `docs/v10/FIRST-RUN-GATE.md`.
349
- - Dependencies: none; C11a works without it.
350
-
351
- ---
352
-
353
- ## Ordering and parallelism
354
- All 12 cards can start now; file sets do not overlap. C1 (HIGH, opus reviewer) is the critical path and goes on the first train; C10 ships on the same train or later. C5 merges early because the CLI-level checks of C4, C6, C7 and both E2E cards reach their commands through it. C2 and C3 together make the Control Plane the default; they build in parallel against C2's `instance.json` contract, and C3 merges on the same train as C2 or one later. C11a and C11b start in parallel, but the final E2E runs only on a tree containing C1-C10. C12 goes only if fix-e2e-bugs has not fixed BUG-2. With 8 engineers: C1, C5, C2, C6, C7, C8, C9, C4 first; then C3, C10, C11a, C11b, C12 as seats free. Only C1 needs the HIGH review.