loki-mode 9.54.0 → 9.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +2 -2
  3. package/VERSION +1 -1
  4. package/autonomy/completion-council.sh +120 -35
  5. package/autonomy/crash.sh +6 -6
  6. package/autonomy/lib/done-recognition.sh +7 -0
  7. package/autonomy/lib/prd-enrich.sh +7 -0
  8. package/autonomy/lib/proof-generator.py +36 -5
  9. package/autonomy/lib/proof-verify.py +93 -21
  10. package/autonomy/lib/voter-agents.sh +7 -0
  11. package/autonomy/loki +404 -119
  12. package/autonomy/quickstart.sh +7 -0
  13. package/autonomy/run.sh +836 -58
  14. package/autonomy/verify.sh +114 -8
  15. package/bin/loki +12 -0
  16. package/dashboard/__init__.py +1 -1
  17. package/dashboard/api_v2.py +4 -0
  18. package/dashboard/audit.py +9 -1
  19. package/dashboard/auth.py +16 -4
  20. package/dashboard/registry.py +34 -12
  21. package/dashboard/rigour_integration.py +25 -17
  22. package/dashboard/server.py +252 -104
  23. package/dashboard/static/cost.html +55 -26
  24. package/dashboard/static/index.html +475 -1544
  25. package/dashboard/static/proofs.html +1 -1
  26. package/docs/INSTALLATION.md +7 -6
  27. package/docs/air-gapped.md +38 -11
  28. package/docs/enterprise/security.md +10 -13
  29. package/docs/environment-variables.md +1 -0
  30. package/docs/exit-codes.md +76 -2
  31. package/docs/image-provenance.md +4 -2
  32. package/docs/v10/BACKLOG.md +64 -2
  33. package/docs/v10/BOARD.md +134 -0
  34. package/docs/v10/CONTROL.md +36 -0
  35. package/docs/v10/DECISIONS.md +102 -0
  36. package/docs/v10/METRICS.md +78 -1
  37. package/docs/v10/PROGRESS.md +240 -0
  38. package/docs/v10/SWARM-PROMPT.md +98 -0
  39. package/docs/v10/SWARM.md +237 -0
  40. package/loki-ts/dist/loki.js +7 -4
  41. package/mcp/__init__.py +1 -1
  42. package/package.json +1 -1
  43. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  44. package/providers/aider.sh +8 -0
  45. package/providers/cline.sh +8 -0
  46. package/providers/codex.sh +9 -0
  47. package/providers/loader.sh +10 -0
  48. package/providers/opencode.sh +9 -0
  49. package/tools/receipt-attest.py +1 -1
  50. package/web-app/dist/assets/AdminPage-CZrBkzRF.js +32 -0
  51. package/web-app/dist/assets/Avatar-gQIp6FBI.js +11 -0
  52. package/web-app/dist/assets/{Badge-BF3mhT57.js → Badge-D2_fBTFY.js} +1 -1
  53. package/web-app/dist/assets/{Button-BDp9aJ9q.js → Button-D5qDd2ef.js} +1 -1
  54. package/web-app/dist/assets/CockpitPage-DVMk2RUK.js +47 -0
  55. package/web-app/dist/assets/ComparePage-NMLyE1xL.js +1 -0
  56. package/web-app/dist/assets/{ErrorBoundary-ut3wrUmu.js → ErrorBoundary-BRABI-BT.js} +1 -1
  57. package/web-app/dist/assets/EvidenceReceiptPanel-D2LNKqUx.js +1 -0
  58. package/web-app/dist/assets/GitHubIssuesPanel-BC5GzKUN.js +7 -0
  59. package/web-app/dist/assets/{GitHubPRsPanel-DurZgSaj.js → GitHubPRsPanel-BSlmoVTS.js} +3 -3
  60. package/web-app/dist/assets/HomePage-D_CDMoen.js +12 -0
  61. package/web-app/dist/assets/{LoginPage-HeRGJeZy.js → LoginPage-BBNA_N9t.js} +1 -1
  62. package/web-app/dist/assets/{MagicPage-CUKrqsZs.js → MagicPage-CnFRZCMx.js} +1 -1
  63. package/web-app/dist/assets/MetricsPage-CPfLMRlh.js +6 -0
  64. package/web-app/dist/assets/{NotFoundPage-BZDZ-B77.js → NotFoundPage-lA0XSGTz.js} +1 -1
  65. package/web-app/dist/assets/ProjectPage-CNAPbr9z.js +256 -0
  66. package/web-app/dist/assets/ProjectsPage-CMtm8goN.js +6 -0
  67. package/web-app/dist/assets/SettingsPage-BhIjVMYE.js +10 -0
  68. package/web-app/dist/assets/{ShowcasePage-BGS2jTGH.js → ShowcasePage-BndtGeYo.js} +1 -1
  69. package/web-app/dist/assets/SystemSettingsPage-euXI-KXE.js +1 -0
  70. package/web-app/dist/assets/TeamsPage-ClUKecun.js +11 -0
  71. package/web-app/dist/assets/TemplatesPage-BWHWVk0L.js +29 -0
  72. package/web-app/dist/assets/{TerminalOutput-togzv9Zl.js → TerminalOutput-Cv3N3Szf.js} +1 -1
  73. package/web-app/dist/assets/{activity-Dreyb_Kr.js → activity-dEhz-bXR.js} +1 -1
  74. package/web-app/dist/assets/{bell-DWH0drES.js → bell-B3XUMq1t.js} +1 -1
  75. package/web-app/dist/assets/{bot-78MoCuDl.js → bot-X-ssNlwR.js} +1 -1
  76. package/web-app/dist/assets/{check-ikfylPst.js → check-C_9Nx7kb.js} +1 -1
  77. package/web-app/dist/assets/{chevron-left-DXUqkd8J.js → chevron-left-tvNswo3A.js} +1 -1
  78. package/web-app/dist/assets/{circle-alert-Da74VO8P.js → circle-alert-CSB88nEs.js} +1 -1
  79. package/web-app/dist/assets/{clock-mlGCFq8l.js → clock-C8p2e4NI.js} +1 -1
  80. package/web-app/dist/assets/{cloud-B_Y5ZrIO.js → cloud-CKp2ZvhG.js} +1 -1
  81. package/web-app/dist/assets/{code-xml-Bbom7YWV.js → code-xml-dY70LCHD.js} +1 -1
  82. package/web-app/dist/assets/{database-CLcofiVp.js → database-DObn5fmn.js} +1 -1
  83. package/web-app/dist/assets/{dollar-sign-C5mkjXIM.js → dollar-sign-DSu6n3K3.js} +1 -1
  84. package/web-app/dist/assets/{file-code-corner-CVrkaOQP.js → file-code-corner-DpCnS73z.js} +1 -1
  85. package/web-app/dist/assets/{file-plus-BV9240gX.js → file-plus-D3IPKCf1.js} +1 -1
  86. package/web-app/dist/assets/{globe-aZ0Zso4I.js → globe-C9QXkgH_.js} +1 -1
  87. package/web-app/dist/assets/{hammer-EgOQNukG.js → hammer-D_FtWe1y.js} +1 -1
  88. package/web-app/dist/assets/index-CI54rara.css +1 -0
  89. package/web-app/dist/assets/{index-DvZ0Kk2x.js → index-Cz7q1bvC.js} +2 -2
  90. package/web-app/dist/assets/{layers-BOVKmepE.js → layers-DsW7jnJX.js} +1 -1
  91. package/web-app/dist/assets/{loader-circle-0K8yLgHl.js → loader-circle-B3HspjKR.js} +1 -1
  92. package/web-app/dist/assets/{lock-XzlDMy8M.js → lock-DSI29pct.js} +1 -1
  93. package/web-app/dist/assets/{package-BtXTKusI.js → package-CLNxtA2x.js} +1 -1
  94. package/web-app/dist/assets/{plus-PxeiD8ju.js → plus-DyI4WC4_.js} +1 -1
  95. package/web-app/dist/assets/{refresh-cw-CjqzYczt.js → refresh-cw-D2dv0xA0.js} +1 -1
  96. package/web-app/dist/assets/{rotate-ccw-CY0os4DH.js → rotate-ccw-CBvihA-a.js} +1 -1
  97. package/web-app/dist/assets/scroll-text-DvoOvAl_.js +6 -0
  98. package/web-app/dist/assets/{server-pIIs9vNp.js → server-DenWvSUt.js} +1 -1
  99. package/web-app/dist/assets/{shield-alert-CsDqKXwA.js → shield-alert-40cX_FvG.js} +1 -1
  100. package/web-app/dist/assets/{thumbs-up-CAh3HVzH.js → thumbs-up-BrjZ6QuV.js} +12 -7
  101. package/web-app/dist/assets/trash-2-C6evwkEH.js +16 -0
  102. package/web-app/dist/assets/{trending-down-DrtBd_C9.js → trending-down-CfbZis3O.js} +1 -1
  103. package/web-app/dist/assets/{trending-up-D-DbCHXm.js → trending-up-DWuEK3WC.js} +1 -1
  104. package/web-app/dist/assets/{upload-D4l6DCOt.js → upload-B9DFBcPG.js} +1 -1
  105. package/web-app/dist/assets/{usePolling-D0aoTX80.js → usePolling-BryVC-T1.js} +1 -1
  106. package/web-app/dist/assets/{user-BQf_XJO_.js → user-DMRXwZiw.js} +1 -1
  107. package/web-app/dist/index.html +2 -2
  108. package/web-app/server.py +122 -47
  109. package/web-app/dist/assets/AdminPage-y6_b2Xo4.js +0 -32
  110. package/web-app/dist/assets/Avatar-BFPkXhIJ.js +0 -6
  111. package/web-app/dist/assets/CockpitPage-5TKICCoA.js +0 -47
  112. package/web-app/dist/assets/ComparePage-BSuNsUHy.js +0 -1
  113. package/web-app/dist/assets/EvidenceReceiptPanel-CcaBwEI-.js +0 -1
  114. package/web-app/dist/assets/GitHubIssuesPanel-DJeYgzAa.js +0 -12
  115. package/web-app/dist/assets/HomePage-Bs451LAy.js +0 -28
  116. package/web-app/dist/assets/MetricsPage-DvY1csgz.js +0 -6
  117. package/web-app/dist/assets/ProjectPage-CqHkEZkl.js +0 -257
  118. package/web-app/dist/assets/ProjectsPage-CLvCK1Y8.js +0 -6
  119. package/web-app/dist/assets/SettingsPage-DP7OmdwP.js +0 -10
  120. package/web-app/dist/assets/SystemSettingsPage-P_rlLh0x.js +0 -1
  121. package/web-app/dist/assets/TeamsPage-DxtBDcBC.js +0 -11
  122. package/web-app/dist/assets/TemplatesPage-5e-faH3W.js +0 -29
  123. package/web-app/dist/assets/folder-open-D_6yV78G.js +0 -11
  124. package/web-app/dist/assets/index-BSgmPwC0.css +0 -1
  125. package/web-app/dist/assets/lightbulb-DdASgqOG.js +0 -6
  126. package/web-app/dist/assets/mail-COu670_W.js +0 -6
  127. package/web-app/dist/assets/minus-bQpUt9ff.js +0 -6
  128. package/web-app/dist/assets/save-v8qep5cY.js +0 -6
  129. package/web-app/dist/assets/trash-2-Dt6urTyY.js +0 -6
@@ -144,7 +144,7 @@
144
144
  var rid = esc(p.run_id);
145
145
  var usd = (p.cost_usd !== null && p.cost_usd !== undefined) ? fmtUsd(p.cost_usd) : "";
146
146
  var fc = (p.files_changed !== null && p.files_changed !== undefined) ? (p.files_changed + " files") : "";
147
- var verdict = p.final_verdict ? '<span class="badge ' + badgeClass(p.final_verdict) + '">' + esc(p.final_verdict) + "</span>" : "";
147
+ var verdict = p.final_verdict ? '<span class="badge ' + badgeClass(p.final_verdict) + '" title="Recorded council vote, not a verification result">council ' + esc(p.final_verdict) + "</span>" : "";
148
148
  var ver = p.loki_version ? ("v" + esc(p.loki_version)) : "";
149
149
  var ts = p.generated_at ? esc(p.generated_at) : "";
150
150
  var href = p.has_html ? ("/api/proofs/" + encodeURIComponent(p.run_id) + "/html") : ("/api/proofs/" + encodeURIComponent(p.run_id));
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v9.54.0
5
+ **Version:** v9.54.2
6
6
 
7
7
  ---
8
8
 
@@ -115,11 +115,12 @@ commands and forward-compat with v8.0.0.
115
115
  - Installs the `loki` CLI binary to your PATH (`bin/loki` shim)
116
116
  - Subsequent `loki setup-skill` creates symlinks at `~/.claude/skills/loki-mode`, `~/.codex/skills/loki-mode`
117
117
 
118
- **Anonymous telemetry is OPT-IN and OFF by default.** A default `npm install`
119
- sends nothing, so air-gapped and enterprise installs are safe out of the box. To
120
- opt in to anonymous diagnostics, run `loki telemetry on` or set
121
- `LOKI_TELEMETRY=on`. To make opting in impossible across a fleet, bake an
122
- opt-out into your base image (opt-out always wins):
118
+ **Anonymous telemetry is ON by default for an individual interactive install**
119
+ and auto-off in CI, non-interactive sessions, and when `LOKI_ENTERPRISE=true` or
120
+ `LOKI_AIRGAP=true` is set. It is disclosed once on first use. Turn it off with
121
+ `loki telemetry off`; `loki doctor --airgap` shows its current state. To make
122
+ opting in impossible across a fleet, bake an opt-out into your base image
123
+ (opt-out always wins):
123
124
  ```bash
124
125
  # Hard-disable everywhere (belt and suspenders; opt-out always wins):
125
126
  LOKI_TELEMETRY_DISABLED=true npm install -g loki-mode
@@ -48,23 +48,50 @@ That is the property competitors' dashboard-bound verification cannot have.
48
48
  loki doctor --airgap
49
49
  ```
50
50
 
51
- prints the egress inventory. Today it reports exactly one REQUIRED point:
51
+ `bin/loki` routes the flag straight to the bash audit regardless of route, so
52
+ no `LOKI_LEGACY_BASH=1` prefix is required. It prints the egress inventory. On
53
+ a default install it reports exactly one REQUIRED point:
52
54
 
53
55
  ```
54
56
  REQUIRED model inference -> https://api.anthropic.com
55
- Set ANTHROPIC_BASE_URL to an in-network gateway, or switch to a
56
- local-weights provider.
57
- optional telemetry [off] disable: loki telemetry off (default off)
57
+ optional telemetry [on] -> https://us.i.posthog.com
58
+ optional update check [on] disable: export LOKI_NO_UPDATE_CHECK=1
58
59
  ```
59
60
 
60
- **We cannot run a build with no model at all.** Nobody can. What we can do is
61
- let you point at a model you host: set `ANTHROPIC_BASE_URL` to an in-network
62
- gateway, or run a provider with local weights. The engine abstracts over CLIs
63
- rather than over one vendor's API.
61
+ It judges the provider `loki start` would use (the project's saved
62
+ `loki provider set` choice, else `LOKI_PROVIDER`), and runs on both the default
63
+ route and `LOKI_LEGACY_BASH=1`. `--json` gives the same inventory for scripts.
64
+ It covers the egress the engine configures, and names what it does not audit
65
+ because it depends on the project: package installs (the app runner's npm, pip
66
+ and docker steps for the built app, the dashboard venv from PyPI, and
67
+ quality-gate tools fetched by `npx` when not installed locally; mirror or
68
+ pre-install them, since a failed `npx` fetch fails that gate) and the delegate
69
+ PR (`git push` and `gh pr create` after a successful run when `gh` is
70
+ installed; `LOKI_DELEGATE_PR=0` turns it off).
71
+
72
+ With a non-claude provider selected, `loki start` on the default (bash) route
73
+ and `loki quickstart` never prompt the `claude` CLI, even when one is
74
+ installed: PRD enrichment, done recognition, the council voters, the USAGE.md
75
+ refresh and the quickstart intent check fall back to their deterministic
76
+ paths. The opt-in Bun loop (`LOKI_SDK_LOOP=1`) is not yet held to this.
77
+ `LOKI_ALLOW_CLAUDE_SIDECALLS=1` restores those claude calls, and the audit then lists them as REQUIRED egress, so it cannot read air-gap ready.
64
78
 
65
- Telemetry is off by default and every opt-out wins (`DO_NOT_TRACK=1`,
66
- `LOKI_TELEMETRY=off`, `~/.loki/config`). The adoption instrumentation added in
67
- v8.6.0 requires a second explicit opt-in on top of that -- see
79
+ **We cannot run a build with no model at all.** Nobody can. What we can do is
80
+ let you point at a model you host. Only a local-weights provider clears the
81
+ required line: opencode, cline or aider with an `ollama/` or `lmstudio/` model
82
+ id in that provider's own variable (for example
83
+ `LOKI_OPENCODE_MODEL=ollama/qwen2.5-coder`). An Ollama cloud model (a `:cloud`
84
+ or `-cloud` tag) runs on ollama.com and is still reported as required. The
85
+ verdict comes from the model id; the audit does not read where your Ollama or
86
+ LM Studio endpoint listens, so confirm that yourself. An in-network gateway for
87
+ claude or codex (`ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL`) keeps traffic inside
88
+ your network, but the audit still counts it as required egress.
89
+
90
+ Telemetry is ON by default for an individual interactive install and auto-off
91
+ in CI, non-interactive, `LOKI_ENTERPRISE=true` and `LOKI_AIRGAP=true`
92
+ contexts. The audit shows its real state. Every opt-out wins (`DO_NOT_TRACK=1`,
93
+ `LOKI_TELEMETRY=off`, `loki telemetry off`). The adoption instrumentation added
94
+ in v8.6.0 requires a second explicit opt-in on top of that -- see
68
95
  [PRIVACY.md](./PRIVACY.md).
69
96
 
70
97
  ## Why `unknown` is the right answer offline
@@ -334,11 +334,11 @@ All Loki Mode data is stored locally on the machine running the system:
334
334
  | Policy files | `.loki/policies.yaml` or `.loki/policies.json` |
335
335
  | Configuration | `.loki/config.yaml` or `.loki/config.json` |
336
336
 
337
- No data is transmitted to external services unless explicitly configured (OTEL endpoint, integration webhooks, syslog forwarding).
337
+ Beyond model inference (required unless you run a local-weights provider), anonymous telemetry (on by default for individual interactive installs, auto-off in CI, non-interactive and `LOKI_ENTERPRISE=true` / `LOKI_AIRGAP=true` contexts) and a daily update check, no data is transmitted to external services unless explicitly configured (OTEL endpoint, integration webhooks, syslog forwarding). `loki doctor --airgap` prints the egress inventory for the current configuration.
338
338
 
339
339
  ### Provider Restrictions
340
340
 
341
- The data residency controller restricts which AI providers can be used based on region:
341
+ The data residency controller (`src/audit/residency.js`, configured in `.loki/residency.json`) answers whether a provider is allowed by a region or air-gapped policy. It is a library check: `loki start` does not consult it today, so setting `air_gapped: true` does not by itself stop a run from calling a hosted provider. Use `loki doctor --airgap` and network policy to enforce that.
342
342
 
343
343
  ```javascript
344
344
  const audit = require('./src/audit');
@@ -351,17 +351,14 @@ const allowed = audit.checkProvider('anthropic', 'us'); // true/false
351
351
  const airGapped = audit.isAirGapped();
352
352
  ```
353
353
 
354
- Configure via `.loki/config.yaml`:
355
-
356
- ```yaml
357
- residency:
358
- allowed_providers:
359
- - anthropic
360
- - openai
361
- allowed_regions:
362
- - us
363
- - eu
364
- air_gapped: false
354
+ Configure via `.loki/residency.json` (the file `residency.js` reads):
355
+
356
+ ```json
357
+ {
358
+ "allowed_providers": ["anthropic", "openai"],
359
+ "allowed_regions": ["us", "eu"],
360
+ "air_gapped": false
361
+ }
365
362
  ```
366
363
 
367
364
  ## Policy Engine Security
@@ -42,6 +42,7 @@ interact and why hitting one is a failure rather than a success.
42
42
  | `LOKI_MAX_TIER` | unlimited | Caps model tier, so a run cannot escalate past what you are willing to pay for. |
43
43
  | `LOKI_MODEL_OVERRIDE` | unset | Overrides the resolved model outright. |
44
44
  | `LOKI_TIER` | `oss` | **Not a model setting.** The open-core licensing seam. Leave it unset. |
45
+ | `LOKI_ALLOW_CLAUDE_SIDECALLS` | unset | `1` (exactly) lets PRD enrichment, done recognition, the council voters, the USAGE.md refresh and the quickstart intent check prompt the `claude` CLI under a non-claude provider; `loki doctor --airgap` then reports that as required egress to Anthropic. |
45
46
 
46
47
  ### Picking a model without naming one
47
48
 
@@ -75,8 +75,8 @@ codes on the Bun route and the bash route (`LOKI_LEGACY_BASH=1`).
75
75
  | Code | Meaning |
76
76
  |---|---|
77
77
  | 0 | Clean: the integrity hash matches and the recorded diff still matches the repo (and, with `--jwks`, the attestation is VERIFIED) |
78
- | 1 | Tampered or drifted; or, with `--jwks`, the attestation FAILED, or is ABSENT (the receipt is unsigned while a key set was supplied) |
79
- | 2 | Could not check: the receipt is present but unusable (malformed JSON), the verifier itself is missing, or, with `--jwks`, the attestation is NOT CHECKED (key set unreadable, or a verifier dependency missing) |
78
+ | 1 | Tampered, or genuinely drifted (the working tree really changed since the recorded base); or, with `--jwks`, the attestation FAILED, or is ABSENT (the receipt is unsigned while a key set was supplied) |
79
+ | 2 | Could not check: the receipt is present but unusable (malformed JSON), the verifier itself is missing, drift could not be re-derived (verified outside a git tree, or the recorded base ref no longer resolves, with every other check passing) rather than genuinely found, or, with `--jwks`, the attestation is NOT CHECKED (key set unreadable, empty, or a verifier dependency missing) |
80
80
  | 64 | Usage error: no proof id, more than one proof id, an unknown option (a mistyped `--jwk` must never skip the check), `--jwks` with no value or an empty one, or `-h`/`--help` (verify never exits 0 without a verdict; full help is `loki proof help`). `--` ends options, so `loki proof verify -- <id>` checks an id that begins with `-` |
81
81
  | 66 | Input missing: no `.loki/proofs/<id>/proof.json` for that id |
82
82
 
@@ -94,6 +94,80 @@ purpose: it rolls up many stages, and an operator who fixes the named FAILED
94
94
  stage and re-runs would see green while still blind on the stage that never
95
95
  ran (`tools/verify-chain.py`, rule 4).
96
96
 
97
+ ## `loki start --remote` receipt check (not a verify command)
98
+
99
+ This is a documented DIVERGENCE from `proof verify`, not a bug. `loki start
100
+ --remote <url>` submits a build to a deployed cluster (`loki_remote_submit`,
101
+ `autonomy/loki`), and after the job finishes it fetches the Evidence Receipt
102
+ and runs an internal check (`loki_remote_verify_receipt`) that prints one of
103
+ several verdict words also used elsewhere in `loki proof`: VERIFIED, UNSIGNED,
104
+ NOT CHECKED, TAMPERED, plus its own NOT VERIFIED when the verifier itself is
105
+ unavailable (a fifth, distinct string -- see the last row below). Despite the
106
+ shared vocabulary, this is not the `proof verify` contract and does not return
107
+ its codes.
108
+
109
+ `loki_remote_verify_receipt` is an internal helper, never a standalone
110
+ command. Its return value feeds `_receipt_rc` inside `loki_remote_submit` via
111
+ `loki_remote_fetch_receipt` (`autonomy/loki`), whose own last statement is the
112
+ verify call with no `||` to reset the status -- so the helper's return code
113
+ propagates out unchanged. `loki_remote_submit` then checks `_receipt_rc`
114
+ BEFORE ever looking at job status:
115
+
116
+ | Receipt verdict | Helper return | Process exit |
117
+ |---|---|---|
118
+ | VERIFIED (gpg or attestation) | 0 | Governed by the job status below |
119
+ | UNSIGNED | 0 | Governed by the job status below |
120
+ | NOT CHECKED -- gpg unavailable on this machine, or an attestation present but unreachable/uncheckable (JWKS unreachable, crypto missing) | 0 | Governed by the job status below |
121
+ | NOT CHECKED -- signed, but the signing key is not in the verifier's keyring (gpg `nopubkey`), and no valid attestation covers it | 1 | 1, unconditionally |
122
+ | NOT VERIFIED -- the local verifier itself is unavailable (no python3, or `lib/proof-verify.py` missing) | 0 | Governed by the job status below |
123
+ | TAMPERED (bad hash, bad gpg signature, bad attestation) | 1 | 1, unconditionally |
124
+
125
+ The exit code `loki start --remote` actually returns is gated on the job's own
126
+ terminal status (`passed` / `failed` / `unknown`) in most cases, but there are
127
+ TWO deliberate overrides that fail the whole submit regardless of job status,
128
+ not one:
129
+
130
+ - **TAMPERED** (bad hash, bad gpg signature, bad attestation): an accusation
131
+ that the receipt itself was altered.
132
+ - **NOT CHECKED / nopubkey** (signed, but the verifying machine does not hold
133
+ the signing public key, and no attestation proves it another way): per the
134
+ code's own comment at this branch (`autonomy/loki:1081-1084`), this is
135
+ "Non-zero: a caller who asked for a signed receipt did not get proof, and in
136
+ CI that must not pass silently." It is explicitly NOT an accusation of
137
+ tampering -- the message says plainly that nothing is wrong with the
138
+ contents -- but it still fails the submit, because a signed remote build
139
+ whose signature could never be checked is exactly the case this override
140
+ exists to catch. Concretely: a build can be genuinely signed, actually pass,
141
+ and still make `loki start --remote` exit 1, if the verifying machine simply
142
+ never imported the publisher's key and no JWKS attestation is available to
143
+ cover the gap.
144
+
145
+ Every other outcome -- VERIFIED by either gpg or attestation, UNSIGNED, the
146
+ gpg-unavailable/attestation-uncheckable NOT CHECKED case, and the
147
+ verifier-unavailable NOT VERIFIED case -- returns 0 from the helper and
148
+ defers entirely to job status, because none of those contradicts the job's
149
+ reported outcome.
150
+
151
+ This is why remote NOT CHECKED can exit 0 in some branches and 1 in others,
152
+ while `loki proof verify`'s NOT CHECKED always exits 2: they answer different
153
+ questions. `proof verify` IS the verdict -- its only output is "was this
154
+ receipt proven", so an unproven result can never exit 0. The remote helper's
155
+ verdict is a secondary integrity check bolted onto a job-status command whose
156
+ primary question is "did the build pass"; most unprovable outcomes do not
157
+ change that answer, but the nopubkey case is carved out by the code itself as
158
+ a hard failure, and TAMPERED always is. (`_deploy_receipt_verdict`'s own
159
+ header comment, elsewhere in `autonomy/loki`, makes the same point about this
160
+ helper for the UNSIGNED case specifically -- "that function returns 0 for
161
+ UNSIGNED, which is right for its caller (submit) and wrong here (integrity is
162
+ not provenance)" -- it does not speak to NOT CHECKED, whose nopubkey behavior
163
+ is documented at its own branch as cited above.)
164
+
165
+ To get the `proof verify` exit-code contract (0/1/2/64/66) against a receipt
166
+ fetched from a remote build, run `loki proof verify <job-id>` afterward
167
+ against the written `.loki/proofs/<job-id>/proof.json` -- that is a genuine
168
+ verify call and returns the codes documented above, not the ones in this
169
+ section.
170
+
97
171
  ## `loki proof chain [workspace]`
98
172
 
99
173
  Runs the whole verification chain (`tools/verify-chain.py`) and passes its exit
@@ -108,8 +108,10 @@ cosign initialize --mirror <your-tuf-mirror> --root <your-root.json>
108
108
  ```
109
109
 
110
110
  Then transfer `loki-image-bundle/` and verify with `cosign verify --local-image
111
- ./loki-image-bundle`. See `loki doctor --airgap` for the full host inventory
112
- the engine itself needs at runtime.
111
+ ./loki-image-bundle`. See `LOKI_LEGACY_BASH=1 loki doctor --airgap` for the
112
+ full host inventory the engine itself needs at runtime (the default Bun route
113
+ does not yet accept the `--airgap` flag, tracked as
114
+ `P5.airgap-audit-default-route`).
113
115
 
114
116
  ## If verification fails
115
117
 
@@ -47,10 +47,10 @@ Status values: todo, in progress, shipped (version), parked (reason).
47
47
 
48
48
  25. **`loki ci --report` crashes on large diffs** with "Argument list too long" (`autonomy/loki:33811` passes the diff to python3 as argv; exit 126). Seen on PR #216 and a 2026-08-30 PR via the "Loki CI Quality Gate" workflow, which runs the published package. Pass the diff on stdin or a file.
49
49
  26. **Bun Parity `doctor-json` flake:** `disk.available_gb` read 94 vs 95 between the two routes on macOS (PR #216). Normalize or drop the value in the parity comparison.
50
- 27. **Dashboard routes vanish on newer FastAPI:** a clean `requirements-test.txt` install resolves FastAPI 0.141.1 / Starlette 1.7.0, where `dashboard.server.app.routes` has 191 routes and zero `/api/v2` paths, against 218 and 24 on FastAPI 0.128.0 (slice Z measurement). Users installing today may get a dashboard missing its v2 API. Pin or fix.
50
+ 27. **(Resolved in cycle 4: a measurement bug, not a product bug. FastAPI 0.141 keeps included routers as lazy entries in `app.routes`; the routes were serving all along, `app.openapi()` is identical on both versions.) Dashboard routes vanish on newer FastAPI:** a clean `requirements-test.txt` install resolves FastAPI 0.141.1 / Starlette 1.7.0, where `dashboard.server.app.routes` has 191 routes and zero `/api/v2` paths, against 218 and 24 on FastAPI 0.128.0 (slice Z measurement). Users installing today may get a dashboard missing its v2 API. Pin or fix.
51
51
 
52
52
  28. **Moat: pending-failure fingerprints.** A pending case that starts failing for a different reason (for example its own positive control broke) stays green. Give each pending entry an expected failure reason and fail on a mismatch (council round 2).
53
- 29. **P7 route count depends on the host** (9 missing on macOS, 21 on Linux CI) because optional routers do not mount there; tied to item 27.
53
+ 29. **(Resolved in cycle 4: the P7 matcher asks the app router directly and fails closed per router.) P7 route count depends on the host** (9 missing on macOS, 21 on Linux CI) because optional routers do not mount there; tied to item 27.
54
54
  30. **P4 results need provenance.** Once the corpus exists, a hand-written per-defect outcomes file could still pass; require a verifiable receipt per outcome.
55
55
  31. **`proof-verify.py` reads "drift unverifiable" (not a git tree, no base_sha) as 1 (tampered)**; it should be 2 (could not check). (The Bun timeout part is fixed: exits above 128 map to 2.)
56
56
  32. **Docs tell npm users to run `doctor --airgap`** (`docs/air-gapped.md:48`, `deploy/helm/README.md:413`), which the default route rejects. Note `LOKI_LEGACY_BASH=1` until `P5.airgap-audit-default-route` is fixed.
@@ -131,6 +131,68 @@ Status values: todo, in progress, shipped (version), parked (reason).
131
131
  91. **Zero-test stage events:** a zero-test run still returns 0 from `enforce_test_coverage`, so the loop emits `stage_complete test_suite pass` (~24590) although the result is inconclusive.
132
132
  92. **Receipt path bases mix under a subdirectory `TARGET_DIR`:** tracked entries are repo-top-relative, untracked and `preexisting_modified` entries are cwd-relative.
133
133
 
134
+ 93. **`test-hard-deadline-confinement.sh` "zero-exit leader" case is load-sensitive:** the leader waits only 0.2s for the forked child to write its PID file, so under CI load the child PID is empty and the case fails although the product behaved correctly (rc 125, no mutation). Seen once, shard 2 at `1dd799f9` (v9.54.0 release run), passed 3/3 locally and on rerun. Wait for the PID file with a bounded, generous timeout.
135
+
136
+ 94. **Scope-audit tests walk `app.routes`** (`test_all_data_gets_scoped`, `test_fleet_observability`, `test_fleet_retry`, `test_memory_read_scope_auth`), so on FastAPI >= 0.141 they never see a v2 or operator route: their v2 coverage is vacuous on CI. Use the same router-matching approach as the P7 case (cycle 4, slice D).
137
+ 95. **(Fixed: cycle 4 nulled the dashboard fleet runs/summary and project totals; the P7 sweep nulled the web-app `/api/session/status` cost and its status push through one shared `_measured_state_cost` reader.) Servers still seed unmeasured cost as 0:** `web-app/server.py` `/api/session/status` seeds `cost_usd = 0.0` (~2932) and `dashboard/registry.py` does the same, so the UI can only treat values above 0 as readings. Send null when nothing was recorded.
138
+ 96. **(Fixed in cycle 4: gauge and health grade removed, BuildInsights nulls, scanner rules 4 and 5.) More fabrications in `ProjectWorkspace.tsx`:** `testCoverage={buildStatus.iteration > 2 ? 60 : 20}`, `gatePassRate={0.8}` (~1559) and hardcoded zeros for `filesModified`/`linesGenerated` passed to BuildInsights. The P7 sample-data scanner misses assignment-form fallbacks and unconditional fakes; widen it.
139
+ 97. **(Fixed in the P7 sweep: `run_check` call site, `skip_check` and `_FAST_KEEP` entry "webapp admin console renders honestly" in `scripts/local-ci.sh`, and a `run_test` line in `tests/run-all-tests.sh`; the harness now also drives the Teams page.) Register `scripts/run-webapp-admin-honesty.sh`** in `scripts/local-ci.sh` (~1850) and `tests/run-all-tests.sh` (~295) next to its sibling harnesses.
140
+ 98. **Headline vs gate listing:** the stale-results fix (82) changes the unit_tests gate listing only; the headline comes from `facts.tests`, which should get the same freshness rule. The council member vote and `_council_convergence_evidence_green` still read `pass: true` as green whatever `failed_count` says, and the Bun npm fallback still reads an exit-0 run printing "Tests: 1 failed" as passed.
141
+
142
+ 99. **The commit-hygiene prompt line does not reach the main build loop for codex, cline or aider:** the bash loop calls `codex exec ... "$prompt"` directly and uses run.sh's own `invoke_cline`/`invoke_aider`; the Bun providers push the raw prompt. Only opencode gets it. The untrack commit is the real guarantee; the cycle-4 commit message overstated the wiring.
143
+ 100. **Interrupted self-commit loses user files:** the untrack step runs only at the normal end of a session (`commit_session_changes`); an agent self-commit followed by an interrupt and a base checkout still deletes the user's files. Run the untrack check in the interrupt path and on resume.
144
+ 101. **Nested repo gitlink stays on the tip:** an untracked `vendor/lib/` with its own `.git` that the agent's `git add -A` records as a gitlink is not matched (`vendor/lib` vs snapshot entry `vendor/lib/`); nothing is lost (checkout keeps the directory).
145
+ 102. **`.loki/state/agent-committed-user-files.z` has no reader** outside the lifecycle test and is never cleared by a later session with no hits.
146
+ 103. **Bash failure-count gaps:** jest "Test Suites: 1 failed" with "Tests: 2 passed" (a suite that failed to compile), and vitest "Test Files 1 failed", still read as passed (`run.sh` ~12697-12707).
147
+ 104. **Pre-existing failing node test:** `dashboard-ui/tests/loki-overview-issue-journey.node.test.mjs` fails 5/5 at v9.54.0 and before; not registered in any runner.
148
+
149
+ 105. **User-committed files on the session branch are misattributed (council S4c):** a file the user deliberately commits on the session branch between sessions is taken off the tip by the next session's untrack step and reported as "the agent committed". It stays on disk. Needs a session-end marker written at every clean session end to tell user commits from an interrupted agent's.
150
+ 106. **Unmeasured spend in the CLI and the budget breaker:** `loki cost` (autonomy/loki) reports `budget_used = 0.0` when unmeasured (ok/0%); `check_budget_limit` writes `0.0` to `budget.json` for a run it measured nothing for; the bash and Bun breakers treat unmeasured cost as $0 and never warn. Proposed default: warn (never pause) when a cap is set and spend is unmeasured, and show unknown in the CLI.
151
+ 107. **(Mostly fixed in the P7 sweep: rule 4 now matches the whole file, so a multi-line ternary is one prop, and `tests/e2e/webapp-admin-honesty.mjs` renders the MetricsPage budget tile against a $10 cap with no spend and asserts "Not recorded". Open: rule 4 still only knows its listed metric names.) Test coverage for two honesty surfaces:** the web-app MetricsPage "Not recorded" budget label has no committed rendering test, and P7 rule 4 (hardcoded metric props) scans one line at a time (misses multi-line ternaries and metric names outside its list).
152
+
153
+ 108. **High (secret exposure, opt-in path): `LOKI_AUTO_PR=1` pushes a branch whose history still holds user files.** After the untrack step warns "do not push <branch> as-is", `create_session_pr` prints push advice and, under `LOKI_AUTO_PR=1`, pushes the branch with the user's files (for example `.env.local`) still in history. Read `.loki/state/agent-committed-user-files.z` in `create_session_pr`; when non-empty, print the `git reset --soft <fork> && git commit` cleanup first and refuse the auto-push (also gives item 102 its reader). Predates cycle 4.
154
+ 109. **The untrack step's global `git reset -q` drops force-staged ignored agent files** (`git add -f dist/bundle.js`) from the session commit with no warning (file stays on disk). Build the untrack commit on a temporary `GIT_INDEX_FILE` and force-remove only those paths from the real index.
155
+ 110. **A resumed session minted by an older Loki (no `session-start-sha`) whose recorded base does not resolve fails closed on every resume**, even when the agent committed nothing, and the warning tells the user to run `git rm --cached` with nothing swept. When no fork resolves, intersect HEAD's tree with the snapshot's exact entries on disk and proceed if empty.
156
+ 111. **pytest "1 passed, 1 error" parses as failed_count 0 and passes** (only "N failed" is counted in the bd39d359 summary parser).
157
+
158
+ 112. **(Partly fixed in the P7 sweep: web-app `/api/session/status` and its status push, dashboard `/api/context` with no `tracking.json`, and the timeline per-iteration token counts and model now send null; `current_run` and the budget snapshot carry a `partial` flag; pinned in P7.unmeasured-cost-never-zero's server and web-app legs, except the WebSocket status push, which shares `_measured_state_cost` and `_max_iterations_or_none` with the pinned GET but whose own payload no test drives. Open: `/metrics` `loki_cost_usd`, the `/api/cost` tracker fallback, the per-run partial flag, and the R3 design doc.) Remaining server paths that send 0 for unmeasured cost:** `web-app/server.py` `/api/session/status` (~2932, ~2956) and its status push (~6292, ~6315); dashboard `/api/context` with no `tracking.json` (~8804, ~8810); `/metrics` `loki_cost_usd` (~10048, sums with no "measured" check; Prometheus needs its own decision); the `/api/cost` tracker fallback when tokens were seen but no cost (~7914, ~7919); timeline per-iteration token counts; a fleet run with mixed priced and unpriced records has no per-run partial flag. `docs/R3-COST-OBSERVABILITY-DESIGN.md` still describes `project_total_usd` as a plain sum.
159
+ 113. **(Fixed in cycle 4, `3a139d81`: `get_proof` adds a computed `integrity_check`; the receipt panel and audit viewer render only computed verdicts; new case P2.console-verdict-needs-computed-result.)** **Critical (moat P1/P2, not P7): two panels state a verification verdict nothing computed.** Found by the P7 sweep; a different property, so not fixed there. `web-app/src/components/EvidenceReceiptPanel.tsx` `classify()` (~43, ~50, ~70-76) labels a receipt "Proven: internally consistent with its own integrity hash" although the browser never recomputes the hash (`dashboard/server.py` `get_proof` returns the raw proof.json with no verification result), labels a receipt with NO hash the same way (`!v.hash`), and can never return "tampered". `dashboard-ui/components/loki-audit-viewer.js` prints "[VALID] ... verified" for `valid !== false`, so `dashboard/audit.py` returning `valid: True, files_checked: 0` reads as verified, and its catch (~169) turns a failed request into "[TAMPERED]". Fix: a server verify endpoint (or the `loki proof verify` result) behind the receipt label; require `valid === true && files_checked > 0`; a failed request is NOT VERIFIED, never TAMPERED.
160
+ 114. **False empty on a failed fetch (class, P7-adjacent).** A request that fails renders the same "nothing here" copy as a real empty result. Sites: `web-app/src/components/NLSearch.tsx` (~140-143, "No results found"), `CommandPalette.tsx` (~171-173), `pages/ProjectsPage.tsx` (~65, ~166-168 ignore the poll hook's error), `ProjectWorkspace.tsx` SecretsPanel (~248-256, ~366-372) and DocsPanel (~431-442, ~589-592), the cockpit (`cockpit/useCockpitState.ts` ~119-123 swallows git status and checkpoint failures, so `ChangeReview.tsx` says "The working tree is clean", `FinalActions.tsx` "Nothing to push", `RiskPanel.tsx` "No checkpoints were recorded"), `DeployConnections.tsx` (~353-370), `CICDPanel.tsx` (unknown conclusions shown as "Failed"), `AIChatPanel.tsx` (~543, ~622 "Done." with no output); dashboard `loki-checkpoint-viewer.js` (~95-106), `loki-task-board.js` (~164-169 hides the error when local tasks exist), `loki-log-stream.js` (~152-181), `loki-council-transcripts.js` (~111-114), `loki-managed-memory-panel.js` (~138-141 ignores `data.error`), `loki-migration-dashboard.js` (~94-98), `loki-api-keys.js` (~557-558). Fix per site: keep the error and render "Could not load ..."; a scanner rule for `.catch(() => [])` followed by an empty-state string would pin the class.
161
+ 115. **Status inference presents a guess as a state.** `web-app/server.py` `_infer_session_status` (~3603-3666) returns "completed" for a failed or paused run once its state file is five minutes old, and for any directory with a top-level source file; the cockpit then shows "Run finished" with every step ticked. `cockpit/useCockpitState.ts` (~209-218) also falls back to "completed" for unknown statuses, and (~118) shows `/api/session/checklist`, which reads the globally running project, as a historical session's gates. `ProjectWorkspace.tsx` (~1403-1412) labels REASON/REFLECT and unknown phases "building"; `PhaseVisualizer.tsx` styles earlier RARV steps as done. Fix: an explicit "unknown"/"stale" status and a per-session checklist (or none).
162
+ 116. **Key misreads that hide or zero real data.** `web-app/src/components/GitHubPRsPanel.tsx` reads `statusCheckRollup.contexts` (~204, ~713) although gh returns an array, so failing CI shows "No status checks"; `reviewDecision || "PENDING"` (~222); uppercase gh `state` never equals `'open'` (~751), which also hides the Close button that only posts a comment yet says "PR #N closed" (~592-606); `GitHubIssuesPanel.tsx`/`GitHubPRsPanel.tsx` comment counts read a number where gh sends an array. `EvidenceReceiptPanel.tsx` (~211, ~219) reads `detail.cost_usd`/`detail.files_changed` where proof.json stores `cost.usd` and `files_changed.count`, so a measured cost reads "unknown". `loki-memory-browser.js` episodes read camelCase keys (`actionLog`, `taskId`, `phase`) where the store writes snake_case, so episode details never open and every episode is titled "Task"; `/api/memory/stats` sends `episode_count`, the panel reads `episodes_count`. `loki-overview.js` `_renderCouncilGateCard` (~394) keys on `g.status`, which `/api/council/gate` never sends (it sends `blocked`), so a blocked gate reads "Not evaluated".
163
+ 117. **Stale or false static copy.** `WhatsNew.tsx` `CURRENT_VERSION = '6.73.1'` and `Footer.tsx` `v6.73.0` (current 9.x); `SettingsPage.tsx` says License "MIT" (the LICENSE is BUSL-1.1) and a hardcoded build date; `ComparePage.tsx` lists Gemini as supported (runtime removed v7.5.18) and "9 automated quality gates", `BenefitCards.tsx` "10 quality gates" and Gemini (the canonical count is 8); `ProductTour.tsx`, `DocsSidebar.tsx` promise Railway deploys (only Vercel, Netlify, GitHub Pages exist) and a coverage gate; `ContextualHelp.tsx`, `SettingsPage.tsx` and `CostEstimator.tsx` still list Gemini; `Roadmap.tsx` hardcodes Q2 2026 as "Current"; `ChangelogWidget.tsx` shows March 2026 tags as "Recent Changes"; `TrustedBy.tsx` asserts "Trusted by developers building the future". The P7 scanner cannot see module-level copy tables (the deleted testimonials were one); a narrow rule for invented person/company rows would close that gap.
164
+ 118. **Fabrications the P7 sweep found and did not fix (outside its enumerated shapes or needing a design).** Per-run `cost.cost_partial` is dropped by `/api/cost/timeline` `runs[]` and `/api/proofs`, so a partly priced run shows as complete (`dashboard/static/cost.html` ~287, ~301, `proofs.html` ~145, the dashboard receipts list); `workspace_diff.py` returns count 0 when the folder is not a git repo (feeds the receipt hash, so needs care); `trust.html` says "Stable" when no axis has two data points, and `trust_trajectory.py` maps any non-PASS verdict to 0.0; `/cost` prices a record with tokens but no `cost_usd` at Sonnet rates when the model is unknown, with no "estimate" label; `loki metrics --json` (`autonomy/loki` cmd_metrics) sends `tokens.total` 0 and `agent_activity.total_iterations` 0 when nothing was recorded; server-side zero or "not blocked" sentinels the panels now render as unknown only where the client can tell: the council-state fallback `total_votes: 0` (`dashboard/server.py` ~8617), a corrupt `notifications/active.json` answered with a zero summary (~8883), a waivers read error answered 200 (~10321), a corrupt app-runner state answered `{"status":"error"}` (~11240), `prompt_optimizer.py` (~73-84) never-ran sentinel zeros, `migration_engine.py` (~770-793) 0/0 progress; the dashboard `StatusResponse` model (~594-607) defaults iteration/pending to 0 and provider/complexity to "claude"/"standard", and the skill-session WS fallback hardcodes `running_agents: 0` (~1042); `loki-analytics.js` counts days outside the returned log window as 0 activities; `CostEstimator.tsx` prices the whole iteration cap as the estimate once a real cap (e.g. 1000) is known; the web-app "Replay Build" button can never appear (`buildPhase` is "idle" whenever no build runs) and the StatusBar "Built in" line no longer receives a value; the dashboard shell has two elements with `id="budget-banner"` (`build-standalone.js` ~1200, ~1801), reverses a newest-first receipts list under a "Newest first" label (~2510), and colours "VERIFIED WITH GAPS" as success (`/^VERIFIED/`).
165
+ 119. **(Fixed in cycle 4, `3a139d81`: `validate_token` rejects a key past `rotation_expires_at`; `cleanup_expired_rotating_keys` still has no caller.)** **High (security, found by the P7 sweep): a rotating API key past its grace period still authenticates.** `dashboard/auth.py` `validate_token` (~346-390) checks `revoked` and `expires_at` but never `rotation_expires_at`, and `dashboard/api_keys.py` `cleanup_expired_rotating_keys` (~291) has no callers. The key panel now derives "rotating" from the timestamps, which still understates it. Fix: reject in `validate_token` once `rotation_expires_at` has passed, with a test.
166
+ 120. **Two dashboard-ui test files do not test the shipped code.** `dashboard-ui/tests/ui-components.test.js` asserts hand-copied versions of `formatGateTime`, `summarizeGates` and `formatRunDuration` that still encode the old "Never" / "pending" / growing-duration behaviour, and jest is not installed, so nothing runs it. `dashboard-ui/tests/loki-overview-issue-journey.node.test.mjs` fails 0/5 at 61af5915 and after this change (the disconnected view hides the journey since 1a871725 and the test never sets `_data.connected`); it is registered in no runner. Import the real functions or delete the copies; fix and register the journey test.
167
+ 121. **Browser-only settings still read like live config.** `web-app/src/pages/SettingsPage.tsx` stores provider API keys in plain text in localStorage, and "Budget limit" and "Auto-deploy on success" describe effects nothing implements; each category now carries a "saved in this browser only" note, but removing or wiring them is a product decision. `DeployConnections.tsx` still starts from `{connected:false}` and, if its own fetch fails, pushes those defaults up to `DeployPanel` on the next connect or disconnect.
168
+
169
+ 122. **Receipt field `first_result_verified_patch` is `True` for any first code change** (`autonomy/lib/proof-generator.py:1164`, from `first-artifact.json`), so the name claims a verification nothing ran. The overview now labels it "first code change"; rename the field additively (`first_result_kind` already carries the kind) or set it only from a verified result.
170
+ 123. **More verdict-class leftovers from the 113 sweep:** the `audit.py verify` CLI and `compute_chain_tip_in_dir` still report valid, exit 0, when nothing was checked (the cross-chain verifier reads them, so changing them needs its own review); the cockpit, overview, checklist, gate-card, standalone, proofs.html and cost.html verdict relabels have no test of their own (only the receipt and audit surfaces are pinned by the moat case); the standalone receipt list shows the oldest receipts first (see 118).
171
+ 124. **Dashboard innerHTML audit:** the standalone receipt and learning panels interpolated run-written text into innerHTML unescaped (fixed in cycle 4, `f6050ef1`, with hostile-input tests). No systematic audit of the other `innerHTML` sites in `dashboard-ui/components` and `build-standalone.js` has been done; add a guard that flags a template literal or string concatenation of a response field into innerHTML without an escape.
172
+
173
+ 125. **P7 scanner (`tests/moat/p7-no-fabricated-data.sh`) bypass shapes found by PF-2 round 5's review but not fixed there (pre-existing before and after `fa5de15f`, not a regression -- each is a candidate for its own small slice):**
174
+ - **B-1 reassignment fallback:** `let rows = data; if (!rows.length) rows = [rows]; setRows(rows);` -- `DECL_ARR` only matches the `const/let/var x = [` declaration, never a later bare `x = [` assignment.
175
+ - **B-2 destructuring / parameter defaults:** `const { rows = [rows] } = data;` or `function load(rows = [rows]) {}` -- neither is a declaration of the form `name = [`.
176
+ - **B-3 object-property value on `this.x` outside a setter:** `this._state = { phases: [rows] };` -- `THIS_ARR` needs `this.x = [` directly, misses a literal nested inside an object RHS.
177
+ - **B-4 paren-wrapped `||`/`??` outside a setter:** `this._rows = d.rows || ([rows]);` -- `FALLBACK_ARR` requires `[` immediately after the operator; a wrapping `(` defeats it (the setter-argument span arm already catches this shape inside a setter call).
178
+ - **B-5 module-level named fallback table:** `const FALLBACK_ROWS = [rows];` at column 0, used later as `data.length ? data : FALLBACK_ROWS` -- the column-0 exemption plus the identifier-branch ternary both let it through; rule 7's name-based catch only fires if the constant name contains sample/mock/demo/fake/placeholder.
179
+ - **B-6 ternary against `null`/`undefined`:** `const r = loading ? null : [rows];` -- `is_literal(null)` is true, so the both-branches-literal exemption suppresses it; should only treat an array/object literal (never null/undefined/empty-string) as the "literal other branch."
180
+ - **B-7 fabricated data returned from a helper:** `function getRows(d) { if (!d) return [rows]; return d; }` then `setRows(getRows(d))` -- no arm follows a function's return value.
181
+ - **B-8 `Array.from` generator producing invented rows:** `Array.from({ length: 3 }, (_, i) => ({ id: i, action: 'Deployed', user: 'Admin' }))` -- the `Array.from` arm only checks a literal first argument, not a generator callback whose body invents field values.
182
+ - Two more from the same review, not yet given IDs: a positive-control risk (a real typed options list chosen against live data, e.g. `isAdmin ? adminOpts : [{value:'view',...}]`, could false-positive under the ternary/setter-span arms -- fix with an allow-annotation if it ever fires on real code, don't weaken the arm); and re-running `tests/moat/p6-*.sh` in isolation to confirm its 300s timeout during the PF-2 review was purely a load artifact (load average ~30 from concurrent session agents), not a real regression the merge introduced.
183
+
184
+ 126. **(CORRECTED and CONFIRMED as a real CI-breaking bug, not just a local artifact -- see the retraction below.) `tests/test-airgap-ollama-host.sh` does not isolate `.loki/state/`, so a stale `.loki/state/provider` file written by an EARLIER test IN THE SAME CI SHARD's shared checkout silently overrides the test's own `LOKI_PROVIDER=opencode` env var** (the CLI's own precedence rule: saved project state wins over the env var, by design). This actually broke `Tests` (shard 3/4) in CI on `ba6610dc` -- root-caused by reproducing the exact CI shard-3 sequence locally: `tests/test-iteration-grace.sh` sources `autonomy/run.sh` from the shared repo checkout's CWD (not its own isolated `$SCRATCH` dir) at two call sites (`probe()`, ~line 42 and ~109), and `autonomy/run.sh:1741-1744`'s provider auto-detection writes `.loki/state/provider` as an unconditional side effect of sourcing. Every later test in the same shard's shared checkout inherits that leftover file. Removing it reproduces the airgap test passing again; the CI failure is not a local-checkout coincidence. **Retraction**: an earlier draft of this item concluded the opposite (called it "not a product bug", attributed it purely to local manual-testing residue, and proposed fixing only `test-airgap-ollama-host.sh`'s own isolation) -- that conclusion was WRONG. It was drawn from a local reproduction where I had ALSO independently left a stray `.loki/state/provider` in my own manual testing, which coincidentally matched the CI symptom and stopped the investigation short of finding the real cross-test contamination source. Lesson: a reproduced symptom is not the same as a verified mechanism -- confirm the EXACT trigger, not just that removing the artifact makes the symptom go away. Two fixes landed and merged: `test-iteration-grace.sh` (`b3d7cdda`) and `test-exit-code-contract.sh` (`e05e09fb`), both isolating CWD before sourcing `run.sh`. Verifying the shard is now clean surfaced a THIRD contaminator: `tests/test-code-review-self-copy.sh` sources a self-copy of `run.sh` with NO `cd` at all. Fix dispatched. This bug class (source `run.sh`, or a copy of it, before `cd`ing into an isolated scratch dir) has now appeared in at least 3 registered-in-CI files; `test-review-diff-moat.sh` also reproduces it standalone but is not registered in any CI path today (orphaned test, latent bug, not a current shard contributor). A systematic repo-wide sweep for this exact pattern (`source .*run\.sh` or a self-copy of it, with no preceding isolating `cd`) is now clearly warranted as its own slice, rather than continuing to discover instances one CI failure at a time -- each new fix's own verification pass keeps finding the next one. **FIXED (2026-09-27, merge 41c2f604):** the repo-wide sweep closed 22 more files (19 originally scoped plus 3 new instances found mid-sweep: `test-doc-scope-generator.sh`, `test-p0-gate-behavior.sh`, `tests/council/test_managed_review_flag.sh`, the last of which sources the bug via a real `bash run.sh --help` subprocess, not just `source`). Verified: all 22 run clean in sequence with no `.loki/state/provider` left behind, the victim test then passes 4/4, moat suite matches main exactly (3 of 9, no rule failed), shellcheck clean. Also independently isolated at the victim side: `test-airgap-ollama-host.sh` now sets its own `LOKI_DIR` (`0f056336`), so it is immune regardless of any remaining, still-undiscovered contaminator elsewhere in the suite. **Retroactive quorum (2026-09-27):** this MEDIUM-tier merge shipped without its 2-reviewer quorum first (a real process gap, self-caught and disclosed in PROGRESS.md). Reviewer 2/2 (Sonnet) returned APPROVE: independently re-derived the root cause (found it is actually UNCONDITIONAL, firing even when `LOKI_PROVIDER` was explicitly set, not just on auto-detect -- a stronger bug than originally described), redid the 23rd-instance search after an advisor-caught blind spot in the first pass (~35 additional call sites found repo-wide, all already isolated or targeting unrelated scripts, none a missed instance), and added a RED-check not in the original ask: removed one file's `cd` fix in an isolated worktree, reproduced the exact original bug (`.loki/state/provider` written into the worktree root), then confirmed reverting the fix restored green. No blocking findings. Reviewer 1/2 (Opus) independently also returned APPROVE: byte-diffed pre/post output across all 22 files (differs only by the 2 new isolation lines), confirmed the sleep-plus-`-nt` mutation harness is genuinely load-bearing (found `test-exit-code-contract.sh` passes VACUOUSLY on real bash 3.2 without the sleep, even while contaminated -- so the sleep is not cosmetic there), ran all 22 through a real 4-shard parallel `tests/run-all-tests.sh` invocation matching local-ci's own sharding with no leaks, and confirmed the moat suite's 2-of-9 (vs. 3-of-9 on a fully-provisioned checkout) is a fresh-worktree `node_modules`-absence artifact, not a regression. **Retroactive quorum: 2/2 APPROVE, no blocking findings from either reviewer -- gap closed.** Both reviewers flagged the same non-blocking point independently: `test-airgap-ollama-host.sh` no longer actually detects this bug class (it was isolated separately via `LOKI_DIR` in `0f056336`), so the real regression detectors going forward are each of the 22 files' own new `-nt` checks, not the original victim test.
185
+
186
+ 127. **(CORRECTED 2026-09-26: an earlier draft of this item claimed `56eb866f` was already merged and fixed. It was reviewed and unanimously approved same-session as S-22 but never actually merged -- a real gap on main until now. Now genuinely fixed and merged: `56eb866f` merged via a dedicated merge commit, pushed. `council_should_stop`'s shadow-write block is now skipped entirely when `PROJECT_DIR` is unset, plus -E on the python3 call; real code-execution RED/GREEN confirmed via a live marker-file demonstration, polled across the backgrounded+disowned call.) Sibling of BACKLOG 63 (S-22): `council_should_stop` (~line 4635 in `autonomy/completion-council.sh`) had the identical unguarded `cd "${PROJECT_DIR:-$(pwd)}"` pattern feeding `python3 -m memory.managed_memory.shadow_write` -- more severe than S-22's own retrieve-path bug since this one EXECUTES a module, not just imports it for reading.** Two more copies of this pattern remain open: `autonomy/run.sh:19861` has the identical unguarded `cd "$PROJECT_DIR" && python3 -m memory.managed_memory.shadow_write` (found during the `56eb866f` fix, not fixed there, out of that slice's file set -- same fix shape needed); `autonomy/completion-council.sh:4204` (`council_managed_should_stop`) has `project_dir="${PROJECT_DIR:-$(pwd)}"` but only feeds a `git diff` read, not a python module execution, gated behind the narrower `LOKI_EXPERIMENTAL_MANAGED_AGENTS` flag -- lower severity, still worth the same guard for consistency.
187
+
188
+ 128. **Stale documentation asserts a `{"runner":"none","pass":true}` writer shape that doesn't exist.** Found while fixing S-07's rework #2 (BACKLOG 33): `references/core-workflow.md:81` and `docs/VERIFIED-COMPLETION-PLAN.md:90,315,316` assert the same false shape corrected in D20 (the real writer, `enforce_test_coverage` at `run.sh:12691-12692`, writes `pass:"inconclusive"`, a string, never boolean `true`). Not behaviorally load-bearing (nothing parses these docs at runtime) but misleading to a future reader. Also: `tests/test-council-devils-advocate.sh` Case 2, `tests/test-da-veto.sh`, `tests/test-council-convergence-floor.sh:92` use synthetic `pass:true` fixtures instead of the real writer shape -- harmless today since `runner=='none'` is unconditional at every site that reads them, but should be replaced with the real shape for honesty.
189
+
190
+ 129. **FIXED.** S-24's tamper-detection seal (BACKLOG 70) only hardened the DIGEST computation, not the snapshot WRITER/READER paths. Found by the adversarial reviewer of the awk-bypass fix (c315cbe9): `_loki_untracked_merge` (writes `preexisting-untracked.z`), `_loki_covered_paths` (reads it after verify), and `_loki_untracked_status`'s `git` invocation were all still bare PATH lookups (`python3 -E`, not `-I` + absolute path; `git` unresolved). An agent that plants a malicious `.pth` file or a fake `python3`/`git` earlier on PATH during one session could forge the CONTENT of the snapshot file itself before the (correctly hardened) seal ever computed its digest over that already-forged content -- the seal then faithfully approved the forgery. Reproduced both classes empirically before fixing: (a) a `.pth` in user site-packages fired arbitrary code during `_loki_untracked_merge`'s and `_loki_covered_paths`'s `python3 -E` calls and could rewrite their declared output after the legitimate write; (b) a fake `git` shadowing the real one on PATH caused `_loki_untracked_status` to omit a real untracked path from its output, which then propagated all the way into the sealed snapshot as if that path never existed. Fixed by adding `_loki_snapshot_py_tool` and `_loki_snapshot_git_tool` (same fixed-root-path-first pattern as `_loki_snapshot_hash_tool`, each candidate probed with a real run, not just `-x`) and routing every python3/git call in the writer chain through them: `_loki_untracked_merge`, `_loki_covered_paths` (both now `-I -S`, not just `-E`/`-I` -- `-S` is required to skip `import site` entirely: `-I` implies `-s` and already skips USER site-packages, but not the resolved interpreter's OWN site-packages directory, which on a Homebrew-installed python3 is itself user-writable; `/usr/bin/python3`'s own site-packages is root-owned, so `-I` alone would suffice there specifically, but `-S` is the correct universal fix since the resolver can fall back to a PATH interpreter), `_loki_untracked_status`, `_loki_tracked_by_user_since_anchor` (feeds the writer's "tracked" exclusion set), `_loki_snapshot_preexisting`'s own `rev-parse --show-toplevel`, and the `hash-snapshot` invocation that writes `.sha.z` (the sibling sealed file). Post-fix, both reproduced bypasses were re-run and confirmed closed (the `.pth`/fake-git have zero effect on the writer/reader's actual output). **Explicitly out of scope, left as BACKLOG 131**: `_loki_untrack_agent_committed_user_files`'s five bare `git` calls (feed the READER, `_loki_covered_paths`, with the paths it checks) and the bare `git reset`/`git commit`/`git update-index` calls in the commit path itself -- a fake git already defeats the commit gate directly at commit time regardless of what the snapshot says, so resolving only the reader's git feed would not change that attacker's achievable outcome; a real fix needs a process-wide resolved-git convention, tracked separately rather than folded into this change.
191
+ 130. **S-24 follow-up, non-blocking, from the same review round:** (a) `_loki_snapshot_verify`'s `SNAPSHOT_TAMPERED` audit log doesn't record the live vs. sealed digest values, so an operator can't distinguish a genuine content change from a flaky hash-tool failure (both produce the identical message) -- add both digests to the audit line; (b) a verify-then-read race exists between `_loki_untrack_agent_committed_user_files`/`commit_session_changes`'s verify call and their later file read, where a background process could swap the file in between (compounded by BACKLOG 129's reader-side exposure, now narrowed by the BACKLOG 129 fix but not closed for the git-fed paths -- see BACKLOG 131); (c) `.loki/state/session-created.z` is not covered by the seal at all -- an agent could add a user's pre-existing files to it, causing a later resume to leave those files out of the snapshot and commit them as session work.
192
+ 131. **Process-wide resolved-git convention needed (split out of BACKLOG 129's fix scope); reviewed 4 times total across two rounds (2/2 then a 2/2 top-up, all four APPROVE), with corrections from both rounds folded in below.** `_loki_untrack_agent_committed_user_files` (autonomy/run.sh:9969-10022; CORRECTED 2026-09-27: 9 bare `git` calls in this one function, not "5" as an earlier draft said -- `git rev-parse --verify` (9982), `git merge-base` x2 (9984, 9988), `git cat-file` (9989), `git rev-parse --show-toplevel` (9999), `git -C diff` (10000), `git reset` (10013), `git -C update-index` (10014), `git commit` (10015); the reset/update-index/commit trio is INSIDE this same function, not in `commit_session_changes` as an earlier draft misattributed -- `commit_session_changes` starts at line 10024 and has its own separate `git reset -q` at 10127) still use unresolved `git`, along with roughly 290+ other bare `git` invocations across `autonomy/run.sh`. (a) **Understated, per the first round's reviewers**: framed as "low-value to fix since a fake git already defeats the commit gate directly" -- true, but that argument only excuses NOT fixing the commit-time calls; the reader's own git feed still means BACKLOG 129's git-side hardening protects the snapshot's stored CONTENT, not what the commit outcome actually is, until this lands -- say that plainly rather than "closed" or "already covered elsewhere." A fake `git` shadowing the real one already defeats the commit gate directly (it can no-op `git commit`, or lie about what `git reset` did) independent of anything the snapshot says, so this is a materially larger fix than BACKLOG 129's writer/reader scope and was deliberately left out of it. (b) `_loki_snapshot_digest`'s python3 fallback (used only when neither sha256sum nor shasum resolves anywhere) is not yet routed through `_loki_snapshot_py_tool`/`-S`; a top-up reviewer independently reproduced that Homebrew python3 `-I` alone (without `-S`) still fires a `.pth` planted in its own site-packages, confirming this gap is real, not theoretical. (c) **Understated, per the first round's reproduction attempt, then widened by the top-up round**: `.git/config`'s agent-writable `core.fsmonitor`/`core.untrackedCache` settings don't just "shape" what a correctly-resolved `git status` reports -- `core.fsmonitor` makes git run a command the agent CHOSE on every status call and trust what it reports, which could be a full bypass of the git-side hardening entirely (an attempted untracked-cache/directory-mtime PoC did not succeed, so the full bypass is not yet proven either way -- treat as open, not dismissed). A top-up reviewer separately confirmed `core.worktree` is not yet named here and is a related agent-writable vector (it can redirect `rev-parse --show-toplevel` and `status` to point at a different tree entirely) -- add it to this enumeration; the same reviewer tried setting it as an attack and found the writer currently fails closed (`rev-parse --show-prefix` fails outside the worktree) rather than being fooled, so this specific vector is not yet a proven bypass, only a named one. Mitigate `core.fsmonitor`/`core.untrackedCache` with `-c core.fsmonitor=false -c core.untrackedCache=false` on the status call, or investigate and disclose definitively; mitigate or investigate `core.worktree` separately.
193
+
194
+ 132. **Mutant-detection test relies on bash-version-specific `cd ""` behavior, found during the BACKLOG 127/S-22 shadow-write fix's quorum top-up.** `tests/test-council-shadow-write-project-dir.sh`'s mutant check (strip only the `-n PROJECT_DIR` guard, leave the `cd "$PROJECT_DIR"` rewrite in place) stays GREEN on bash 5.3 (Homebrew) because bash 5.3's `cd ""` hard-fails with `cd: null directory` and short-circuits the `&&` chain -- but the identical mutant reproduces RED on real bash 3.2.57 (macOS system `/bin/bash`, what `#!/usr/bin/env bash` resolves to on a plain macOS machine), because bash 3.2's `cd ""` is a silent success that leaves cwd unchanged. The merged fix itself is correct on both interpreters (verified directly by two independent reviewers); only the test's mutant discriminator is interpreter-luck under bash 5. Fix: run the GREEN/mutant comparison under `/bin/bash` explicitly, not `#!/usr/bin/env bash`, so a future edit that dropped only the `-n PROJECT_DIR` guard cannot look safe locally while shipping a live hole on the macOS default interpreter.
195
+
134
196
  ## Later milestones
135
197
 
136
198
  M1 one command, M2 Seal and Wall, M3 assign like a teammate, M4 system map, M5 the line, M6 ship and operate, M7 one screen, M8 legacy lane, M9 enterprise readiness, M10 simplify and ship v10.0.0. Items get broken out here when their milestone comes up.
@@ -0,0 +1,134 @@
1
+ # Board
2
+
3
+ Written only by the Product Owner and the Release Captain. One row per
4
+ slice. See `docs/v10/SWARM.md`.
5
+
6
+ ## Status format (normalized for `scripts/v10-pulse.sh`)
7
+
8
+ Every row's `Status` cell is exactly one token, an `@`, and a UTC timestamp
9
+ (`<token>@YYYY-MM-DDTHH:MMZ`) -- the time the status last changed. Prose,
10
+ history, and caveats move to `Notes`; `Status` itself must stay
11
+ machine-parseable with no free text. Tokens, in the lifecycle order the pulse
12
+ script expects:
13
+
14
+ `ready` -> `building` -> `review` -> `review-blocked` (returns to `building`)
15
+ -> `approved` -> `merged` -> `released`. Side states: `blocked` (needs a
16
+ Captain/PO decision), `rejected` (sent back, will be re-cut), `parked`
17
+ (deliberately shelved).
18
+
19
+ `merged` here means "the Captain landed it on main" -- a slice sits at
20
+ `merged` until the Captain flips it to `released` on the next version bump
21
+ that ships it. The pulse script does NOT trust this field for its own
22
+ "merged-but-unreleased age" computation: it derives that directly from git
23
+ (`git log` on main after the last release tag) since a table cell can go
24
+ stale between edits. This field still matters for humans and for the
25
+ ready/building/review/blocked counts, which git cannot derive.
26
+
27
+ ## Grandfathered slices (in flight before the swarm existed)
28
+
29
+ These were built and reviewed under the cycle process, not cut by the PO.
30
+ They enter the Captain's merge queue as-is, each reviewed at HIGH tier before
31
+ merge (all touch trust-core or security-relevant surfaces), oldest first.
32
+
33
+ | ID | Owner | Branch @ SHA | File set (summary) | Tier | Status | Notes |
34
+ |---|---|---|---|---|---|---|
35
+ | GF-1 | cycle-4 (main) | main @ 779c50e5 (v9.54.1) | cycle 4 + verdict fix + XSS fix + pkill-scoping fix (D15/D16/D17, RELEASED) + D8-D17 | HIGH | blocked@2026-09-27T01:33Z | PF-2 (P7 scanner gap fix) finished at ecfd9578, ready for review. Full council re-run required once merged (D12). PF-1 shipped separately as v9.54.1, ahead of GF-1. |
36
+ | GF-2 | P5 slice | main @ c7b2d8b4 | providers/loader.sh, autonomy/loki, bin/loki, docs/air-gapped.md, docs/enterprise/*, README.md, tests/moat/p5-*.sh, tests/test-doctor-json-sentrux.sh | HIGH | merged@2026-09-27T01:33Z | Rebased onto current main (past S-11), 4 conflicts resolved (cloud-tag detection + OLLAMA_HOST override are complementary, merged with cloud-tag checked first). HIGH review APPROVE. P5 PROVEN (9/9), pushed. Moat is now 3 of 9 (P5, P6, P7). CORRECTION: the `.loki/state/provider` issue found during verification was NOT a local-only false alarm -- it is a real CI-breaking cross-test contamination bug (test-iteration-grace.sh writes it, poisoning every later test in the same CI shard's shared checkout, including test-airgap-ollama-host.sh); it genuinely broke `Tests` (shard 3/4) on `ba6610dc`. See BACKLOG 126 for the corrected root cause and the fix (shipped, see PROGRESS.md/BACKLOG 126). |
37
+ | GF-3 | P9 slice | worktree-agent-a706097c126bd997f @ 7a92f848 | .github/workflows/*.yml, .github/actions/issue-to-pr, autonomy/run.sh (github_token wrap), loki-ts/src/runner/github_token.ts, tests/moat/p9-rule-of-two.sh | HIGH | review@2026-09-27T01:33Z | Dispatched, rebase+review in flight -- had sat unreviewed all session. P9 PROVEN in worktree (4/4 cases) as of pre-rebase SHA. Needs a validation PR (auto-merge OFF; see SWARM.md Captain step 6) BEFORE counting P9 proven on main. Ship alone (touches release.yml). S-18 is BLOCKED behind this merging first. |
38
+ | GF-4 | P4 slice | rebased ref gf4-rebase-review-branch @ 40ae1406 (scratchpad worktree, not merged) | providers/model_catalog.json, loki-ts/src/commands/start.ts, loki-ts/src/runner/providers.ts | MEDIUM | ready@2026-09-27T01:33Z | Unblocked: S-19 (the deadlock's other half) merged to main unanimous 2/2 APPROVE. Rebase GF-4 onto current main, then RESTORE the `P4.three-setups-resolve` pending.txt line (a second S-19 reviewer flagged this precisely: GF-4's current pending.txt edit deletes that line, which will read as an unlisted ratchet regression once S-19's stricter bar is in the tree -- update the pending reason to reflect what's now true on GF-4's tree: bash/Bun both honor the opus pin and per-tier vars; only high/small still resolve to sonnet, matching D10's documented default). `P4.catalog-has-top-model`'s promotion is unaffected and can proceed as originally written. |
39
+ | GF-5 | verdict/security fix | worktree-agent-a9b8124e8cfc6809a @ 399e1f3a | web-app/src/components/EvidenceReceiptPanel.tsx, dashboard-ui/components/loki-audit-viewer.js, dashboard/audit.py, dashboard/server.py (get_proof), dashboard/auth.py | HIGH | merged@2026-09-27T01:33Z | Cherry-picked onto main as 3a139d81 (GF-1 now includes it). This row is historical; do not re-review separately. |
40
+
41
+ ## Priority out-of-band fix (severity: kills unrelated user sessions)
42
+
43
+ | ID | Owner | Branch @ SHA | File set | Tier | Status | Notes |
44
+ |---|---|---|---|---|---|---|
45
+ | PF-1 | pkill-scoping fix | main @ 9cd51d1a | autonomy/run.sh (kill_provider_child), tests/test-kill-provider-child-scoping.sh, docs/v10/DECISIONS.md (D15) | HIGH | released@2026-09-27T01:33Z | Founder-reported: every loki-mode session ending via signal (supervisor signal, double Ctrl+C, Ctrl+C in perpetual mode) ran an unscoped `pkill -f` matching ANY process on the machine by command-line substring, terminating unrelated Claude Code sessions. Fixed to scope by process group. Shipped as v9.54.1, ahead of the swarm queue. |
46
+ | PF-2 | P7 scanner gap fix | main @ fa5de15f | tests/moat/p7-no-fabricated-data.sh | HIGH | merged@2026-09-27T01:33Z | Round 5, unanimous 4/4 APPROVE under a corrected scoping rule (only a regression/false-positive/weakening introduced by the diff blocks; a pre-existing gap the diff doesn't worsen is a new slice, not a veto). Rebased onto current main, merged fa5de15f, pushed. 10 candidate follow-up slices recorded below (B-1..B-8 plus 2 more from the review). |
47
+
48
+ ## Fixed on main already (not slices; recorded for PO context)
49
+
50
+ - BACKLOG 95, 97: fixed (cycle 4 + P7 sweep).
51
+ - BACKLOG 96, 98: partly fixed by the P7 sweep (0afb6e2c); remaining pieces
52
+ are BACKLOG 112, 118.
53
+ - BACKLOG 113, 119: fixed (3a139d81, in GF-1).
54
+ - XSS in receipt/learnings panels: fixed (f6050ef1, in GF-1).
55
+
56
+ ## Ready queue (PO-cut)
57
+
58
+ Cut against BACKLOG items not already owned by an in-flight slice (PF-1, PF-2,
59
+ GF-2 P5, GF-3 P9, GF-4 P4, the repo-wide kill-scan agent). Each slice's file
60
+ set is declared to avoid overlap with those and with each other.
61
+
62
+ | ID | Owner | File set | Tier | Acceptance checks (Wall) | Status | Notes |
63
+ |---|---|---|---|---|---|---|
64
+ | S-01 | BACKLOG 22: orphaned `sleep 300` resource monitor | autonomy/run.sh (resource-monitor spawn/cleanup only, not kill_provider_child), one new test | MEDIUM | red: `loki start` then interrupt leaves a `sleep 300` orphan (ps shows it after exit); green: it is reaped or never orphaned, by PID recorded at spawn | released@2026-09-27T01:33Z | 027ec085 confirmed ancestor of origin/main; note corrected, was stale. |
65
+ | S-02 | BACKLOG 23: client-route test hardcodes main checkout path | tests/lib-extract-client-paths.mjs, tests/lib-match-client-routes.py, tests/test-verify-client-routes.sh | LOW | red: run from a worktree, test checks the wrong tree (provably, by diffing what it scanned vs cwd); green: it resolves its own repo root dynamically | building@2026-09-27T01:33Z | |
66
+ | S-03 | BACKLOG 25: `loki ci --report` "Argument list too long" on a large diff | autonomy/loki (cmd_ci report path only), one new test | MEDIUM | red: a synthetic diff over the ARG_MAX threshold, exit 126; green: same diff passed via stdin/file, exit 0 | rejected@2026-09-27T01:33Z | Real crash site (export-based ARG_MAX) misdiagnosed and left unfixed; sent back to builder. |
67
+ | S-04 | BACKLOG 26: Bun Parity `doctor-json` flake (disk.available_gb 94 vs 95) | .github/workflows/bun-parity.yml, tests/test-bun-parity-disk-tolerance.sh (new) | LOW | red: two consecutive doctor --json calls disagree on disk.available_gb and the parity check flags it; green: normalized (rounded/bucketed) or excluded from strict comparison, documented why | merged@2026-09-27T01:33Z | Merged 7a13b816, test registered f0f51587, pushed. |
68
+ | S-05 | BACKLOG 31: `proof-verify.py` "drift unverifiable" reads 1 (tampered) instead of 2 (could not check) | autonomy/lib/proof-verify.py, its test | MEDIUM | red: a genuinely-unverifiable drift case reads 1; green: it reads 2 | merged@2026-09-27T01:33Z | Merged 1a02bab1, pushed; quorum topped up 2/2 APPROVE. Fixed the receipt-attest.py substring-lookup break from the rename, plus documented an accepted ceiling (a hand-forged, re-hashed receipt with a stripped diff stat is indistinguishable from an honestly-unverifiable one; unreachable from any honestly-generated receipt). |
69
+ | S-06 | BACKLOG 32: docs tell npm users to run `doctor --airgap`, which the default (Bun) route rejects | docs/air-gapped.md, deploy/helm/README.md | LOW | the doc now says `LOKI_LEGACY_BASH=1 loki doctor --airgap` (or notes the Bun-route gap) until P5.airgap-audit-default-route ships; grep confirms the old bare command is gone from both files | released@2026-09-27T01:33Z | In main since c83732b2 / v9.54.2. |
70
+ | S-07 | BACKLOG 33: council `runner=none` counts as a positive signal, a zero-test suite does not -- pick one rule | autonomy/completion-council.sh (the specific vote-reading site only), its test | MEDIUM | merged@2026-09-26T23:10Z | Confirmed on main directly at ad47a2ec (this row was stale: BOARD said `review` but the commit was already an ancestor of origin/main; no separate merge commit was needed). Rework #2. Round 1 (regression from rework #1's pass:true narrowing) fixed by dropping the narrowing entirely -- runner=='none' unconditional again (matches the real writer, run.sh:12691-12692, which writes pass as the STRING "inconclusive", never boolean true), while keeping the correct status-first ordering fix. Unanimous 2/2 APPROVE: both reviewers independently confirmed status is checked unconditionally before runner=='none' in both parsers (a crafted runner:none+status:no_tests_run record cannot bypass), original bug and rework-#1 regression both verified fixed with independent fixtures. 78/78, 24/24, 41/41, 5/5 across the 4 regression suites. |
71
+ | S-08 | BACKLOG 39: attestation edge labels (`{"keys": []}` reads FAILED not NOT CHECKED; `attestation: false` inconsistent local vs remote) | autonomy/receipt_jwt.py or the relevant attestation renderer, its test | MEDIUM | red: an empty keyset and a false-attestation receipt each render a misleading label on one route; green: both read NOT CHECKED consistently on both routes | merged@2026-09-27T01:33Z | Merged bbe307d8, pushed; unanimous 2/2 APPROVE. |
72
+ | S-09 | BACKLOG 40: `P3.implementer-prompt-has-no-check-authoring` is phrase-based only | tests/moat/p3-*.sh (add a behavioral assertion; do not weaken the existing phrase check) | MEDIUM | new assertion: the implementer session does not actually write to the checklist file, not just that a phrase is absent from the prompt; red on a synthetic prompt that omits the phrase but still authors checks, green once caught | review@2026-09-27T01:33Z | 7a8adead @ worktree-agent-a7d3c71ecfeeb3a16; honestly rescoped to a broader text-signature detector, not a true behavioral check -- states its own ceiling explicitly. |
73
+ | S-10 | BACKLOG 41: `P1.different-tree-fails` lacks an in-script no-edit control | tests/moat/p1-*.sh | LOW | add the control described (an unedited copy verifies 0 / clean); case still passes, control documented inline | merged@2026-09-26T21:22Z | Confirmed on main directly at 368332cf (this row was stale: BOARD said `review` but the commit was already an ancestor of origin/main). |
74
+ | S-11 | BACKLOG 42: `doctor --airgap` "local" is a loose substring match, ignores remote `OLLAMA_HOST` | autonomy/loki (airgap audit only) or loki-ts doctor.ts, its test | MEDIUM | red: `OLLAMA_HOST=https://remote.example.com` still reads as local because the string contains "ollama"; green: a remote OLLAMA_HOST counts as required egress | merged@2026-09-27T01:33Z | 698ce1c8, pushed; ships in next release cut. |
75
+ | S-12 | BACKLOG 46: P2 advisory-only case's positive control differs from the probe in two variables | tests/moat/p2-*.sh | LOW | control now uses an exogenous gate with no tests, matching the probe's other variable exactly; case still passes | released@2026-09-27T01:33Z | 0a59f420 confirmed ancestor of origin/main; note corrected, was stale. |
76
+ | S-13 | BACKLOG 48: same-class D7 hardening -- `loki outcomes canary verify`, `proof share`/`show` heredocs, empty PATH component | autonomy/loki (the specific inline python3 sites named), its test | MEDIUM | merged@2026-09-27T01:33Z | Merged bb99e675, pushed; quorum topped up 2/2 APPROVE. RED leg pins a literal parent SHA (reachability-checked, SKIP on a shallow clone) instead of HEAD, confirmed to survive being merged. `loki_verify_tool_path()` rejects any non-absolute PATH entry. Quorum reviewer confirmed all 5 hardened call sites fail closed (hard error), never silently fall back to a bare unscoped python3. |
77
+ | S-14 | BACKLOG 49: verifier stdout still says `ok: true` on ABSENT/NOT CHECKED while exit is 1/2 | autonomy/lib/proof-verify.py (or the relevant verifier), its test | MEDIUM | red: stdout JSON says ok:true for an ABSENT/NOT CHECKED result although exit code disagrees; green: the JSON verdict field matches the exit code exactly | building@2026-09-27T01:33Z | Rebasing -- caught before review completed: original commit (9032727) was cut from a STALE base (a4177fbf) that predates S-08 (merged bbe307d8), which touches the SAME two files in the same attestation-exit-code area. The 2 reviews dispatched against the stale base were stopped before returning a verdict; dispatched a rebase-first pass instead. Real fix site is `autonomy/loki`'s `cmd_proof verify)` case (not proof-verify.py as originally guessed). |
78
+ | S-15 | BACKLOG 50: remote NOT CHECKED returns 0, local returns 2 -- align or document | the remote verifier path, docs/exit-codes.md | LOW | either the remote exit is changed to 2 with a test proving it, or docs/exit-codes.md documents the divergence explicitly with the reason; pick one and record which in the commit message | merged@2026-09-27T01:33Z | Main since 34198ff4. Round 1 found the first draft factually wrong (missed the nopubkey unconditional-exit-1 override), corrected and re-approved. |
79
+ | S-16 | BACKLOG 22 follow-on / housekeeping: `scripts/release.sh` only bumps 3 of the 14 files CLAUDE.md's release checklist requires (VERSION, package.json, vscode-extension/package.json) | scripts/release.sh | MEDIUM | red: run --dry-run on a version bump, diff shows SKILL.md/Dockerfile/Dockerfile.sandbox/plugin.json/server.json/dashboard __init__.py/mcp __init__.py untouched; green: the script bumps all 14 files CLAUDE.md's Release Workflow section 1 lists, or the script explicitly documents which it intentionally leaves to the Captain and why | building@2026-09-27T01:33Z | |
80
+ | S-17 | BACKLOG 43: council/runner test-result readers (`completion-council.sh`'s 4 `python3 -c` sites) can be shadowed by a committed `hashlib.py`/`json.py` in the agent's own repo (cwd module shadowing, honest-verdict, HIGH) | autonomy/completion-council.sh (the 4 named `python3 -c` test-results readers only, not the S-07 vote site), its test | HIGH | review@2026-09-27T02:30Z | Rebased onto current main in worktree-agent-a5422f6f162d3dd2f (now at 23025a09), NOT yet merged -- HIGH tier requires 3+1 unanimous APPROVE (D12/D13) before merge, which has not run against this diff. Found the CODE fix already shipped in 3bc40efb (all 43 inline python3 sites in this file, including all 4 named readers, already have the D7 pattern) -- this slice is test-coverage-only, no production code changed. Extended tests/moat/p2-honest-verdict.sh's existing P2.council-readers-not-shadowed case to drive the 3 previously-untested readers (council_heuristic_review, council_evaluate_member, council_devils_advocate_review) via mutation-cycle verification (stripped each site's filter, confirmed shadow fired/verdict flipped, restored, reconfirmed clean). No cases.txt/pending.txt change (no new case ID). Solo-verified (not a substitute for quorum): moat matches main (3 of 9, no rule failed), shellcheck clean (one pre-existing unrelated SC2034 warning). Quorum dispatch pending. |
81
+ | S-18 | BACKLOG 44: P9 injection case only checks env/argv for the canary, not the run HOME or provider-visible credential files (`~/.config/gh/hosts.yml` etc.) | tests/moat/p9-rule-of-two.sh (extend the existing injection case; never weaken it or add a new pending entry) | MEDIUM | red: a canary placed in a provider-visible credential file is not detected by the current grep scope; green: the case's grep also covers run HOME and provider config files, case still passes from day one | building@2026-09-27T01:33Z | Dispatched; instructed to check for GF-3 conflict and report BLOCKED if the file has diverged. |
82
+ | S-19 | BACKLOG 45: `P4.three-setups-resolve`'s pass bar lets a documented spelling silently dispatch the wrong model (e.g. `--session-model high` sending sonnet) with only a note, not a failure | tests/moat/p4-*.sh (the three-setups case only) | MEDIUM | red: construct the silent-wrong-dispatch scenario, confirm the case currently notes it but still passes; green: the case fails when a documented spelling dispatches a different model than documented | merged@2026-09-27T01:33Z | 885b1c1d @ worktree-agent-acb0607e467ebc729, unanimous 2/2 APPROVE, merged. IMPORTANT: found that GF-4 (still unmerged) would falsely claim P4.three-setups-resolve is promotable under GF-4's own catalog changes when it actually still silently mis-resolves high/small under this stricter bar -- GF-4's reviewer must re-check this before GF-4 merges, or the merge will look like a ratchet regression. |
83
+ | S-20 | BACKLOG 47: moat bootstrap is not refused on a hotfix/release branch once any moat release already exists on origin (a branch cut before the first carrying tag runs in bootstrap, unratcheted) | tests/moat/run.sh (the bootstrap-detection logic only) | MEDIUM | red: construct a branch state where a moat release tag exists on origin but is not an ancestor of this branch, confirm the runner still bootstraps (accepts pending.txt as-is) instead of refusing; green: the runner detects this and fails closed with a clear message | building@2026-09-27T01:33Z | Dispatched. |
84
+ | S-21 | BACKLOG 61: `evidence-block.json` writes the raw `$test_pass` in the block path, so `tests.pass` can read true for an inconclusive test axis when another axis blocks | autonomy/completion-council.sh (the evidence-block.json write site only, ~line 2792), its test | MEDIUM | merged@2026-09-27T01:33Z | Merged 8431bb39, pushed; quorum topped up 2/2 APPROVE. Same defect class BACKLOG 55 already fixed for evidence-gate-details.json, mirrored to this sibling write site. 60/60 on test-evidence-gate.sh (was 51/51). |
85
+ | S-22 | BACKLOG 63: `council_augment_from_managed_memory` runs `python3 -m memory.managed_memory.retrieve` after `cd "${PROJECT_DIR:-$(pwd)}"`; with PROJECT_DIR unset this can run the AGENT's own repo's memory/ package into the council prompt | autonomy/completion-council.sh (this one function only), its test | HIGH | merged@2026-09-27T01:33Z | Merged a8e749be, pushed; quorum now 3/4 APPROVE + 1 CONCERN, the CONCERN scoped to the sibling `council_should_stop` function this diff doesn't touch, tracked separately as BACKLOG 127/S-25 and now also fixed and merged. PROJECT_DIR unset now returns 0 immediately (fail-closed no-op), matching the existing D7 pattern in council_managed_should_stop. Production unaffected (run.sh always sets PROJECT_DIR before sourcing this file). BACKLOG 127's sibling shadow-write RCE fix (56eb866f) separately re-verified from scratch by 2 fresh reviewers (unanimous APPROVE, no blocking findings), settling an earlier quorum-count discrepancy; independent RED/GREEN/control repro confirmed real on both bash 3.2 and bash 5.3. BACKLOG 132 opened for a bash-3.2-only mutant-detection gap the reviewers found in that fix's own test. |
86
+ | S-23 | BACKLOG 68: the resume union never prunes entries that became tracked -- a file the user committed between sessions is still unstaged by the session commit while the receipt lists it as modified | autonomy/run.sh (the resume-union logic only), its test | MEDIUM | merged@2026-09-27T01:33Z | Merged 0bff408e, pushed. Two follow-up fixes combined on top of 5610cebb: (1) bash 3.2 syntax (5 apostrophe-in-comment instances, verified against a real 3.2.57 binary); (2) marker-clear bug (turn-in-flight cleared even on anchor-write failure, silently reintroducing the fail-unsafe scenario -- gated on write success). Merging this into main (which already had S-24's tamper-detection seal) surfaced a REAL interaction bug neither fix's own review could see in isolation: 6 test cases used `resume_log="$(setup_agent_branch ...)"`, a command-substitution subshell whose `_LOKI_SNAPSHOT_SEAL` update never reaches the parent shell -- a stale seal then mismatched the live (correctly pruned) files and fired a false SNAPSHOT_TAMPERED warning, blocking every commit. Confirmed test-only (production calls setup_agent_branch directly, never in `$(...)`); fixed by redirecting to a file instead, matching a pattern one existing test case already used with a comment documenting the exact hazard. 55/55 passing. Lesson: full-suite verification AFTER combining independently-reviewed fixes is not optional when they share mutable state. Follow-up review batch (marker-clear fix, merge-conflict resolution, 6-test-site subshell fix) closed: unanimous 2/2 APPROVE, no blocking findings, 62/62 and moat unchanged. Non-blocking notes recorded as BACKLOG follow-ups (stray stderr on failed tmp-write, a contrived directory-as-anchor edge case that still fails safe, no warning when the marker stays stuck). |
87
+ | S-24 | BACKLOG 70: the snapshot/hash state lives in agent-writable `.loki/state` -- an adversarial agent can add entries (hide/uncommit files) or delete the hash file (suppress preexisting_modified disclosure) | autonomy/run.sh (snapshot/hash file location and read/write guards only), its test | HIGH | merged@2026-09-27T01:33Z | Merged c983c4fc + the awk-bypass fix c315cbe9, both pushed; quorum now 4/4 APPROVE on the awk fix. In-memory seal, checked at 3 read sites; a mismatch forces the existing fail-closed "commit nothing" path. Two rounds of adversarial review found and closed two real bypasses (`.pth`-file hijack, then a relocated `awk`-on-PATH bypass in the fix for the first). Digest computation now uses only fixed-path system tools and pure shell string handling, with hex validation. 51/51 regression, moat unchanged. BACKLOG 129/130 track the remaining follow-ups (writer/reader paths still bare python3 -E, audit-log digest values, session-created.z unsealed); BACKLOG 129's writer/reader hardening (merge 3e374500) now at full 4/4 HIGH-tier APPROVE quorum. Both bypass classes independently re-reproduced and re-confirmed closed by the top-up reviewers (.pth in Homebrew's own site-packages, fake git on PATH); legitimate-case output confirmed byte-identical pre/post fix. Non-blocking: BACKLOG 131 corrected for an undercounted/misattributed git-call inventory and a missing core.worktree mention; BACKLOG 132 opened for a bash-3.2-only mutant-detection gap in the shadow-write fix's own test (unrelated slice, found during the same top-up pass). |
88
+ | S-26 | Founder directive: `scripts/v10-pulse.sh`, the anti-drift status/violation reporter (docs/v10/CONTROL.md). See docs/v10/BACKLOG.md for the full spec. | scripts/v10-pulse.sh (new), tests/test-v10-pulse.sh (new), tests/run-all-tests.sh (registration) | MEDIUM | review@2026-09-27T02:56Z | Built (worktree-agent-a21ed4c1097f3e590, rebased to b5011426), 23/23 own tests pass, bash 3.2 clean, shellcheck clean. Two independent reviewers (Opus, Sonnet) both returned CONCERN with real, reproduced blocking findings -- NOT ready to merge: (1) git-derived metrics (release tag, moat baseline) follow the calling worktree's own HEAD instead of pinning to `main` via `PULSE_MAIN_REF`, contradicting the script's own documented contract -- reproduced live from a scratch checkout at an older tag, which flipped the moat-count reading; (2) merged-but-unreleased age walks non-first-parent history (`git log tag..main` with no `--first-parent`), so it picks up a merged branch's ORIGINAL commit timestamps rather than the merge time, producing a systematic false UNRELEASED_MERGE positive on ordinary swarm `merge:` commits; (3) MOAT_REGRESSION (priority #2 in the founder's own violation order) is structurally unable to detect a real regression: it infers "proven" purely from a property's absence in tests/moat/pending.txt, but pending.txt's own ratchet rule guarantees that set can only shrink, so `current_count < baseline_count` can never go true even when tests/moat/run.sh itself reports a live FAIL with unlisted REGRESSION lines -- reproduced on the CURRENT repo state (pulse says "3 of 9 proven", the real moat run says "2 of 9, FAIL (3 rule failures)" for the identical commit) -- and this also violates the script's own "never silently read as clean" contract, since the unmeasured moat count is never routed through UNKNOWN. Rework needed on all three before this can merge; do not ship as-is. |
89
+
90
+ Every slice above: implement with red-then-green tests, run diff-scoped tests
91
+ plus `bash tests/moat/run.sh` in your own worktree, never touch `main`,
92
+ `VERSION`, or a hot file/bundle (see below), and return BLOCKED (never guess
93
+ past your declared file set) if you need a file outside it. `git merge
94
+ --ff-only <captain's current main sha>` first; worktrees are cut from
95
+ origin/main. No version bumps, no emojis, no em or en dashes, bash 3.2
96
+ compatible, shellcheck clean at CI flags on changed .sh files. Stage by
97
+ name, conventional commit message, no Co-Authored-By, end with the line:
98
+ Claude-Session: https://claude.ai/code/session_019U6NAdpZyYeCEKzvHfZ4Tq. Do
99
+ not push.
100
+
101
+ ## Hot files (Captain-owned; no builder file set may include these)
102
+
103
+ `tests/moat/cases.txt`, `tests/moat/pending.txt`, `CHANGELOG.md`,
104
+ `docs/v10/BACKLOG.md`, `docs/v10/BOARD.md`, `docs/v10/PROGRESS.md`,
105
+ `docs/v10/METRICS.md`, `docs/v10/CONTROL.md`, `scripts/local-ci.sh`,
106
+ `tests/run-all-tests.sh`, `VERSION`, `package.json`, `SKILL.md`, `Dockerfile`,
107
+ `Dockerfile.sandbox`, `plugins/loki-mode/.claude-plugin/plugin.json`,
108
+ `server.json`, `dashboard/__init__.py`, `mcp/__init__.py`, `CLAUDE.md`.
109
+
110
+ ## Generated bundles (Captain rebuilds after every merge touching sources)
111
+
112
+ `loki-ts/dist/loki.js`, `dashboard/static/index.html`, `dashboard-ui/dist/*`,
113
+ `web-app/dist/*`.
114
+
115
+ ## Wave log
116
+
117
+ Appended by the Captain after each wave closes. See METRICS.md for the
118
+ numeric rollup.
119
+
120
+ - **Wave 0 (setup, 2026-09-26):** SWARM.md and BOARD.md written. Council
121
+ round 4 (pre-swarm, cycle-4-only) killed mid-run: two of three lenses had
122
+ no pinned model and the session model changed under it (D13). Stray
123
+ worktrees `agent-a8fad9a6e301fe297` (old, pre-v10, already merged) and a
124
+ scratch `mutwt` checkout (0 commits ahead of main) removed; four real
125
+ in-flight worktrees (GF-2, GF-3, GF-4, GF-5) recorded above. GF-5
126
+ cherry-picked into main directly (it was a small, already-approved fix,
127
+ not a swarm-cut slice) plus a same-session XSS fix and D8-D13. No release
128
+ cut yet this wave.
129
+
130
+ ## PF-3: repo-wide kill-by-name scan (severity: same class as PF-1/D14, found in shipped surfaces)
131
+
132
+ | ID | Owner | Branch | File set | Tier | Status | Notes |
133
+ |---|---|---|---|---|---|---|
134
+ | PF-3 | kill-scan sweep | main @ b539391d | autonomy/loki, autonomy/verify.sh, .github, dashboard harness scripts, scripts/cleanup-test-processes.sh, tests/test-runtime-gate.sh, tests/test-backend-floor.sh, 8 new scoping tests | HIGH | merged@2026-09-27T01:33Z | Round 6 fixed the port-overflow bug (narrowed range); unanimous 2/2 APPROVE. Merged to main via --no-ff (b539391d, 21 commits). All 9 new test files verified passing directly and registered in tests/run-all-tests.sh (6560ca95), pushed to origin (5b785b51..6560ca95). No version bump in this push; on main, not yet in a tagged release (latest tag is still v9.54.2 as of this correction). |