loki-mode 9.73.0 → 9.75.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 (79) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/council-v2.sh +60 -13
  4. package/autonomy/lib/done-recognition.sh +73 -21
  5. package/autonomy/lib/proof-generator.py +7 -0
  6. package/autonomy/loki +18 -7
  7. package/autonomy/run.sh +14 -1
  8. package/dashboard/__init__.py +1 -1
  9. package/dashboard/server.py +22 -13
  10. package/dashboard/static/index.html +5 -5
  11. package/docs/INSTALLATION.md +1 -1
  12. package/docs/R3-COST-OBSERVABILITY-DESIGN.md +7 -1
  13. package/docs/exit-codes.md +21 -0
  14. package/docs/v10/ARCHITECT-CUTS.md +386 -317
  15. package/docs/v10/BOARD.md +64 -24
  16. package/docs/v10/GUARDS.md +13 -7
  17. package/loki-ts/dist/loki.js +251 -250
  18. package/mcp/__init__.py +1 -1
  19. package/package.json +1 -1
  20. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  21. package/web-app/dist/assets/{AdminPage-B11jqriL.js → AdminPage-CMGkmNnt.js} +1 -1
  22. package/web-app/dist/assets/{Avatar-vmIEiL2U.js → Avatar-9mn8sJ-2.js} +1 -1
  23. package/web-app/dist/assets/{Badge-DxtrDGcl.js → Badge-mSFLvvRX.js} +1 -1
  24. package/web-app/dist/assets/{Button-BiPlRkkg.js → Button-B1v4CSkD.js} +1 -1
  25. package/web-app/dist/assets/CockpitPage-BW0hYuzC.js +47 -0
  26. package/web-app/dist/assets/{ComparePage-B4ddvt9J.js → ComparePage-C1BXuolM.js} +1 -1
  27. package/web-app/dist/assets/{ErrorBoundary-BIJy59N_.js → ErrorBoundary-C2sJyBhb.js} +1 -1
  28. package/web-app/dist/assets/{EvidenceReceiptPanel-C4-Phh1f.js → EvidenceReceiptPanel-a1irlBPD.js} +1 -1
  29. package/web-app/dist/assets/GitHubIssuesPanel-zM-WRxYB.js +7 -0
  30. package/web-app/dist/assets/{GitHubPRsPanel-B_VHTB-t.js → GitHubPRsPanel-DvRa5Rxq.js} +1 -1
  31. package/web-app/dist/assets/{HomePage-cxONjTSm.js → HomePage-CNwRexp5.js} +2 -2
  32. package/web-app/dist/assets/{LoginPage-D49p6aVy.js → LoginPage-HKogNiEX.js} +1 -1
  33. package/web-app/dist/assets/{MagicPage-C5GUEIuM.js → MagicPage-u1mvdkUd.js} +1 -1
  34. package/web-app/dist/assets/{MetricsPage-I-OXi9kG.js → MetricsPage-CThdMJb5.js} +1 -1
  35. package/web-app/dist/assets/{NotFoundPage-Do19cbOP.js → NotFoundPage-B1v3lELS.js} +1 -1
  36. package/web-app/dist/assets/{ProjectPage-TRyzTIFH.js → ProjectPage-hDvSsdLF.js} +63 -63
  37. package/web-app/dist/assets/{ProjectsPage-BLSDhtVb.js → ProjectsPage-CRAegSM6.js} +1 -1
  38. package/web-app/dist/assets/{SettingsPage-BvixtAw7.js → SettingsPage-Dt90bFxo.js} +1 -1
  39. package/web-app/dist/assets/{ShowcasePage-By96BMJz.js → ShowcasePage-C5a9l8u_.js} +1 -1
  40. package/web-app/dist/assets/{SystemSettingsPage-AvVujpIo.js → SystemSettingsPage-DtqsDXbx.js} +1 -1
  41. package/web-app/dist/assets/{TeamsPage-BiT3N4px.js → TeamsPage-CooeoWxR.js} +1 -1
  42. package/web-app/dist/assets/{TemplatesPage-Y_m-dzfd.js → TemplatesPage-CJhTxWrw.js} +1 -1
  43. package/web-app/dist/assets/{TerminalOutput-oPTCcEGy.js → TerminalOutput-DvjfbDxb.js} +1 -1
  44. package/web-app/dist/assets/{activity-DCa9Pi7k.js → activity-DK6KBQKA.js} +1 -1
  45. package/web-app/dist/assets/{bell-Br7Xsjam.js → bell-Cd8tQpN6.js} +1 -1
  46. package/web-app/dist/assets/{bot-BsQKlSP5.js → bot-Y30w8rML.js} +1 -1
  47. package/web-app/dist/assets/{check-Cb49hCzQ.js → check-B5eUl1j9.js} +1 -1
  48. package/web-app/dist/assets/{chevron-left-Cs63FOmY.js → chevron-left-qyvpFqze.js} +1 -1
  49. package/web-app/dist/assets/{circle-alert-C8A636Wl.js → circle-alert-DXtjVPDB.js} +1 -1
  50. package/web-app/dist/assets/{clock-DhlnHjQI.js → clock-dqQis2Q6.js} +1 -1
  51. package/web-app/dist/assets/{cloud-uOYQxGRY.js → cloud-CzTURYO9.js} +1 -1
  52. package/web-app/dist/assets/{code-xml-9O9XlVo9.js → code-xml-DLbw1HTk.js} +1 -1
  53. package/web-app/dist/assets/{database-D9Oek2JS.js → database-BIj38v85.js} +1 -1
  54. package/web-app/dist/assets/{dollar-sign-CHetUBPR.js → dollar-sign-9bVcHIkD.js} +1 -1
  55. package/web-app/dist/assets/{file-code-corner-oTJV_ce4.js → file-code-corner-D57x6xD1.js} +1 -1
  56. package/web-app/dist/assets/{file-plus-vuloOqQB.js → file-plus-BXOrz3T5.js} +1 -1
  57. package/web-app/dist/assets/{globe-DMVdf8bk.js → globe-Cnq8h4Gt.js} +1 -1
  58. package/web-app/dist/assets/{hammer-15WRQJ1o.js → hammer-iiLGlF28.js} +1 -1
  59. package/web-app/dist/assets/{index-SRKz3Imw.js → index-uGGZ1Ij3.js} +2 -2
  60. package/web-app/dist/assets/{layers-C-QQQP9K.js → layers-gcGxxNEN.js} +1 -1
  61. package/web-app/dist/assets/{loader-circle-DqoJevCK.js → loader-circle-B4Ad11xq.js} +1 -1
  62. package/web-app/dist/assets/{lock-B68yfqau.js → lock-DvQNG5RI.js} +1 -1
  63. package/web-app/dist/assets/{package-DAxTtm-S.js → package-BtQBNbq2.js} +1 -1
  64. package/web-app/dist/assets/{plus-BSbdI5QI.js → plus-DKLiMXBZ.js} +1 -1
  65. package/web-app/dist/assets/{refresh-cw-DlvFuI3J.js → refresh-cw-B4Gu8e2m.js} +1 -1
  66. package/web-app/dist/assets/{rotate-ccw-BF03pKE0.js → rotate-ccw-DATSPLYS.js} +1 -1
  67. package/web-app/dist/assets/{scroll-text-QFZ1JdTD.js → scroll-text-GA8EtlY4.js} +1 -1
  68. package/web-app/dist/assets/{server-tyIvmSke.js → server-DT_3kM4V.js} +1 -1
  69. package/web-app/dist/assets/{shield-alert-DHeqbtBK.js → shield-alert-BEaqZEhO.js} +1 -1
  70. package/web-app/dist/assets/{thumbs-up-CRag1Tn5.js → thumbs-up-DVvA-Fas.js} +1 -1
  71. package/web-app/dist/assets/{trash-2-B0n4x-Ds.js → trash-2-BCyT5A9N.js} +1 -1
  72. package/web-app/dist/assets/{trending-down-DC4Aqzso.js → trending-down-BLcNt39o.js} +1 -1
  73. package/web-app/dist/assets/{trending-up-DFG157ke.js → trending-up-BqfBp13A.js} +1 -1
  74. package/web-app/dist/assets/{upload-BdWQ4cWx.js → upload-BxH0rjQU.js} +1 -1
  75. package/web-app/dist/assets/{usePolling-DMgD-dJ5.js → usePolling-CokbBZIQ.js} +1 -1
  76. package/web-app/dist/assets/{user-DXaZeAe4.js → user-C_u9mNZM.js} +1 -1
  77. package/web-app/dist/index.html +1 -1
  78. package/web-app/dist/assets/CockpitPage-DXKAc0jc.js +0 -47
  79. package/web-app/dist/assets/GitHubIssuesPanel-CVIG0uwi.js +0 -7
@@ -1,334 +1,403 @@
1
- # Architect slice cuts (2026-09-27)
2
1
 
3
- Verbatim final reports from the two Architect agents, kept so BOARD rows S-112..S-151 can cite a durable source.
4
2
 
5
- ## 17:10Z cut: S-112..S-131
6
-
7
- Here are 20 new slices, S-112 to S-131. I edited no files. Each red condition below comes from reading or grepping the current source. I did not run any failing command to confirm a red.
8
-
9
- Two rules apply to every moat row. First, the Wall check is the single moat script's `CASE ... PASS` line, not `tests/moat/run.sh`. `pending.txt` is a Captain-owned hot file, and `run.sh` fails the suite when a case that is still listed there passes. So `run.sh` is expected to go red on exactly the named case ID, and the Captain deletes that pending line at merge. Second, any new test file is registered in `tests/run-all-tests.sh` by the Captain, because that file is hot too.
10
-
11
- | S-112 | P8 pending (P8.ablation-runtime-change-load-bearing, P8.ablation-na-docs-only): build the ablation producer to the interface in the p8 script header, writing `.loki/quality/ablation.json` with status load_bearing, not_load_bearing or not_applicable | autonomy/lib/ablation.py (new), tests/test-ablation.sh (new) | HIGH | red: `python3 autonomy/lib/ablation.py --help` fails today (file absent); green: on a fixture repo whose check exercises the changed function, ablation.json has status load_bearing; a docs-only change gives not_applicable with a non-empty reason; the working tree is byte-identical after the run, and after a SIGTERM sent mid-ablation (git status clean, sha256 of every file unchanged) | ready@2026-09-27T17:10Z | It rewrites the user's working tree in place (data-risk class), so restore on signal is required. Contract: tests/moat/p8-load-bearing-proof.sh lines 22-50. The P8 cases go green only once both S-112 and S-113 are merged. Neither half flips a case alone. Do not promote until a wiring slice lands (BACKLOG 34). |
12
- | S-113 | P8 pending (P8.ablation-dead-code-not-sealed): proof-generator copies ablation.json verbatim into facts.ablation, and _compute_headline never returns VERIFIED when status is not_load_bearing | autonomy/lib/proof-generator.py (the facts.ablation collector and _compute_headline only), tests/test-proof-ablation-headline.sh (new) | HIGH | red: on a fixture .loki with a hand-written ablation.json of status not_load_bearing and otherwise green evidence, the generated proof.json headline reads VERIFIED; green: facts.ablation equals the file's keys and the headline is not VERIFIED; an absent ablation.json leaves today's headline unchanged | ready@2026-09-27T17:10Z | Seal work. Independent of S-112 because it reads the file only. Other slices that touched proof-generator.py (S-52, S-59, S-60) are all released. Same BACKLOG 34 caveat: not wired into run.sh. |
13
- | S-114 | P3 pending (P3.check-author-context-excludes-implementation): build `check_author.py context --spec --repo` to interface A, printing spec_sha256, inputs and prompt, with the spec as the only repo input | autonomy/lib/check_author.py (new), tests/test-check-author-context.sh (new) | HIGH | red: `bash tests/moat/p3-the-wall.sh` emits `CASE P3.check-author-context-excludes-implementation FAIL` today; green: the same script emits PASS, and a fixture repo with src/app.py never appears in inputs or prompt | ready@2026-09-27T17:10Z | Contract: tests/moat/p3-the-wall.sh interface A (~lines 70-85). The script is present but not invoked by the pipeline, so do not promote until a wiring slice exists (BACKLOG 34). |
14
- | S-115 | P3 pending (P3.checks-frozen-before-eval): add `checklist-verify.py --freeze`, which records a digest of check definitions only; a later verify on edited definitions exits non-zero with "hash mismatch", and the verifier's own write-back never trips the digest | autonomy/checklist-verify.py, tests/test-checklist-freeze.sh (new) | HIGH | red: `python3 autonomy/checklist-verify.py --checklist x --freeze` is an unknown flag today, and p3-the-wall.sh emits `CASE P3.checks-frozen-before-eval FAIL`; green: that case emits PASS; freezing then verifying twice in a row exits 0 both times; editing one verification spec gives a non-zero exit with "hash mismatch" | ready@2026-09-27T17:10Z | Contract: interface B in the p3 header. Nothing in the pipeline calls --freeze yet (BACKLOG 34 caveat). |
15
- | S-116 | P2 pending (P2.council-inconclusive-cannot-exit-zero, BACKLOG 4): council_evidence_gate must not let inconclusive evidence through, and council_evaluate must not approve on the vote alone | autonomy/completion-council.sh (council_evidence_gate ~1858 and council_evaluate ~4154 only), tests/test-council-inconclusive-no-approve.sh (new) | HIGH | red: `bash tests/moat/p2-honest-verdict.sh` emits `CASE P2.council-inconclusive-cannot-exit-zero FAIL`; green: that case emits PASS; a genuine green-evidence fixture still approves (positive control); every other P2 case is unchanged | ready@2026-09-27T17:10Z | Honest-verdict moat. The case looks bash-only (case_council_inconclusive has no loki_route call). If the builder finds a Bun leg, return BLOCKED and do not touch council.ts. The S-49 and S-17 edits to this file are released. |
16
- | S-117 | P4 pending (P4.seeded-defect-corpus-present, BACKLOG 6 and 9), part 1 of 5: 20 logic-bug seeded defects to the manifest contract at p4-model-freedom.sh lines 65-84 | benchmarks/seeded-defects/cases/SD-0001 to SD-0020 (new), benchmarks/seeded-defects/fragments/logic-bug.json (new) | MEDIUM | red: benchmarks/seeded-defects does not exist; green: the fragment lists 20 entries of category logic-bug with expected_verdict NOT_SEALED; each deterministic check exits non-zero on its fixture (the defect is real); no two fixtures are byte-identical, and a renamed copy of one defect does not count as a new one | ready@2026-09-27T17:10Z | ID ranges are pre-assigned so the five corpus builders cannot collide. The moat script's corpus.py checker (line ~361) is the acceptance oracle once S-122 assembles the manifest. |
17
- | S-118 | P4 corpus part 2 of 5: 20 spec-miss seeded defects | benchmarks/seeded-defects/cases/SD-0021 to SD-0040 (new), benchmarks/seeded-defects/fragments/spec-miss.json (new) | MEDIUM | red: fragment absent; green: 20 spec-miss entries, each fixture with a spec plus code that misses it and a check that fails; unique and non-duplicate as in S-117 | ready@2026-09-27T17:10Z | Same contract as S-117. |
18
- | S-119 | P4 corpus part 3 of 5: 20 test-fitting seeded defects (tests pass by being fitted to wrong code) | benchmarks/seeded-defects/cases/SD-0041 to SD-0060 (new), benchmarks/seeded-defects/fragments/test-fitting.json (new) | MEDIUM | red: fragment absent; green: 20 test-fitting entries; each fixture's own tests pass while a held-out check named in "check" fails; verdict_path set honestly (model where no deterministic check exists) | ready@2026-09-27T17:10Z | Same contract. This is the hardest category to make deterministic. |
19
- | S-120 | P4 corpus part 4 of 5: 20 mock-abuse seeded defects | benchmarks/seeded-defects/cases/SD-0061 to SD-0080 (new), benchmarks/seeded-defects/fragments/mock-abuse.json (new) | MEDIUM | red: fragment absent; green: 20 mock-abuse entries, each with a mock that hides the real defect and a check that exposes it | ready@2026-09-27T17:10Z | Same contract. |
20
- | S-121 | P4 corpus part 5 of 5: 20 security seeded defects (hermetic, no network) | benchmarks/seeded-defects/cases/SD-0081 to SD-0100 (new), benchmarks/seeded-defects/fragments/security.json (new) | MEDIUM | red: fragment absent; green: 20 security entries (injection, path traversal, secret in code and similar), each with a check that fails on the fixture, no live exploit and no network | ready@2026-09-27T17:10Z | Same contract. |
21
- | S-122 | P4 corpus assembler: build_manifest.py merges fragments/*.json into manifest.json with schema loki.seeded-defects/v2 and fails on a duplicate id, a missing category or fewer than 100 defects | benchmarks/seeded-defects/build_manifest.py (new), tests/test-seeded-defects-manifest.sh (new) | LOW | red: script absent; green: on synthetic fragments it writes a valid manifest; a duplicate id, a missing category or 99 defects each exits non-zero with the reason | ready@2026-09-27T17:10Z | Buildable now against fixtures. The Captain runs it once S-117 to S-121 merge, and only then does `CASE P4.seeded-defect-corpus-present PASS` appear. P4.floor-does-not-raise-wrong-pass still needs a real measurement run that no slice covers. |
22
- | S-123 | BACKLOG 20: loki_proof_attestation_check accepts plain http:// JWKS URLs (a MITM controls the key set). Require https, allowing only loopback http | autonomy/loki (loki_proof_attestation_check ~956 and loki_remote_attestation_status, same guard), tests/test-jwks-https-only.sh (new) | MEDIUM | red: `loki proof verify <receipt> --jwks http://example.invalid` attempts the fetch today (source line ~990 accepts http://); green: a non-loopback http:// JWKS reads NOT CHECKED with a reason naming https, without fetching; http://127.0.0.1 still works, so tests/test-remote-attestation-verdict.sh stays green | ready@2026-09-27T17:10Z | Security. The Bun route forwards --jwks to bash (loki-ts/src/commands/proof.ts:646), so there is no TS change. This is the only slice on autonomy/loki. |
23
- | S-124 | BACKLOG 79: a previous session's static-analysis.pass survives iteration 0 and reads as a pass of the exogenous static_analysis gate | autonomy/run.sh (the iteration-0 evidence drop at ~21595 only), tests/test-iteration0-drops-static-analysis.sh (new) | LOW | red: with .loki/quality/static-analysis.pass present and ITERATION_COUNT=0, the file survives the drop block today (it removes only .test-results.iter, unit-tests.pass and test-results.json); green: it is removed, and a mid-session (iteration above 0) static-analysis.pass is kept | ready@2026-09-27T17:10Z | Honest verdict. It does not touch S-100's trusted-push area. This is the only slice on run.sh. |
24
- | S-125 | BACKLOG 76 and 89: done-recognition tests_axis reads a zero-test record ({"passed":0,"failed":0,"total":0}) as green via the failed==0 branch, and ignores failed_count on a pass:true record | autonomy/lib/done-recognition.sh (tests_axis classifier ~434-485 only), tests/test-done-recognition-tests-axis.sh (new) | MEDIUM | red: the zero-test record and {"pass":true,"failed_count":2} each classify green today; green: they classify unknown and red respectively; a real {"pass":true,"exit_code":0,"failed_count":0} stays green | ready@2026-09-27T17:10Z | Honest verdict. Current source read confirms the fall-through at the "failed == 0" line. |
25
- | S-126 | BACKLOG 94 part a: test_all_data_gets_scoped walks server.app.routes, which on FastAPI 0.141 and later never lists the v2 or operator routes, so its v2 coverage is vacuous on CI | tests/dashboard/test_all_data_gets_scoped.py | MEDIUM | red: under FastAPI 0.141 or later, the audited route set contains zero /api/v2 paths while app.openapi() lists them; green: the route set comes from the app router in the way the P7 matcher does it, asserts at least one /api/v2 route was audited, and passes on 0.128 and 0.141 | ready@2026-09-27T17:10Z | Local FastAPI is 0.128.0, so the red must be run on a 0.141 venv (requirements-test.txt resolves it). Avoid vacuity: assert the count is non-zero. |
26
- | S-127 | BACKLOG 94 part b: the same vacuous app.routes walk in test_fleet_observability (~171), test_fleet_retry (~414) and test_memory_read_scope_auth (~94) | tests/dashboard/test_fleet_observability.py, tests/dashboard/test_fleet_retry.py, tests/dashboard/test_memory_read_scope_auth.py | MEDIUM | red: under FastAPI 0.141 or later, each walk sees zero v2 or operator routes; green: each asserts a non-zero count of the route class it audits and passes on both versions | ready@2026-09-27T17:10Z | Reuse the approach from S-126, but do not edit that file. Running in parallel is fine. |
27
- | S-128 | BACKLOG 148: moat baseline detection (read_baselines, unreached_release) cannot see a symlinked pending.txt, because git grep -l and -L both return rc 1 on a mode-120000 blob | tests/moat/run.sh (read_baselines and unreached_release only), tests/test-moat-runner.sh | MEDIUM | red: a fixture repo with a release tag whose tests/moat/pending.txt is a symlink is treated as carrying no baseline (bootstrap), so it is unratcheted; green: that tag is either read as a baseline or refused outright with a named reason; every existing test-moat-runner.sh case stays green | ready@2026-09-27T17:10Z | Moat integrity. It does not touch pending.txt or cases.txt. |
28
- | S-129 | BACKLOG 93: the "zero-exit leader" case in test-hard-deadline-confinement.sh waits only 0.2s for the child PID file, so under CI load the PID is empty and the case fails on correct product behaviour | tests/test-hard-deadline-confinement.sh | LOW | red: the source has `deadline = time.monotonic() + 0.2` (~line 199), and the case fails when run under stress (for example 4 parallel copies with a CPU hog); green: a bounded wait of 10s or more for the PID file, 20/20 green under the same stress, and the rc 125 plus no-mutation assertions unchanged | ready@2026-09-27T17:10Z | Load-flake fix only. Do not weaken the fail-closed assertion. |
29
- | S-130 | S-51 follow-up (BACKLOG 117 remainder): SettingsPage lists Gemini as an active provider (deprecated and removed from the runtime in v7.5.18), and its build date is hardcoded to 2026-03-24 | web-app/src/pages/SettingsPage.tsx | LOW | red: `grep -n "gemini" web-app/src/pages/SettingsPage.tsx` shows a provider entry (~108) and the default providerPriority (~220), and the file contains the literal 2026-03-24 (~1003); green: Gemini is removed from PROVIDERS and the default priority (or marked deprecated and not selectable), the build date comes from build metadata or is dropped, and the web-app typecheck passes | ready@2026-09-27T17:10Z | User-visible. The Captain rebuilds web-app/dist. opencode, missing from the list, can be added the same way if trivial. |
30
- | S-131 | BACKLOG 106 (Bun half): the budget breaker treats unmeasured spend as $0 and never warns; warn (never pause) when a cap is set and spend is unmeasured | loki-ts/src/runner/budget.ts, loki-ts/tests/runner/budget.test.ts | MEDIUM | red: a unit test with a cap of $10 and zero measured iterations gets budget_used 0 and no warning; green: the state marks spend as unmeasured and a warning is emitted, while exceeded stays false and nothing pauses; measured runs are unchanged | ready@2026-09-27T17:10Z | P7-class honesty. The bash half (autonomy/loki cost and check_budget_limit) is a later slice, because autonomy/loki is taken by S-123 and run.sh by S-124. The Captain rebuilds loki-ts/dist. |
31
-
32
- Notes:
33
- 1. The task premise is stale: tests/moat/pending.txt holds 13 entries, not 38. Two of them I did not slice, because they need a CEO decision (both are breaking changes deferred to v10.0.0): P2.fast-verify-inconclusive-not-zero, which is also dispatched through autonomy/loki and so collides with S-123, and P2.verify-exit-contract. P1.verification-metadata-signed is Seal work bigger than 30 minutes.
34
- 2. Risky: S-112 rewrites the user's working tree in place. S-112 to S-115 would promote P3 and P8 as present but not wired (BACKLOG 34 says do not promote until a stub-provider run.sh session records facts.ablation and invokes check-author), so a wiring slice on run.sh has to follow S-124.
35
- 3. CEO decisions: whether to spend 6 slices on the corpus (S-117 to S-122, which prove only P4.seeded-defect-corpus-present, not the floor-vs-top measurement), and S-131's warn-never-pause default. GUARDS 11 and 13 were not cut because they collide with S-99 (v10-guard.sh) and the pulse script. GUARDS 12 (gitconfig-write lint) is the next alternate.
36
-
37
- ## 18:05Z cut: S-132..S-151
38
-
39
- I've cut 20 slices, S-132 to S-151, and checked each one against the current tree. Every row below names something still open in the code or in CI, and no two rows edit the same file. None of them edits a file or block the in-flight reworks own (S-30/S-50, S-96, S-99, S-100, S-123, S-124, S-125, S-126).
40
-
41
- Candidates I dropped because they are already fixed or would not help:
42
- - **BACKLOG 13:** no Docker pin is left.
43
- - **BACKLOG 35:** `tests/test-moat-runner.sh:653` already has the 0-byte case.
44
- - **BACKLOG 37 and 60:** already fixed in `quality_gates.ts`.
45
- - **BACKLOG 53 and 54:** those files already use `-E` everywhere.
46
- - **Pinning pytest-xdist:** every entry in `requirements-test.txt` is unpinned, so pinning one would break the file's convention.
47
-
48
- | S-132 | Velocity: skip the full Tests workflow on a version-bump-only commit whose parent's Tests run succeeded; run it in full otherwise (source 4) | .github/workflows/test.yml (new first gate job, `actions: read`, `needs`/`if` on the heavy jobs), scripts/ci/version-bump-only.sh (new), tests/test-version-bump-only.sh (new) | HIGH | red: on a bump-only fixture commit, test.yml runs every job; a fixture whose `loki-ts/dist` differs by more than the version string is judged eligible by nothing yet. green: bump-only plus parent Tests success skips the heavy jobs; parent pending or red runs the full suite (never waits); any non-allowlisted file runs the full suite; a parity test pulls release.yml's STEP 1 out by its content markers and gets the same eligibility verdict as the new script on at least 6 fixture diffs; mutating the script's allowlist turns the parity test red | ready@2026-09-27T18:05Z | Why HIGH: a Tests run whose jobs are all skipped concludes success, and required-ci rule (a) accepts success at the release SHA without looking at the parent. So the gate job itself must confirm the parent's Tests run succeeded, and its eligibility must be no looser than release.yml STEP 1. Never skip the version-bump consistency step (test.yml:81). Do not edit release.yml; deduplicating STEP 1 is a later slice. What it buys: runner capacity, plus no flaky failure at the release SHA (rule (b)) can block a release whose parent is green. The Captain registers the new test. |
49
- | S-133 | Velocity: make the Release workflow's required-ci poll faster (source 4), and fix the stale publish-npm comment S-85's review flagged as non-blocking (source 1) | .github/workflows/release.yml (the `sleep 30` in the STEP 2 poll loop, and that one comment in publish-npm) | LOW | red: `grep -n 'sleep 30' release.yml` matches inside the required-ci loop, and the publish-npm comment names publish-docker for the dist assert. green: poll interval is 10s or less, the 2400s deadline is unchanged, the comment names publish-docker-build, actionlint is clean | ready@2026-09-27T18:05Z | Token budget: 2 API calls per iteration at 10s is 12 a minute, at most 480 across the 40-minute deadline, against GITHUB_TOKEN's shared 1000 an hour per repo. Keep it at 10s or more. Edit STEP 2 only; S-132's parity test reads STEP 1 by content markers. Ships alone, since it touches release.yml (D22). |
50
- | S-134 | Velocity: shorten the slowest Tests shard by re-measuring shard-durations.tsv (it is from run 36319020918, now stale), plus the duration-table drift detector S-81's review listed as a follow-up (sources 4 and 1) | tests/shard-durations.tsv, tests/test-shard-durations-drift.sh (new) | LOW | red: Tests run 36338540948 shows shard 2/8 at 325s against shard 7/8 at 124s; the drift test fails today on any registered suite missing from the table, or any table row naming no registered suite. green: the table is rebuilt from the `END: <name> (Ns)` lines of all 8 shard logs of a recent green run, the drift test passes, and the next real Tests run's max shard is under 250s | ready@2026-09-27T18:05Z | Measure the whole workflow's wall clock, not just the max shard: once rebalanced, the 195s Moat suite likely becomes the floor. The floor for any single shard is ShellCheck at 140s. The Captain registers the drift test. |
51
- | S-135 | Velocity: run tests/test-trust-core-tests-detect.sh's 90 probe_case mutations in parallel (130s in shard 1, the second-slowest suite) (source 4) | tests/test-trust-core-tests-detect.sh | MEDIUM | red: 130s on CI and one probe at a time. green: bounded parallel probes under 50s locally; the case count stays exactly 90 and every case still executes; a deliberately removed guard still turns its case red; 5 runs under 4-way CPU load with 0 flakes | ready@2026-09-27T18:05Z | First confirm probe_case mutates a private copy. If it edits the shared tree in place, parallel runs corrupt each other and the slice changes shape. Its anchors point into completion-council.sh (line 196 loops council_checklist_gate/council_heldout_gate) and probably into the run.sh failure-count awk, so rebase after S-141 and S-140 if they land first. |
52
- | S-136 | Velocity: tests/test-select-tests.sh hit the 60s D27 cap (rc=124) on S-49's and S-91's train 4 checks (source 1) | tests/test-select-tests.sh | MEDIUM | red: `timeout 60 bash tests/test-select-tests.sh` returns rc=124. green: it finishes in under 30s with rc=0, still 38/38 cases, and a mutation of scripts/select-tests.sh (drop one rule) still turns it red | ready@2026-09-27T18:05Z | Coverage must not drop: assert the case count, not just the time. Do not touch scripts/select-tests.sh or tier-a.yml (S-96). |
53
- | S-137 | S-81 review follow-up: shard-coverage check T4 recomputes a static count instead of running the real runner (source 1) | tests/test-shard-coverage.sh | LOW | red: a mutation that makes run-all-tests.sh's LPT assignment drop one suite from shard 0 leaves T4 green. green: T4 enumerates every shard of the real runner at n=4 and n=8 through a dry-run/list path, the union equals the registration list, and the same mutation turns it red | ready@2026-09-27T18:05Z | If run-all-tests.sh has no list mode, stop and report. Adding one would touch a Captain-owned hot file. |
54
- | S-138 | GUARDS 12 (no guard yet): a lint so test fixtures never write the operator's real ~/.gitconfig (source 3) | tests/test-no-ambient-gitconfig-writes.sh (new) | LOW | red: a planted fixture with `git config --global url.x.insteadOf y` and no `HOME=`/`GIT_CONFIG_GLOBAL=` isolation before it is flagged, rc=1. green: the lint passes on the current 5 files that do global writes (test-trusted-push-agent-config, -acceptance-resume-idempotence, -enterprise-resilience, -branch-lifecycle, plus the moat p6/p9 scripts) only if each isolates first; any real offender is named, not allowlisted in silence | ready@2026-09-27T18:05Z | If a current file really does write the ambient config, report it; do not fix it inside this slice. The Captain registers the lint and updates GUARDS 12. |
55
- | S-139 | GUARDS 13 (pending) plus BACKLOG 136: pulse flags a `released@` row stamped after npm's newest publish time (RELEASED_AHEAD_OF_NPM), and reports UNKNOWN when the local release tag disagrees with npm's latest version (sources 3 and 2) | scripts/v10-pulse.sh, tests/test-v10-pulse.sh | MEDIUM | red: a fixture BOARD with a row stamped `released@15:54Z` while the PULSE_NPM_CMD fixture's latest publish is 15:00Z raises no violation; a local tag v9.54.2 against npm latest 9.55.0 prints a confident "N commits since". green: both fixtures give the new violation and an UNKNOWN respectively, existing pulse tests stay green, runtime stays under 5s | ready@2026-09-27T18:05Z | Reuse the existing `npm view loki-mode time --json` result and its cache. No new network call, to stay inside S-104's 5s budget. Chosen over a board-row-status precondition because the pulse runs on every prompt, while nothing shows the Captain writes `released` through v10-ops.sh. |
56
- | S-140 | BACKLOG 103: jest "Test Suites: 1 failed" alongside "Tests: 2 passed", and vitest "Test Files 1 failed", still record as passed (source 2) | autonomy/run.sh (the BACKLOG 73/111 failure-count awk near line 14240 only), plus the existing S-45 summary-parser test | MEDIUM | red: fixture output with "Test Suites: 1 failed, 1 total" and "Tests: 2 passed" records failed_count 0 and pass true; same for vitest "Test Files 1 failed \| 2 passed". green: both count at least 1 failed and the gate fails; a pure-green jest/vitest summary is still a measured 0; the trust-core suite still passes | ready@2026-09-27T18:05Z | Far from S-100's region (on_run_complete ~6012, trusted push) and S-124's (iteration 0, load_state ~21470). Run the trust-core suite in the wall and fix any anchor this breaks. |
57
- | S-141 | S-49 follow-up: council verdict readers outside the 3 fixed functions still run bare `python3 -E` (~1425, ~1564 gate results; ~4232 aggregate_result) (source 1) | autonomy/completion-council.sh (those 3 call sites only), tests/test-council-gate-readers-pth.sh (new) | HIGH | red: a user-site `.pth` planted under a scratch HOME flips each of the 3 readers' verdicts (as S-49 reproduced). green: all 3 go through the resolved `-I -S` interpreter helper, fail closed when no interpreter is found, the `.pth` has no effect, and a mutation reverting one site turns the test red; the trust-core suite passes | ready@2026-09-27T18:05Z | A standalone suite, not a moat case, because tests/moat/cases.txt is a hot file. The Captain registers the suite. Line numbers drift, so find the sites by grep. |
58
- | S-142 | S-48 follow-up: convergence-floor NEGATIVE-B passes even without the `runner!='none'` guard (source 1) | tests/test-council-convergence-floor.sh | LOW | red: removing the runner guard from the fast-path check (a scratch-copy mutation) leaves every case green. green: a new NEGATIVE-C writes the legacy shape `runner:"none", pass:true`, returns 1, and turns red under that mutation | ready@2026-09-27T18:05Z | Test-only. Confirm the guard still exists before writing; if it was removed on purpose, report that instead. |
59
- | S-143 | BACKLOG 117 remainder / S-51 follow-ups: Gemini, Railway, stale gate counts and "Q2 2026 Current" copy (source 2) | web-app/src/components/Roadmap.tsx, DocsSidebar.tsx, ContextualHelp.tsx, ProductTour.tsx, CostEstimator.tsx, web-app/src/pages/SystemSettingsPage.tsx | LOW | red: `grep -il -E 'gemini\|railway\|9 (automated )?quality gates\|10 quality gates\|Q2 2026'` matches all 6 files. green: 0 matches in them; gate count is 8; deploy targets are Vercel, Netlify and GitHub Pages only | ready@2026-09-27T18:05Z | Copy only. If CostEstimator's Gemini entry is pricing logic, remove the option and note it. ProjectWorkspace.tsx is out of scope. The Captain rebuilds web-app/dist. |
60
- | S-144 | S-51 follow-up: Footer.tsx shows v9.55.0 against VERSION 9.66.0 and is not in the bump list (source 1) | scripts/release.sh, tests/test-release-sh.sh, web-app/src/components/Footer.tsx | LOW | red: Footer.tsx holds v9.55.0 while VERSION is 9.66.0, and a `--bump-only` fixture run leaves Footer unchanged. green: `--bump-only` rewrites the Footer version slot, and the test asserts it for that file by name | ready@2026-09-27T18:05Z | WhatsNew.tsx is deliberately excluded. Its modal opens when `lastSeen !== CURRENT_VERSION`, so bumping it automatically would pop the same stale modal for every user at every release (3-5 an hour). Keying it to its own content version is a separate question (see the end list). |
61
- | S-145 | BACKLOG 118: GET /api/checklist/waivers answers 200 `{waivers: []}` when waivers.json is corrupt, so a read error looks like "no waivers" (source 2) | dashboard/server.py (get_checklist_waivers only), tests/dashboard/test_checklist_waivers_read_error.py (new) | LOW | red: a corrupt waivers.json returns 200 with an empty list. green: it returns 500 with an error body, matching the POST sibling at ~10431; a missing file still returns 200 with an empty list | ready@2026-09-27T18:05Z | Survey the consumers first (dashboard-ui checklist viewer, standalone): each must show "Could not load" on 500, not an empty state. Note which consumers were checked in the wall. |
62
- | S-146 | BACKLOG 118: the dashboard shell has two elements with `id="budget-banner"` (source 2) | dashboard-ui/scripts/build-standalone.js | LOW | red: `grep -c 'id="budget-banner"'` returns 2. green: returns 1, and the banner script still finds and updates the remaining element (a node or DOM check in the builder's evidence) | ready@2026-09-27T18:05Z | While in the file, check the other two 118 items (the "Newest first" reversal at ~2510); fix only if trivial, otherwise report. The Captain rebuilds dashboard/static/index.html. |
63
- | S-147 | BACKLOG 118: `loki metrics --json` sends `tokens.total` 0 and `total_iterations` 0 when nothing was recorded, and invents `time_saved_hours = iterations*15/60` (source 2) | autonomy/loki (cmd_metrics only), tests/test-metrics-json-unmeasured.sh (new) | MEDIUM | red: on an empty .loki, `loki metrics --json` prints total 0, iterations 0 and a numeric time_saved_hours. green: null (unknown) for unrecorded values, time_saved_hours removed or null, real recorded values unchanged | ready@2026-09-27T18:05Z | cmd_metrics (~28481) is far from S-123's attestation region (~956); rebase if S-123 lands first. Survey consumers of the JSON (dashboard, docs) before changing 0 to null. The Captain registers the test. |
64
- | S-148 | BACKLOG 118: trust.html says "Stable: no significant change" when every axis lacks the 2+ runs of history needed (source 2) | dashboard/static/trust.html | LOW | red: an API fixture where every axis has `insufficient: true` renders the "Stable" headline. green: it renders "Not enough history yet"; a fixture with real flat axes still says "Stable" (pull the headline into a named function and check it with a small node assertion) | ready@2026-09-27T18:05Z | Hand-written page, not a generated bundle. The `delta: 0.0` on an insufficient axis in trust_trajectory.py is out of scope; note it for later. |
65
- | S-149 | BACKLOG 118: prompt_optimizer.get_current_version returns never-ran zeros (version 0, based_on_sessions 0), and a corrupt latest file is folded into the same default (source 2) | dashboard/prompt_optimizer.py, tests/dashboard/test_prompt_optimizer_sentinels.py (new) | LOW | red: a corrupt latest file and a missing one return the identical zero dict. green: missing returns an explicit never-ran marker (nulls); corrupt returns an error marker distinct from never-ran | ready@2026-09-27T18:05Z | Survey the endpoint and panel consumers first so the new fields render as unknown, not crash. |
66
- | S-150 | BACKLOG 118: migration_engine.get_progress reports 0/0 features and steps when the features or plan file is corrupt or missing (source 2) | dashboard/migration_engine.py (get_progress only), tests/dashboard/test_migration_progress_unknown.py (new) | LOW | red: a corrupt plan.json gives `steps_total 0, steps_completed 0`, identical to a real empty plan. green: corrupt or missing gives null counts plus a reason; a real empty plan still gives 0 | ready@2026-09-27T18:05Z | Survey consumers (loki-migration-dashboard.js, server endpoint) to make sure null renders as unknown. |
67
- | S-151 | BACKLOG 118: loki-analytics heatmap counts days before the returned activity window as 0 activities (source 2) | dashboard-ui/components/loki-analytics.js (_computeHeatmap and the cell render only) | LOW | red: a fixture with activity starting 10 days ago renders the previous ~355 days as "0 activities". green: days before the earliest returned entry (or the API's window start) render as "no data", while days inside the window with no activity stay 0 | ready@2026-09-27T18:05Z | The Captain rebuilds dashboard/static/index.html. Different file from S-146. |
68
-
69
- **Risks**
70
- - **Registration is on the Captain.** 8 rows add a new test file (S-132, S-134, S-138, S-141, S-145, S-147, S-149, S-150). Each needs the Captain to register it in `tests/run-all-tests.sh`, or it never runs in CI.
71
- - **Rebuilds are on the Captain.** S-143 needs web-app/dist rebuilt; S-146 and S-151 need dashboard/static rebuilt.
72
- - **Test anchors, not files, couple S-135, S-140 and S-141.** Trust-core probes anchor into completion-council.sh and probably into the run.sh failure-count awk. Merge S-140 and S-141 first, and each of them must run the trust-core suite in its wall. S-135's first step is confirming probes use a private copy.
73
- - **Two rows touch the release gate.** S-132 is the highest-risk slice: an all-skipped Tests run reads as success at the release SHA. It needs a HIGH review focused on the parent-green check and on eligibility never being looser than release.yml STEP 1. S-133 also touches release.yml, so under D22 it ships in its own train.
74
- - **Same-file neighbours.** S-140 (run.sh) and S-147 (autonomy/loki) share a file with in-flight S-100/S-124 and S-123, in regions far from theirs. Expect rebases, not conflicts.
75
- - **S-134's gain only shows on a real CI run.** The first Tests run after merge is the evidence; the Moat suite (195s) may become the new floor.
76
- - **Changing zeros to null or 200 to 500 changes response shapes.** S-145, S-147, S-149 and S-150 must name the consumers they checked, or a panel can crash where it used to show a false zero.
77
-
78
- **Held for a CEO decision or waiting on another slice (not in the 20)**
79
- - S-112 and S-117 to S-122 (P4 corpus and ablation producer): already held.
80
- - BACKLOG 4: breaking exit-code renumber for `loki verify` (v10.0.0).
81
- - BACKLOG 18: Caveman bootstrap and its global `~/.claude` hook default-on; changing the default is a product decision.
82
- - BACKLOG 121: browser-only settings (plain-text API keys in localStorage, unwired "Budget limit" and "Auto-deploy"): remove or wire is a product decision.
83
- - BACKLOG 77: whether Bun should write its own test-results freshness marker.
84
- - BACKLOG 123: `audit.py verify` reporting valid on nothing checked; the cross-chain verifier reads it, so it needs its own review.
85
- - WhatsNew.tsx version: stays stale until someone decides what should trigger the modal.
86
- - GUARDS 11 (scratchpad glob `rm`): a helper nobody is forced to call blocks nothing. The real rule belongs in `scripts/v10-guard.sh`, so cut it after S-99 lands.
87
- - Waiting on a slice in this set: BACKLOG 127 remainder (`run.sh` auto_capture_episode, `cd "$PROJECT_DIR"` with no unset guard, ~21361) after S-140; BACKLOG 38 after S-141; BACKLOG 133 after S-139.
88
-
89
- I edited no files. The only temp file was a log saved under `$TMPDIR` and deleted in the same command. I started no background processes.
90
-
91
- ## 19:10Z cut (S-154..S-173)
92
-
93
- I edited no files and started no background processes. Each red condition below comes from reading or grepping the current tree at e952e1ae. Two numbers were measured on this host (load average about 9):
94
- - `python3 tests/lib/scan-unreachable-shipped.py` ran in 47s, rc 0.
95
- - `EVENT_NAME=push SHA=8d17be2b bash scripts/ci/version-bump-only.sh check` printed `normalizer: not on the allowlist: web-app/src/components/Footer.tsx`, rc 1.
96
-
97
- `tests/test-sentrux-init-rules.sh` ran once for timing. It removed its own mktemp dir through its EXIT trap: `ls -d /tmp/loki-test-sentrux-init-*` found 0 entries. The working tree shows only the Chief of Staff's uncommitted BOARD.md and TODO.md edits.
98
-
99
- Rules that apply to every row:
100
- - No file set includes a hot file.
101
- - Any new test file is registered in `tests/run-all-tests.sh` by the Captain.
102
- - Dashboard-ui rows need the Captain to rebuild `dashboard/static/index.html`. Web-app rows need a `web-app/dist` rebuild.
103
- - UI walls follow the pattern already in the repo. Web-app rows pull the copy decision into a named pure function tested with `node --test`, like `web-app/src/cockpit/useCockpitState.derive-view.test.mjs`. Dashboard-ui rows drive the real component class against a DOM stub, like `dashboard-ui/tests/loki-empty-vs-error.node.test.mjs`.
104
- - Each UI wall includes a negative assertion: the error render must not contain the empty-state sentence. A grep for the absence of a string is not accepted as a wall.
105
-
106
- ### S-154: prune-worktrees.sh treats a cherry-picked branch as merged (GUARDS 5, TODO item 8)
107
- - Files: scripts/prune-worktrees.sh, tests/test-prune-worktrees.sh (new)
108
- - Tier: MEDIUM (it removes worktrees)
109
- - Red: the script counts a worktree as merged only through `git merge-base --is-ancestor`. The Chief of Staff lands slices by cherry-pick, so no slice branch is ever an ancestor of main, and the dry run lists 0 of them. That is why pruning is still manual (TODO item 8: "count now 7 of 15"). No test file for the script exists (`ls tests | grep -i prune` shows only checkpoint tests).
110
- - Green, on a fixture repo:
111
- - A branch whose commits are all patch-equivalent on main (`git cherry main <branch>` shows no `+` lines) is listed as removable.
112
- - A branch with one `+` commit is kept.
113
- - A locked worktree is kept, and so is a dirty one.
114
- - `--apply` removes only through `git worktree remove`.
115
- - The test also greps the script and fails on any `stat`, `mtime`, `-mmin` or `-newer` token, so GUARDS 5 finally has a checked-in test.
116
- - Wall: `bash tests/test-prune-worktrees.sh` exits 0 with every case PASS. A mutation that drops the `git cherry` branch makes the cherry-picked fixture case FAIL.
117
-
118
- ### S-155: shipped-module reachability scan under 10s, same verdicts (velocity)
119
- - Files: tests/lib/scan-unreachable-shipped.py (referenced_from_runtime and its caller only)
120
- - Tier: LOW
121
- - Red: the scan takes 47s locally and 95s on CI (`tests/shard-durations.tsv`: "shipped modules have a recorded reachability verdict 95"). It is the 4th slowest suite. Every module runs one large alternation regex over every corpus file, including a `(node|bun)\s+[^\n]*` arm, and nothing filters the files first.
122
- - Green:
123
- - The builder first measures where the time goes (regex or I/O) and puts the numbers in the commit.
124
- - Then add a substring prefilter: skip any corpus file that does not contain the module's stem. This is sound because every pattern alternative contains the stem literally. Also compile the regex once per module.
125
- - stdout and rc are byte-identical to a capture taken before the change.
126
- - Wall: the scan runs in under 10s wall time. `bash tests/test-no-unreachable-shipped.sh` shows 4 passed, 0 failed. A mutation that removes one real require of a listed module still gets reported (rc 1).
127
-
128
- ### S-156: auto-capture shadow_write runs the agent repo's memory package when PROJECT_DIR is unset, and splices importance into python source (BACKLOG 127 remainder)
129
- - Files: autonomy/run.sh (the managed-memory auto-capture block, found by grepping `shadow_write`, about line 21380-21396 only), tests/test-autocapture-shadow-write-guard.sh (new)
130
- - Tier: HIGH (code execution on a verdict-adjacent path)
131
- - Red: `cd "$PROJECT_DIR" 2>/dev/null && ... python3 -m memory.managed_memory.shadow_write` has no `-n PROJECT_DIR` guard. On bash 3.2, `cd ""` succeeds silently and the cwd stays inside the agent repo. Also, `python3 -c "print('yes' if float('$_ep_imp') >= 0.6 else 'no')"` interpolates an episode-file value straight into the python source.
3
+ ## 20:12Z cut (S-194..S-213)
4
+
5
+ **Checked before cutting.** All reads are on main at 8ba7024d (v9.73.0).
6
+ - **Alternates re-read against the code.**
7
+ - **A1 (BACKLOG 108) is real.** `create_session_pr`'s `LOKI_AUTO_PR=1` branch calls `_loki_trusted_push` and never reads `.loki/state/agent-committed-user-files.z`. The only references to that file are the writer comment and the writer itself, inside `_loki_untrack_agent_committed_user_files`.
8
+ - **A2 (BACKLOG 17) is real.** The "Notify dashboard of active project directory" block, about 24477 today, posts to `/api/focus` behind `command -v curl` only. It has no `ENABLE_DASHBOARD` check.
9
+ - **A3 is real and larger than the card says.** run.sh 211 and 239 always set PROJECT_DIR to the Loki install root. So the BACKLOG 127 "guard when unset" would do nothing. The actual defect is that `council_managed_should_stop` runs `git diff --stat` in Loki's own tree, not the target project. Its three readers are still bare `python3 -E`, and p2-honest-verdict.sh (about 858) says so.
10
+ - **A4 (BACKLOG 51) is real.** `grep -c PYTHONUSERBASE autonomy/loki` prints 0. The NOT CHECKED text says "Install python3 cryptography" even to a user who installed it through PYTHONPATH or PYTHONUSERBASE, which `-E` ignores.
11
+ - **A5 is split.**
12
+ - BACKLOG 145 sits in the Array.from generator arm, about 1934-1994. That does not overlap S-180's helper-return arm (about 2081-2170).
13
+ - BACKLOG 147 edits `HELPER_LOCAL_DECL_TMPL`, which is in S-180's region, so it is held.
14
+ - **New class found: every `-E`-only verdict reader outside completion-council.sh (BACKLOG 53 and 54).** These are:
15
+ - autonomy/lib/done-recognition.sh: 16 sites.
16
+ - autonomy/council-v2.sh: 7 sites.
17
+ - autonomy/lib/voter-agents.sh: 3 sites.
18
+ - autonomy/lib/proof-check.sh: 3 sites.
19
+ - autonomy/prd-checklist.sh: about 11 sites.
20
+ - loki-ts/src/commands/proof.ts:606, the Bun `loki proof verify` path.
21
+
22
+ All of them run PATH `python3 -E`. BACKLOG 134 showed that a user-site `.pth` still loads under `-E`.
23
+
24
+ On `-S` compatibility:
25
+ - The imports I read are stdlib only: swarm/sycophancy.py, autonomy/checklist-verify.py, and autonomy/lib/proof-verify.py (which adds its own directory to sys.path at line 76). So `-I -S` should hold for those.
26
+ - Each builder still audits the imports of the rest of its sites, including the prd-checklist oracle heredoc.
27
+
28
+ Existing tests source each of these files on their own, so each file needs the guarded helper copy. For voter-agents.sh, `.` at completion-council.sh 4245 sources it into the parent shell.
29
+ - **council-v2 fails open on sycophancy.** A detector crash yields `0.000`, then `should_challenge=no`. A unanimous approve then skips the devil's advocate.
30
+ - **Cockpit remainder (BACKLOG 114).**
31
+ - `FinalActions` receives `git=null` after a failed status fetch (the `settle` fallback) and still says "The working tree is clean" and "Nothing to push".
32
+ - A failed `getStatus` (the swallow at useCockpitState.ts:202) makes Pause, Resume and Stop read "No run in progress".
33
+ - A failed `getChecklist` (the swallow at 204) renders "No gate results recorded."
34
+ - **Already handled, so dropped:**
35
+ - BACKLOG 31: proof-verify maps unverifiable drift to 2.
36
+ - BACKLOG 38: `no_pass_recorded`.
37
+ - BACKLOG 49: the `--jwks` output is patched.
38
+ - BACKLOG 16 and 85: `PYTHONDONTWRITEBYTECODE` in `enforce_test_coverage`, plus the py_compile rewrite.
39
+ - BACKLOG 133: `PULSE_MOAT_RESULT` carries a `.sha` sidecar.
40
+ - BACKLOG 140: the environment-variables.md row names all four tokens and both routes.
41
+ - BACKLOG 13: release.sh bumps INSTALLATION.md at 320.
42
+ - BACKLOG 22: the resource monitor backgrounds its sleep.
43
+ - BACKLOG 26: test-bun-parity-disk-tolerance.sh.
44
+ - The unreadable-waivers banner in loki-checklist-viewer is already pinned in loki-unmeasured-panels-honesty.node.test.mjs:319.
45
+ - The GitPanel badge on a null fetch renders nothing.
46
+ - PhaseVisualizer: its only caller passes 'idle'.
47
+ - **Dropped on merit: BACKLOG 69 (snapshot hash size cap).**
48
+ - A size-plus-mtime fingerprint is spoofable, which weakens `preexisting_modified`.
49
+ - It changes the sealed `.sha.z` entries.
50
+ - Nobody has measured the stall.
51
+
52
+ ### S-194: LOKI_AUTO_PR pushes a branch whose history still holds user files (BACKLOG 108, A1)
53
+ - Files:
54
+ - autonomy/run.sh: `create_session_pr` only, from its head to the `_loki_trusted_push` call. Anchor on the function name, because S-175 shifts line numbers when it merges.
55
+ - tests/test-auto-pr-agent-committed-refuse.sh (new)
56
+ - Tier: HIGH (secret exposure)
57
+ - Region: this does not overlap S-175 (`enforce_test_coverage`) or S-195 (the focus notify block).
58
+ - Red:
59
+ - With a non-empty agent-committed-user-files.z and `LOKI_AUTO_PR=1`, the stubbed `_loki_trusted_push` gets called.
60
+ - The advisory path prints `git push -u` advice with no cleanup step.
132
61
  - Green:
133
- - The block is skipped when PROJECT_DIR is empty.
134
- - `importance` is passed through argv or env and never spliced into source.
135
- - A planted `memory/managed_memory/shadow_write.py` in the cwd writes no marker file, run under `/bin/bash` and under bash 5.
136
- - An importance value of `0.7'); open('PWNED','w'); ('` writes no PWNED file.
137
- - The builder names who writes `episode_path_file`.
138
- - Wall: `bash tests/test-autocapture-shadow-write-guard.sh` exits 0, and `bash tests/test-trust-core-tests-detect.sh` still passes. Reverting either guard makes its case FAIL.
139
-
140
- ### S-157: council member vote and convergence floor read pass:true as green whatever failed_count says (BACKLOG 98 remainder, 89)
141
- - Files: autonomy/completion-council.sh (council_evaluate_member's test-results parser and `_council_convergence_evidence_green` only), tests/test-council-failed-count-honesty.sh (new)
142
- - Tier: HIGH (council verdict)
143
- - Red: both parsers test `passed is True` and never read `failed_count`. The convergence reader is `print('yes' if (runner != 'none' and passed is True and d.get('status') != 'no_tests_run') ...`. So `{"runner":"jest","pass":true,"failed_count":2}` reads green at both sites. The evidence gate at ~2014 already treats failed_count as winning.
62
+ - When the record is non-empty:
63
+ - Recompute the fork the way `_loki_untrack_agent_committed_user_files` does: the merge base with base-branch.txt, else session-start-sha.
64
+ - Print `git reset --soft <fork> && git commit` before any push advice.
65
+ - Under `LOKI_AUTO_PR=1`, refuse the push: `log_warn` naming the files, and return 1.
66
+ - An empty or absent record leaves both paths byte-identical to today.
67
+ - Gate: the builder first reproduces the stub push call on main. If it does not reproduce, the slice closes as handled.
68
+ - Test rule: `cd` to scratch before sourcing run.sh, and assert that no `.loki/state/provider` appears in the repo (BACKLOG 126).
69
+ - Wall: `bash tests/test-auto-pr-agent-committed-refuse.sh` exits 0. Its legs:
70
+ - Auto path with a record: no push, rc 1, and the cleanup line printed.
71
+ - Advisory path with a record: the cleanup line appears before `git push -u`.
72
+ - Empty record: the push happens.
73
+ - Removing the record read in a scratch copy fails the first leg.
74
+ - `bash tests/test-trusted-push-agent-config.sh` exits 0.
75
+
76
+ ### S-195: the /api/focus POST fires with the dashboard disabled (BACKLOG 17, A2)
77
+ - Files:
78
+ - autonomy/run.sh: the "Notify dashboard of active project directory" block only.
79
+ - tests/test-focus-post-dashboard-off.sh (new)
80
+ - Tier: MEDIUM
81
+ - Region: this does not overlap S-175 or S-194.
82
+ - Red: with `ENABLE_DASHBOARD=false`, a curl shim on PATH records one POST to `/api/focus`.
144
83
  - Green:
145
- - At both sites, failed_count above 0 means not green, using the same rule as the evidence gate.
146
- - A real `{"pass":true,"failed_count":0}` stays green (positive control).
147
- - `runner=none` behaviour is unchanged.
148
- - Wall: `bash tests/test-council-failed-count-honesty.sh` exits 0, and `bash tests/test-trust-core-tests-detect.sh` and `bash tests/test-council-convergence-floor.sh` pass. A mutation that reverts either site makes its case FAIL.
149
-
150
- ### S-158: per-run cost_partial is dropped by /api/cost/timeline runs[] and /api/proofs (BACKLOG 118)
151
- - Files: dashboard/server.py (the runs.append in `_compute_cost_timeline` about line 8351, and the /api/proofs row builder about line 12538 only), dashboard/static/cost.html, dashboard/static/proofs.html, tests/dashboard/test_cost_partial_surfaced.py (new)
152
- - Tier: MEDIUM (response shape change)
153
- - Red: `autonomy/lib/efficiency_cost.py:209` writes `cost.cost_partial` into proof.json, but both endpoints copy only `cost.usd`. A partly priced run therefore shows as a complete total.
84
+ - Guard on the runtime `ENABLE_DASHBOARD`, which run.sh sets false at about 19288. Do not guard on `LOKI_DASHBOARD`.
85
+ - Test: extract the block by its comment anchor, as test-autocapture-shadow-write-guard.sh does. No full run.sh source.
86
+ - Wall: `bash tests/test-focus-post-dashboard-off.sh` exits 0.
87
+ - false: the shim log is empty.
88
+ - true: exactly one POST.
89
+ - Removing the guard in a scratch copy fails the false leg.
90
+
91
+ ### S-196: council_managed_should_stop reads Loki's install tree for its diff and runs -E readers (A3)
92
+ - Files:
93
+ - autonomy/completion-council.sh: `council_managed_should_stop` only.
94
+ - tests/moat/p2-honest-verdict.sh: `case_council_readers_no_user_site_pth`, plus the about-858 comment in `case_council_readers_not_shadowed`.
95
+ - tests/test-council-managed-diff-target.sh (new)
96
+ - Tier: HIGH (moat)
97
+ - Red:
98
+ - A stub `providers.managed` records `_CC_DIFF`. It shows install-tree paths, not the target project's change.
99
+ - A planted user-site `.pth` forges the test_summary read.
154
100
  - Green:
155
- - Both endpoints carry `cost_partial` (a missing key becomes false).
156
- - Both pages render a partial run as "at least $X".
157
- - A fully priced run renders as today.
158
- - The builder names every consumer of the two endpoints that was checked.
159
- - Wall: `python3 -m pytest tests/dashboard/test_cost_partial_surfaced.py -q` passes. A fixture proof.json with `cost_partial: true` gives `runs[0].cost_partial == true`, and a mutation that drops the key fails the test.
160
-
161
- ### S-159: NLSearch says "No results found" when the search request failed (BACKLOG 114)
162
- - Files: web-app/src/components/NLSearch.tsx, web-app/src/components/NLSearch.state.test.mjs (new)
101
+ - `diff_summary` comes from `${TARGET_DIR:-.}`, the same root as `loki_dir` and `LOKI_TARGET_DIR`.
102
+ - The two inline readers and the heredoc run through `_loki_snapshot_py_tool -I -S`.
103
+ - If no interpreter resolves, test_summary is empty, pending is `[]`, and the function returns 1 (the Bash voting fallback). It never returns 0.
104
+ - The new leg lands in the same commit as the fix, because the case is already proven.
105
+ - Gate: reproduce the install-tree diff with the stub first. Reuse S-173's stub.
106
+ - Wall:
107
+ - `bash tests/moat/p2-honest-verdict.sh` prints `CASE P2.council-readers-no-user-site-pth PASS` and `CASE P2.council-readers-not-shadowed PASS`.
108
+ - `bash tests/test-council-managed-diff-target.sh` exits 0: a file changed only in the target appears in `_CC_DIFF`.
109
+ - Reverting the readers to `python3 -E` in a scratch copy makes the pth case FAIL.
110
+ - `bash tests/moat/run.sh` reports no rule failed.
111
+
112
+ ### S-197: the NOT CHECKED text should name PYTHONPATH and PYTHONUSERBASE (BACKLOG 51, A4)
113
+ - Files:
114
+ - autonomy/loki:
115
+ - `loki_remote_verify_receipt`'s attestation NOT CHECKED branch only.
116
+ - `cmd_proof verify`'s `--jwks` NOT CHECKED echo only.
117
+ - tests/test-proof-verify-jwks.sh (case 4 only)
163
118
  - Tier: LOW
164
- - Red: the catch at ~140 does `setResults([])` ("show empty results gracefully"), so a failed API call renders "No results found. Try a different query." (~261).
119
+ - Region: this does not overlap S-179 (`cmd_cost`).
165
120
  - Green:
166
- - The failure is kept in state and renders "Search failed: <reason>".
167
- - A real empty result still says "No results found".
168
- - The branch decision lives in an exported pure function.
169
- - Wall: `node --test web-app/src/components/NLSearch.state.test.mjs` passes, including the negative assertion. `cd web-app && npx tsc -b` exits 0.
170
-
171
- ### S-160: CommandPalette file search failure reads as no results (BACKLOG 114)
172
- - Files: web-app/src/components/CommandPalette.tsx, web-app/src/components/CommandPalette.state.test.mjs (new)
173
- - Tier: LOW
174
- - Red: the catch at ~171 does `setFileResults([])`, so a failed `api.searchFiles` renders "No results found" (~392).
175
- - Green: the error state renders "File search failed", and a genuine empty result keeps "No results found".
176
- - Wall: `node --test web-app/src/components/CommandPalette.state.test.mjs` passes, and `cd web-app && npx tsc -b` exits 0.
177
-
178
- ### S-161: ProjectsPage ignores the poll error and says "No projects yet" (BACKLOG 114)
179
- - Files: web-app/src/pages/ProjectsPage.tsx, web-app/src/pages/ProjectsPage.state.test.mjs (new)
121
+ - Both messages add that the verifier runs `python3 -E`, so cryptography supplied through PYTHONPATH or PYTHONUSERBASE is not seen.
122
+ - Exit codes are unchanged.
123
+ - Wall:
124
+ - `bash tests/test-proof-verify-jwks.sh` exits 0, and case 4 asserts that the NOT CHECKED output names PYTHONUSERBASE.
125
+ - `grep -c PYTHONUSERBASE autonomy/loki` prints 2 or more (0 today).
126
+ - `bash tests/test-remote-attestation-verdict.sh` exits 0.
127
+
128
+ ### S-198: the P7 Array.from arm loses an outer generator's index when generators nest (BACKLOG 145, A5)
129
+ - Files: tests/moat/p7-no-fabricated-data.sh. Only two regions:
130
+ - the Array.from generator arm, about 1934-1994;
131
+ - one fixture beside the B-8 fixture, about 3301.
132
+ - Tier: HIGH (moat)
133
+ - Region: this does not overlap S-180's helper-return arm. Do not append fixtures at the file tail.
134
+ - Red: `Array.from({length:2}, (_, r) => Array.from({length:2}, (_, c) => ({ id: r, user: 'Admin' })))` goes through.
135
+ - Green: substitute the enclosing generator's params as well as the inner ones.
136
+ - Branch: run the widened arm on main first.
137
+ - On any live hit in dashboard-ui or web-app, the slice becomes report-only.
138
+ - Never add an allowlist.
139
+ - cases.txt stays unchanged.
140
+ - Wall:
141
+ - `bash tests/moat/p7-no-fabricated-data.sh` prints PASS for every P7 case, and the nested fixture is flagged.
142
+ - Removing the outer substitution lets the fixture through.
143
+
144
+ ### S-199: the py-tool identity test covers every guarded copy
145
+ - Files: tests/test-council-py-tool-identity.sh
180
146
  - Tier: LOW
181
- - Red: `usePolling` returns `error` (web-app/src/hooks/usePolling.ts:7), but ProjectsPage destructures only `data` and `refresh` (~65). A failed history fetch renders "No projects yet. Start building." (~168).
182
147
  - Green:
183
- - With error set and no data, the page renders "Could not load projects" plus a retry.
184
- - With data present and a later poll error, the page keeps the data and shows a stale notice.
185
- - Real empty data still says "No projects yet".
186
- - Wall: `node --test web-app/src/pages/ProjectsPage.state.test.mjs` passes, and `cd web-app && npx tsc -b` exits 0.
187
-
188
- ### S-162: SecretsPanel and DocsPanel swallow fetch errors into empty states (BACKLOG 114)
189
- - Files: web-app/src/components/ProjectWorkspace.tsx (SecretsPanel fetchSecrets ~248-256 with its empty branch ~366-372, and DocsPanel fetchStatus ~432-442 with its empty branch ~589-592 only), web-app/src/components/ProjectWorkspace.panels.test.mjs (new)
190
- - Tier: LOW
191
- - Red: both catches are `// ignore`. A failed secrets fetch renders "No secrets configured yet". A failed docs fetch renders "No documentation generated yet". SecretsPanel even declares an `error` state that the catch never sets.
192
- - Green: each failure renders "Could not load secrets" or "Could not load documentation", and genuine empties are unchanged.
193
- - Wall: `node --test web-app/src/components/ProjectWorkspace.panels.test.mjs` passes, and `cd web-app && npx tsc -b` exits 0.
194
-
195
- ### S-163: CICDPanel shows unknown conclusions as "Failed" and unknown statuses as running (BACKLOG 114)
196
- - Files: web-app/src/components/CICDPanel.tsx (normalizeRunStatus, normalizeJobStatus, normalizeStepStatus and the status style map only), web-app/src/components/CICDPanel.status.test.mjs (new)
197
- - Tier: LOW
198
- - Red: all three normalizers map `default: return 'failed'` for a completed item, so `neutral`, `action_required`, `stale` and a null conclusion read as Failed. Any unlisted `status` returns `'running'`.
148
+ - Compare every `^_loki_snapshot_py_tool() {` body found by `git grep -l` under autonomy/ against run.sh's.
149
+ - Vacuity guard: at least one copy besides run.sh, and each body at least 10 lines.
150
+ - This is the only row that edits this file. S-200 to S-204 add copies and rely on it.
151
+ - Wall:
152
+ - `bash tests/test-council-py-tool-identity.sh` exits 0 and prints the number of copies compared.
153
+ - A one-byte change to completion-council.sh's copy in a scratch tree exits 1.
154
+
155
+ ### S-200: done-recognition readers load user-site .pth (BACKLOG 54)
156
+ - Files:
157
+ - autonomy/lib/done-recognition.sh: every python3 site, plus a guarded helper copy.
158
+ - tests/test-done-recognition-no-user-site-pth.sh (new)
159
+ - Tier: HIGH
160
+ - Red: a user-site `.pth` under a scratch HOME changes the verdict read (the about-703 site).
199
161
  - Green:
200
- - A new `unknown` status with a neutral style.
201
- - `failure`, `timed_out` and `startup_failure` still map to failed.
202
- - `success`, `cancelled` and `skipped` are unchanged.
203
- - Wall: `node --test web-app/src/components/CICDPanel.status.test.mjs` passes and covers each conclusion. `cd web-app && npx tsc -b` exits 0.
204
-
205
- ### S-164: AIChatPanel prints "Done." for a task that failed with no output (BACKLOG 114)
206
- - Files: web-app/src/components/AIChatPanel.tsx (the two `|| 'Done.'` fallbacks at ~543 and ~622 only), web-app/src/components/AIChatPanel.result.test.mjs (new)
207
- - Tier: LOW
208
- - Red: both completion paths render `content || 'Done.'` whatever `returncode` is, so a non-zero exit with no output reads as success.
209
- - Green: a non-zero returncode with no output renders "Failed (exit N), no output". Zero with no output renders "Finished with no output". Real output is unchanged.
210
- - Wall: `node --test web-app/src/components/AIChatPanel.result.test.mjs` passes, and `cd web-app && npx tsc -b` exits 0.
211
-
212
- ### S-165: TrustedBy asserts a trust claim and ChangelogWidget shows March 2026 v6.x as "Recent Changes" (BACKLOG 117 remainder)
213
- - Files: web-app/src/components/TrustedBy.tsx, web-app/src/components/ChangelogWidget.tsx
214
- - Tier: LOW
215
- - Red: TrustedBy.tsx:70 renders "Trusted by developers building the future" on HomePage and ShowcasePage. ChangelogWidget.tsx hardcodes v6.71.1, v6.70.0 and v6.69.0 (dates Mar 20-24, 2026) under "Recent Changes" while VERSION is 9.x.
162
+ - All sites run through `_loki_snapshot_py_tool -I -S`.
163
+ - The builder lists each site's result when no interpreter resolves. None of them may read as done or met.
164
+ - Add a byte-identical guarded copy.
165
+ - Gate: reproduce the forged read on main first.
166
+ - Wall:
167
+ - `bash tests/test-done-recognition-no-user-site-pth.sh` exits 0.
168
+ - Reverting one site to `-E` in a scratch copy fails it.
169
+ - `bash tests/test-done-recognition-tests-axis.sh` and `bash tests/test-reuse-done-recognition.sh` exit 0.
170
+
171
+ ### S-201: council-v2 readers load .pth, and a crashed sycophancy check skips the devil's advocate (BACKLOG 54)
172
+ - Files:
173
+ - autonomy/council-v2.sh: its 7 python3 sites and the Step 4 and 5 fallbacks, plus a guarded helper copy.
174
+ - tests/test-council-v2-no-user-site-pth.sh (new)
175
+ - Tier: MEDIUM. The path is opt-in through `LOKI_COUNCIL_VERSION=2`.
176
+ - Red:
177
+ - A `.pth` forges a vote read.
178
+ - A detector failure yields `0.000`, so a unanimous approve never challenges.
216
179
  - Green:
217
- - The headline becomes a factual label (for example "At a glance").
218
- - The widget either drops the hardcoded list for a link to the changelog, or stops calling it recent.
219
- - The remaining stats are checked against the code (5 providers, template count) and noted in the commit.
220
- - Wall: `grep -n "Trusted by developers" web-app/src/components/TrustedBy.tsx` returns rc 1. `grep -n "6.71.1" web-app/src/components/ChangelogWidget.tsx` returns rc 1. `cd web-app && npx tsc -b` exits 0.
221
-
222
- ### S-166: checkpoint viewer drops a rejected fetch and clears the error (BACKLOG 114)
223
- - Files: dashboard-ui/components/loki-checkpoint-viewer.js (the load at ~95-110 only), dashboard-ui/tests/loki-checkpoint-viewer-fetch-error.node.test.mjs (new)
224
- - Tier: LOW
225
- - Red: `Promise.allSettled` never throws. On `status === 'rejected'` the code keeps the old or empty list and then sets `this._error = null`, so a failed read renders as "no checkpoints".
226
- - Green: a rejected result sets `_error` and renders a load-failure message. A fulfilled empty result still renders the empty state.
227
- - Wall: `node --test dashboard-ui/tests/loki-checkpoint-viewer-fetch-error.node.test.mjs` passes, including the negative assertion.
228
-
229
- ### S-167: council transcripts hide a failed hook-events read as zero events (BACKLOG 114)
230
- - Files: dashboard-ui/components/loki-council-transcripts.js (the hook events load at ~104-118 only), dashboard-ui/tests/loki-council-transcripts-fetch-error.node.test.mjs (new)
231
- - Tier: LOW
232
- - Red: the catch sets `this._hookEvents = []` and records no error, so a failed read renders the same as "no hook events".
233
- - Green: the failure is recorded and rendered as "Could not load hook events". A real empty list is unchanged.
234
- - Wall: `node --test dashboard-ui/tests/loki-council-transcripts-fetch-error.node.test.mjs` passes.
235
-
236
- ### S-168: task board hides a load error whenever local tasks exist (BACKLOG 114)
237
- - Files: dashboard-ui/components/loki-task-board.js (the catch at ~164-169 and the error render at ~1585 only), dashboard-ui/tests/loki-task-board-fetch-error.node.test.mjs (new)
238
- - Tier: LOW
239
- - Red: the render shows the error only when `this._error && this._tasks.length === 0`, and the catch refills `_tasks` with local tasks. A failed server read with any local task therefore shows no error, and the board looks complete.
240
- - Green: with local tasks present, a server failure still shows a banner ("Server tasks could not be loaded; showing local tasks only") above the local tasks.
241
- - Wall: `node --test dashboard-ui/tests/loki-task-board-fetch-error.node.test.mjs` passes. `node --test dashboard-ui/tests/loki-task-board-modal-guard.node.test.mjs` still passes.
242
-
243
- ### S-169: log stream swallows API failures silently (BACKLOG 114)
244
- - Files: dashboard-ui/components/loki-log-stream.js (the API poll catch at ~179 and the empty render only), dashboard-ui/tests/loki-log-stream-fetch-error.node.test.mjs (new)
180
+ - `-I -S` on every site.
181
+ - An unmeasured sycophancy score on a unanimous approve runs the devil's advocate. A resolver failure never yields "no challenge".
182
+ - Gate: reproduce both on main first.
183
+ - Wall:
184
+ - `bash tests/test-council-v2-no-user-site-pth.sh` exits 0, covering the `.pth` leg and the detector-failure leg.
185
+ - Reverting either change fails its leg.
186
+ - `bash tests/test-council-v2-quorum.sh` exits 0.
187
+
188
+ ### S-202: voter-agents readers load user-site .pth (BACKLOG 54)
189
+ - Files:
190
+ - autonomy/lib/voter-agents.sh: its 3 python3 sites, plus a guarded helper copy.
191
+ - tests/test-voter-agents-no-user-site-pth.sh (new)
192
+ - Tier: HIGH
193
+ - Red: a `.pth` changes a parsed voter verdict.
194
+ - Green: `-I -S`. With no interpreter, the dispatch fails, so completion-council's existing fail-closed CONTINUE applies.
195
+ - Gate: reproduce the changed verdict on main first.
196
+ - Wall:
197
+ - `bash tests/test-voter-agents-no-user-site-pth.sh` exits 0, and reverting fails it.
198
+ - `bash tests/test-voter-agents-json.sh` and `bash tests/test-sdk-voter-agents.sh` exit 0.
199
+
200
+ ### S-203: proof-check readers load user-site .pth (BACKLOG 54)
201
+ - Files:
202
+ - autonomy/lib/proof-check.sh: its 3 heredoc readers, plus a guarded helper copy.
203
+ - tests/test-proof-check-no-user-site-pth.sh (new)
204
+ - Tier: MEDIUM
205
+ - Red: a `.pth` forges the headline read.
206
+ - Green: `-I -S`. With no interpreter, the readers print empty, as they do today when python3 is absent.
207
+ - Gate: reproduce the forged headline on main first.
208
+ - Wall:
209
+ - `bash tests/test-proof-check-no-user-site-pth.sh` exits 0, and reverting fails it.
210
+ - `bash tests/test-proven-pr-check.sh` exits 0.
211
+
212
+ ### S-204: checklist verification loads user-site .pth (BACKLOG 53)
213
+ - Files:
214
+ - autonomy/prd-checklist.sh: every python3 site, including the checklist-verify.py call and the oracle heredoc, plus a guarded helper copy.
215
+ - tests/test-prd-checklist-no-user-site-pth.sh (new)
216
+ - Tier: HIGH
217
+ - Red: a `.pth` changes the status_token or the waiver read.
218
+ - Green:
219
+ - `-I -S`. The builder audits the oracle heredoc's imports first.
220
+ - Any site that needs a non-stdlib module stays `-E` with a ponytail note.
221
+ - Resolver failure: the builder lists each site's result. None reads as verified.
222
+ - Gate: reproduce the changed read on main first.
223
+ - Wall:
224
+ - `bash tests/test-prd-checklist-no-user-site-pth.sh` exits 0, and reverting fails it.
225
+ - `bash tests/moat/p2-honest-verdict.sh` prints `CASE P2.checklist-verify-not-shadowed PASS`.
226
+ - `bash tests/test-prd-checklist-interval-w4.sh` exits 0.
227
+
228
+ ### S-205: Bun loki proof verify runs a PATH python3 -E (BACKLOG 48 class, Bun half)
229
+ - Files:
230
+ - loki-ts/src/commands/proof.ts: the verifier spawn only.
231
+ - loki-ts/src/util/python.ts, only to extend the helper that already exists there.
232
+ - loki-ts/tests/commands/proof_verify_interpreter.test.ts (new)
233
+ - Tier: HIGH
234
+ - Red: a user-site `.pth` under a scratch HOME changes verify output, or writes a marker.
235
+ - Green:
236
+ - Resolve like `_loki_snapshot_py_tool`: /usr/bin/python3 and /bin/python3 first, then absolute PATH dirs, each probed with `-I -S -c ''`. Run with `-I -S`.
237
+ - If none resolves, exit 2 as NOT CHECKED.
238
+ - Gate: reproduce the `.pth` effect on main first.
239
+ - Wall:
240
+ - `cd loki-ts && bun test tests/commands/proof_verify_interpreter.test.ts tests/commands/proof_verify_parity.test.ts` exits 0.
241
+ - Reverting to `["python3","-E"]` fails the new test.
242
+ - The Captain rebuilds loki-ts/dist.
243
+
244
+ ### S-206: cockpit actions say "working tree is clean" and "no run in progress" after a failed fetch (BACKLOG 114)
245
+ - Files:
246
+ - web-app/src/cockpit/FinalActions.tsx (disabled reasons only)
247
+ - web-app/src/cockpit/FinalActions.reasons.test.mjs (new)
245
248
  - Tier: LOW
246
- - Red: the catch body is only the comment `// API not available, will retry on next poll`. A panel that never reached the API looks like a quiet log.
247
- - Green: consecutive failures set a visible "Log source unreachable, retrying" state, and it clears on the next success. Polling is unchanged.
248
- - Wall: `node --test dashboard-ui/tests/loki-log-stream-fetch-error.node.test.mjs` passes. `node --test dashboard-ui/tests/loki-poll-registry.node.test.mjs` still passes.
249
-
250
- ### S-170: API keys panel shows "No API keys configured" under its own load-error banner (BACKLOG 114)
251
- - Files: dashboard-ui/components/loki-api-keys.js (the table branch at ~568-575 only), dashboard-ui/tests/loki-api-keys-fetch-error.node.test.mjs (new)
249
+ - Green:
250
+ - Export a pure reasons function.
251
+ - `git === null`: commit, push and PR read "Working tree status not loaded".
252
+ - `status === null`: pause, resume and stop read "Run status not loaded".
253
+ - Real data keeps today's copy.
254
+ - Wall:
255
+ - `node --test web-app/src/cockpit/FinalActions.reasons.test.mjs` passes:
256
+ - git null lacks "The working tree is clean" and "Nothing to push";
257
+ - status null lacks "No run in progress";
258
+ - `{ahead:0}` with no files keeps both sentences.
259
+ - `cd web-app && npx tsc -b` exits 0.
260
+ - The Captain rebuilds web-app/dist.
261
+
262
+ ### S-207: cockpit evidence says "No gate results recorded" when the checklist request failed (BACKLOG 114)
263
+ - Files:
264
+ - web-app/src/cockpit/useCockpitState.ts: `getChecklist` through `settle`, plus a `checklistError` field.
265
+ - web-app/src/cockpit/EvidencePanel.tsx (empty branch only)
266
+ - web-app/src/cockpit/ExecutionCockpit.tsx (EvidencePanel props only)
267
+ - web-app/src/cockpit/EvidencePanel.state.test.mjs (new)
252
268
  - Tier: LOW
253
- - Red: a failed load sets `_error = "Failed to load API keys: ..."`, which renders as a banner at ~652. The table branch still falls to `keys.length === 0` and prints "No API keys configured. Create one to get started." Both render together.
254
- - Green: when the list load failed, the empty-state sentence is not rendered. A real empty list keeps it.
255
- - Wall: `node --test dashboard-ui/tests/loki-api-keys-fetch-error.node.test.mjs` passes. It asserts that the error render does not contain "No API keys configured".
256
-
257
- ### S-171: lint for response fields put into innerHTML without escaping (BACKLOG 124)
258
- - Files: tests/test-no-unescaped-innerhtml.sh (new), tests/lib/scan-unescaped-innerhtml.py (new)
269
+ - Wall:
270
+ - `node --test web-app/src/cockpit/EvidencePanel.state.test.mjs` passes:
271
+ - an error renders "Could not load gate results" and not "No gate results recorded";
272
+ - `items:[]` with no error keeps that sentence.
273
+ - `node web-app/src/cockpit/run-derive-view-test.mjs` exits 0.
274
+ - `cd web-app && npx tsc -b` exits 0.
275
+ - The Captain rebuilds web-app/dist.
276
+
277
+ ### S-208: trust trajectory reads any non-pass council verdict as a failure (BACKLOG 118)
278
+ - Files:
279
+ - autonomy/lib/trust_trajectory.py (`_verdict_is_pass` only)
280
+ - tests/test_trust_trajectory_unknown_verdict.py (new)
259
281
  - Tier: MEDIUM
260
- - Red: no guard exists. A planted fixture component with ``el.innerHTML = `<b>${data.name}</b>` `` is flagged, rc 1.
282
+ - Red: a verdict such as "UNKNOWN" or "INCONCLUSIVE" maps to 0.0.
261
283
  - Green:
262
- - The scanner covers dashboard-ui/components and dashboard-ui/scripts/build-standalone.js.
263
- - It flags a template-literal or string interpolation into innerHTML that is not wrapped in the file's escape helper.
264
- - It must pass on today's tree. Every current offender goes in an in-file ALLOWLIST with a reason of at least 40 characters, the same pattern scan-unreachable-shipped.py uses. Any real XSS found is named in the report and not fixed here.
265
- - A non-vacuity check asserts that 20 or more files were scanned.
266
- - Wall: `bash tests/test-no-unescaped-innerhtml.sh` exits 0 on main. The planted fixture case exits 1. An allowlist entry with an empty reason fails.
267
-
268
- ### S-172: shadow-write mutant check depends on the bash version's `cd ""` behaviour (BACKLOG 132)
269
- - Files: tests/test-council-shadow-write-project-dir.sh
270
- - Tier: LOW
271
- - Red: the mutant that strips only the `-n PROJECT_DIR` guard stays GREEN under bash 5.3, because `cd ""` hard-fails there. It goes RED under /bin/bash 3.2.57. The file runs `bash -c` from `#!/usr/bin/env bash`.
284
+ - Only explicit fail tokens map to False. The builder enumerates them from completion-council.sh and proof-generator's writers.
285
+ - Anything else maps to None, meaning the axis has no data point.
286
+ - Gate: find a writer string that is neither pass nor fail. If none exists, close the slice as handled.
287
+ - Wall:
288
+ - `python3 -m pytest -q tests/test_trust_trajectory_unknown_verdict.py tests/test_trust_trajectory.py tests/dashboard/test_trust_trajectory_endpoint.py` passes.
289
+ - An unknown verdict adds no data point, and REJECTED still reads 0.0.
290
+ - Reverting fails the first case.
291
+
292
+ ### S-209: Bun codex, cline and aider get the raw prompt without the commit-hygiene line (BACKLOG 99, Bun half)
293
+ - Files:
294
+ - loki-ts/src/runner/providers.ts: the codex, cline and aider invokers only.
295
+ - loki-ts/tests/runner/provider_commit_hygiene.test.ts (new)
296
+ - loki-ts/tests/runner/providers.test.ts, only where exact-argv assertions move.
297
+ - Tier: MEDIUM
272
298
  - Green:
273
- - The GREEN and mutant runs execute under `/bin/bash` when it exists, and also under the PATH bash.
274
- - The in-file mutant leg goes red under both interpreters. It asserts on the executed-module marker, not on the result of `cd`.
275
- - Wall: `bash tests/test-council-shadow-write-project-dir.sh` exits 0. Its mutant leg reports red under both `/bin/bash --version` and the PATH bash, and the two version strings are printed in the output.
276
-
277
- ### S-173: P2.council-readers-not-shadowed does not drive the 6th reader, council_managed_should_stop (BACKLOG 135)
278
- - Files: tests/moat/p2-honest-verdict.sh (the council-readers-not-shadowed case only)
279
- - Tier: HIGH (moat)
280
- - Red: the case covers 5 readers. `council_managed_should_stop` (completion-council.sh ~4351) reads test-results.json behind `LOKI_EXPERIMENTAL_MANAGED_COUNCIL`/`LOKI_MANAGED_AGENTS` and is never driven.
299
+ - Prefix the prompt with the hygiene line plus a blank line, matching the bash `"$PROVIDER_COMMIT_HYGIENE"$'\n\n'"$prompt"`.
300
+ - The test reads the literal from providers/codex.sh; it does not copy it.
301
+ - Wall:
302
+ - `cd loki-ts && bun test tests/runner/provider_commit_hygiene.test.ts tests/runner/providers.test.ts` exits 0.
303
+ - All three argvs carry the bash literal.
304
+ - Claude's prompt is not double-prefixed.
305
+ - Removing the prefix fails the three legs.
306
+ - The Captain rebuilds loki-ts/dist.
307
+
308
+ ### S-210: speed up the two command-probe suites (29s and 30s in shard-durations.tsv)
309
+ - Files: tests/test-help-discoverability.sh, tests/test-completion-coverage.sh (the probe loops only)
310
+ - Tier: LOW
311
+ - Change:
312
+ - The builder captures `real_count` and the pass and fail lines first.
313
+ - Then probe with `xargs -P 8`, each probe in its own scratch cwd, writing one result file per command.
314
+ - Keep the captured-output (never piped) rule noted in the file.
315
+ - Wall:
316
+ - `time bash tests/test-help-discoverability.sh` and `time bash tests/test-completion-coverage.sh` each exit 0 with real time under 10s.
317
+ - `real_count` and the pass counts match the pre-change capture.
318
+ - The builder reports both timings.
319
+
320
+ ### S-211: register the four suites BACKLOG 75 still leaves unrun (after S-174 merges)
321
+ - Files: tests/run-all-tests.sh (four `run_test` lines only), tests/shard-durations.tsv (four lines)
322
+ - Tier: LOW. **Captain-built after S-174 merges.**
323
+ - Suites: tests/council/test_managed_completion_flag.sh, tests/council/test_managed_review_flag.sh, tests/test-evidence-gate-no-tests.sh, tests/test-voter-agents-json.sh.
324
+ - All four appear only in comments in run-all-tests.sh, so no runner executes them.
325
+ - The evidence-gate suite also appears in scripts/local-ci.sh, which is retired.
326
+ - Gate:
327
+ - Each suite passes 3 of 3 from a scratch cwd and leaves no `.loki/state/provider` in the repo.
328
+ - A suite that fails is reported, not registered, and becomes its own fix slice (the S-110 lesson).
329
+ - Wall:
330
+ - `grep -c -e test_managed_completion_flag -e test_managed_review_flag -e test-evidence-gate-no-tests -e test-voter-agents-json tests/run-all-tests.sh` prints 8 (4 before registration).
331
+ - `bash tests/test-shard-coverage.sh` exits 0.
332
+
333
+ ### S-212: GUARDS 5, 12 and 13 still read PENDING after S-138, S-139 and S-154 landed (after S-181 merges)
334
+ - Files: docs/v10/GUARDS.md (sections 5, 12 and 13 only). This does not overlap S-181's section 11.
335
+ - Tier: LOW
281
336
  - Green:
282
- - Add a leg that sets those flags and plants a cwd `json.py` or `hashlib.py`. It passes today, because the D7 sys.path filter is present.
283
- - A scratch-copy mutation that removes that filter turns the case red.
284
- - Scope is the cwd-shadow class only. This reader is still `python3 -E` with no `-I -S`, so a `.pth` leg would turn a proven case red on main. The `.pth` leg is deferred to the production fix, which is alternate A2 below.
285
- - Wall: `bash tests/moat/p2-honest-verdict.sh` prints `CASE P2.council-readers-not-shadowed PASS`, and the mutated copy prints FAIL for that case.
286
-
287
- **Risks and notes**
288
- 1. **Fold into S-153 now (the biggest velocity finding).**
289
- - Since S-144, every bump commit rewrites Footer.tsx, and Footer.tsx is on neither allowlist. `version-bump-only.sh check` against the v9.68.0 bump 8d17be2b is rc 1 ("not on the allowlist: web-app/src/components/Footer.tsx"). So S-132's Tests skip, merged at 19:06, cannot fire on any bump.
290
- - S-153 changes only release.yml STEP 1. The script's header says it is a byte-for-byte copy of STEP 1, and `tests/test-version-bump-only.sh` checks parity against it.
291
- - S-153's file set should therefore also include `scripts/ci/version-bump-only.sh` and Footer fixtures in `tests/test-version-bump-only.sh`. That also covers any web-app/dist files S-153 adds to the bump. Without both, the skip still never fires; with only one side changed, the parity test goes red.
292
- 2. **Same-file neighbours.**
293
- - S-156 (run.sh ~21390) and S-157 (completion-council.sh) are the only rows on those files, in regions far from S-135's test-only rework.
294
- - Trust-core probes anchor into both files by content, so both walls run `tests/test-trust-core-tests-detect.sh`. If S-135 lands first, rebase.
295
- 3. **Registration and rebuilds.**
296
- - 17 rows add a new test file and need the Captain to register it: S-154, S-156, S-157, S-158, S-159 to S-164, S-166 to S-171.
297
- - S-159 to S-165 need a `web-app/dist` rebuild. S-166 to S-170 need `dashboard/static/index.html` rebuilt.
298
- 4. **Response shapes.** S-158 adds a field and changes the rendered copy. The builder must name the consumers it checked.
299
- 5. **Held, not in the 20:**
300
- - BACKLOG 19 (remote stripped signature from a signing server). `docs/exit-codes.md` documents UNSIGNED as helper return 0. Changing it is an exit-contract change on a verifier (CEO, like BACKLOG 4). A label-only version would need a new JWKS fetch, meaning egress on the verify path.
301
- - BACKLOG 18, 77, 121, 123 and WhatsNew.tsx, as before.
302
- - BACKLOG 52 (renaming the "Moat suite" check), because branch protection may key on the check name.
303
- 6. **Alternates, ready once a file frees up:**
304
- - A1: BACKLOG 17, the `/api/focus` POST fires with LOKI_DASHBOARD=false (run.sh ~24478). Cut it after S-156.
305
- - A2: `council_managed_should_stop` still runs `python3 -E` with no `-I -S`, and has `project_dir="${PROJECT_DIR:-$(pwd)}"` (BACKLOG 127c). Cut it after S-157, then add S-173's `.pth` leg.
306
- - A3: GUARDS 11 as a scripts/v10-guard.sh rule. Cut it after S-152, which owns that file.
307
- - A4: BACKLOG 112 `/api/cost` tracker fallback and `/metrics` loki_cost_usd. Cut it after S-158 (dashboard/server.py).
308
- - A5: "VERIFIED WITH GAPS" coloured as success in build-standalone.js. Cut it after S-146.
309
- 7. **Already fixed, dropped after checking:**
310
- - BACKLOG 38 (no_pass_recorded exists, completion-council.sh:2099).
311
- - BACKLOG 56 (the Bun reader handles passed_count/failed_count and reuses the zero-test detector).
312
- - BACKLOG 81 (python3 -E plus the sys.path filter at run.sh:13783).
313
- - BACKLOG 116 memory-browser and overview keys (snake_case and `g.blocked` are in place).
314
-
315
- | S-154 | GUARDS 5 + TODO 8: prune-worktrees.sh treats a cherry-picked branch (git cherry, no + lines) as merged; test forbids mtime signals | scripts/prune-worktrees.sh, tests/test-prune-worktrees.sh (new) | MEDIUM | bash tests/test-prune-worktrees.sh exits 0; cherry-picked fixture listed, one-unique-commit, locked and dirty fixtures kept; dropping the git cherry branch fails the case | ready@2026-09-27T19:10Z | Removes worktrees only via git worktree remove. Captain registers the test. Source: 19:10Z cut. |
316
- | S-155 | Velocity: reachability scan (95s on CI, 47s local) under 10s via a per-module stem prefilter, identical verdicts | tests/lib/scan-unreachable-shipped.py | LOW | scan wall time under 10s; stdout and rc byte-identical to the pre-change capture; bash tests/test-no-unreachable-shipped.sh 4 passed 0 failed; removing one real require is still reported | ready@2026-09-27T19:10Z | Builder measures regex vs I/O first and records both numbers. Source: 19:10Z cut. |
317
- | S-156 | BACKLOG 127 remainder: auto-capture shadow_write runs with PROJECT_DIR unset, and float() splices episode importance into python source | autonomy/run.sh (shadow_write auto-capture block about 21380-21396 only), tests/test-autocapture-shadow-write-guard.sh (new) | HIGH | bash tests/test-autocapture-shadow-write-guard.sh exits 0 under /bin/bash and bash 5; no marker from a planted cwd memory package; no file from an injected importance; trust-core suite passes | ready@2026-09-27T19:10Z | Builder names who writes episode_path_file. Only run.sh row in this cut. Source: 19:10Z cut. |
318
- | S-157 | BACKLOG 98/89: council member vote and _council_convergence_evidence_green read pass:true as green whatever failed_count says | autonomy/completion-council.sh (those 2 parsers only), tests/test-council-failed-count-honesty.sh (new) | HIGH | pass:true with failed_count 2 is not green at both sites; failed_count 0 stays green; trust-core and convergence-floor suites pass; reverting either site fails its case | ready@2026-09-27T19:10Z | Mirrors the evidence gate rule (~2014). Only completion-council.sh row in this cut. Source: 19:10Z cut. |
319
- | S-158 | BACKLOG 118: per-run cost_partial dropped by /api/cost/timeline runs[] and /api/proofs; pages show a lower bound as a total | dashboard/server.py (runs.append in _compute_cost_timeline and the /api/proofs row only), dashboard/static/cost.html, dashboard/static/proofs.html, tests/dashboard/test_cost_partial_surfaced.py (new) | MEDIUM | pytest test_cost_partial_surfaced.py passes; fixture cost_partial true surfaces in both endpoints and renders as at least $X; dropping the key fails | ready@2026-09-27T19:10Z | Builder names the consumers checked. Source: 19:10Z cut. |
320
- | S-159 | BACKLOG 114: NLSearch renders No results found when the search request failed | web-app/src/components/NLSearch.tsx, web-app/src/components/NLSearch.state.test.mjs (new) | LOW | node --test NLSearch.state.test.mjs passes incl. the negative assertion; npx tsc -b exits 0 | ready@2026-09-27T19:10Z | Captain rebuilds web-app/dist. Source: 19:10Z cut. |
321
- | S-160 | BACKLOG 114: CommandPalette file-search failure reads as No results found | web-app/src/components/CommandPalette.tsx, web-app/src/components/CommandPalette.state.test.mjs (new) | LOW | node --test CommandPalette.state.test.mjs passes; npx tsc -b exits 0 | ready@2026-09-27T19:10Z | Captain rebuilds web-app/dist. Source: 19:10Z cut. |
322
- | S-161 | BACKLOG 114: ProjectsPage ignores usePolling error and says No projects yet on a failed fetch | web-app/src/pages/ProjectsPage.tsx, web-app/src/pages/ProjectsPage.state.test.mjs (new) | LOW | node --test ProjectsPage.state.test.mjs passes (error, stale-data and real-empty cases); npx tsc -b exits 0 | ready@2026-09-27T19:10Z | Captain rebuilds web-app/dist. Source: 19:10Z cut. |
323
- | S-162 | BACKLOG 114: SecretsPanel and DocsPanel swallow fetch errors into No secrets / No documentation empty states | web-app/src/components/ProjectWorkspace.tsx (SecretsPanel and DocsPanel fetch and empty branches only), web-app/src/components/ProjectWorkspace.panels.test.mjs (new) | LOW | node --test ProjectWorkspace.panels.test.mjs passes; npx tsc -b exits 0 | ready@2026-09-27T19:10Z | Captain rebuilds web-app/dist. Source: 19:10Z cut. |
324
- | S-163 | BACKLOG 114: CICDPanel maps neutral/action_required/stale/null conclusions to Failed and unknown statuses to running | web-app/src/components/CICDPanel.tsx (3 normalizers and status map only), web-app/src/components/CICDPanel.status.test.mjs (new) | LOW | node --test CICDPanel.status.test.mjs passes for every conclusion; npx tsc -b exits 0 | ready@2026-09-27T19:10Z | Captain rebuilds web-app/dist. Source: 19:10Z cut. |
325
- | S-164 | BACKLOG 114: AIChatPanel prints Done. for a non-zero exit with no output | web-app/src/components/AIChatPanel.tsx (the two Done. fallbacks only), web-app/src/components/AIChatPanel.result.test.mjs (new) | LOW | node --test AIChatPanel.result.test.mjs passes; npx tsc -b exits 0 | ready@2026-09-27T19:10Z | Captain rebuilds web-app/dist. Source: 19:10Z cut. |
326
- | S-165 | BACKLOG 117 remainder: TrustedBy asserts Trusted by developers; ChangelogWidget shows March 2026 v6.x as Recent Changes | web-app/src/components/TrustedBy.tsx, web-app/src/components/ChangelogWidget.tsx | LOW | grep for Trusted by developers and 6.71.1 in those files returns rc 1; npx tsc -b exits 0 | ready@2026-09-27T19:10Z | Copy only. Captain rebuilds web-app/dist. Source: 19:10Z cut. |
327
- | S-166 | BACKLOG 114: checkpoint viewer ignores a rejected allSettled fetch and clears the error | dashboard-ui/components/loki-checkpoint-viewer.js (load block only), dashboard-ui/tests/loki-checkpoint-viewer-fetch-error.node.test.mjs (new) | LOW | node --test the new file passes incl. the negative assertion | ready@2026-09-27T19:10Z | Captain rebuilds dashboard/static/index.html. Source: 19:10Z cut. |
328
- | S-167 | BACKLOG 114: council transcripts render a failed hook-events read as zero events | dashboard-ui/components/loki-council-transcripts.js (hook events load only), dashboard-ui/tests/loki-council-transcripts-fetch-error.node.test.mjs (new) | LOW | node --test the new file passes | ready@2026-09-27T19:10Z | Captain rebuilds dashboard/static/index.html. Source: 19:10Z cut. |
329
- | S-168 | BACKLOG 114: task board hides a server load error whenever local tasks exist | dashboard-ui/components/loki-task-board.js (catch and error render only), dashboard-ui/tests/loki-task-board-fetch-error.node.test.mjs (new) | LOW | node --test the new file passes; loki-task-board-modal-guard test still passes | ready@2026-09-27T19:10Z | Captain rebuilds dashboard/static/index.html. Source: 19:10Z cut. |
330
- | S-169 | BACKLOG 114: log stream swallows API failures with an empty catch | dashboard-ui/components/loki-log-stream.js (API poll catch and empty render only), dashboard-ui/tests/loki-log-stream-fetch-error.node.test.mjs (new) | LOW | node --test the new file passes; loki-poll-registry test still passes | ready@2026-09-27T19:10Z | Captain rebuilds dashboard/static/index.html. Source: 19:10Z cut. |
331
- | S-170 | BACKLOG 114: API keys panel renders No API keys configured under its own load-error banner | dashboard-ui/components/loki-api-keys.js (table branch only), dashboard-ui/tests/loki-api-keys-fetch-error.node.test.mjs (new) | LOW | node --test the new file passes; error render lacks the empty-state sentence | ready@2026-09-27T19:10Z | Captain rebuilds dashboard/static/index.html. Source: 19:10Z cut. |
332
- | S-171 | BACKLOG 124: lint for response fields interpolated into innerHTML without the escape helper | tests/test-no-unescaped-innerhtml.sh (new), tests/lib/scan-unescaped-innerhtml.py (new) | MEDIUM | passes on main with a reasoned in-file allowlist; planted fixture exits 1; empty-reason entry fails; at least 20 files scanned | ready@2026-09-27T19:10Z | Real XSS found is reported, not fixed here. Captain registers. Source: 19:10Z cut. |
333
- | S-172 | BACKLOG 132: shadow-write mutant check is green on bash 5.3 and red on /bin/bash 3.2 | tests/test-council-shadow-write-project-dir.sh | LOW | bash tests/test-council-shadow-write-project-dir.sh exits 0; mutant leg red under both /bin/bash and PATH bash, both versions printed | ready@2026-09-27T19:10Z | Test-only. Source: 19:10Z cut. |
334
- | S-173 | BACKLOG 135: P2.council-readers-not-shadowed does not drive council_managed_should_stop (cwd-shadow class only) | tests/moat/p2-honest-verdict.sh (that case only) | HIGH | p2-honest-verdict.sh prints CASE P2.council-readers-not-shadowed PASS; removing the sys.path filter in a scratch copy prints FAIL | ready@2026-09-27T19:10Z | No .pth leg: that reader is still python3 -E, so a .pth leg would redden a proven case; follows alternate A2. Source: 19:10Z cut. |
337
+ - Section 12 names tests/test-no-ambient-gitconfig-writes.sh (S-138).
338
+ - Section 13 names the RELEASED_AHEAD_OF_NPM case in tests/test-v10-pulse.sh (S-139). It states that this is a flag, not a refusal.
339
+ - Section 5 names tests/test-prune-worktrees.sh only if that test asserts the no-mtime rule. Otherwise it stays PENDING and says what would close it.
340
+ - Wall:
341
+ - `awk '/^## 12\./,0' docs/v10/GUARDS.md | grep -c 'PENDING, no slice cut'` prints 0.
342
+ - `bash tests/test-no-ambient-gitconfig-writes.sh` and `bash tests/test-v10-pulse.sh` exit 0.
343
+
344
+ ### S-213: no test of their own covers the council-vote labels on cost.html and proofs.html (BACKLOG 123, tests only)
345
+ - Files: dashboard-ui/tests/static-council-vote-label.node.test.mjs (new)
346
+ - Tier: LOW
347
+ - It pins:
348
+ - proofs.html: the badge reads "council " plus the verdict, with the title "Recorded council vote, not a verification result".
349
+ - cost.html: the runs header reads "Council vote".
350
+ - Wall:
351
+ - `node --test dashboard-ui/tests/static-council-vote-label.node.test.mjs` passes.
352
+ - Renaming the header, or dropping the title, in a scratch copy fails it.
353
+
354
+ **Registration and rebuilds (Captain).**
355
+ - New tests to register:
356
+ - S-194 to S-198.
357
+ - S-200 to S-209.
358
+ - S-213.
359
+ - Bundle rebuilds:
360
+ - S-205 and S-209: loki-ts/dist.
361
+ - S-206 and S-207: web-app/dist.
362
+ - Sequencing:
363
+ - S-211 builds after S-174 merges.
364
+ - S-212 builds after S-181 merges.
365
+ - S-194 and S-195 are the only run.sh rows. They name regions that do not overlap each other or S-175, and merge one at a time.
366
+
367
+ **Held, not in the 20:**
368
+ - BACKLOG 147 (P7 useMemo composed form): wait for S-180.
369
+ - BACKLOG 36 (P7 zero-file scan): wait for S-180 and S-198.
370
+ - BACKLOG 19 (a stripped remote attestation reads UNSIGNED exit 0): same function as S-197, so wait for S-197.
371
+ - The bash `loki proof verify` interpreter (autonomy/loki `cmd_proof`, `python3 -E`): wait for S-179 and S-197.
372
+ - BACKLOG 99, bash half (run.sh main loop): wait for S-175, S-194 and S-195.
373
+ - workspace_diff reports count 0 in a non-git directory: needs a design, because the value lands in the receipt.
374
+ - dashboard/server.py items from BACKLOG 118: wait for S-178. They are:
375
+ - `/cost` pricing at Sonnet rates with no estimate label;
376
+ - council-state `total_votes: 0`;
377
+ - notifications zero summary;
378
+ - `StatusResponse` defaults;
379
+ - skill-session `running_agents: 0`.
380
+ - ProjectWorkspace phase labels and Replay Build: wait for S-187.
381
+ - Speeding up test-autocapture-shadow-write-guard.sh: its negative legs wait on a disowned process and need a design.
382
+ - Carried over: P1.verification-metadata-signed; the P2 verify renumber (CEO); BACKLOG 18, 28, 52, 121, and 123 (the audit.py half); S-112, S-114, S-115, S-117 to S-122.
383
+
384
+ | S-194 | BACKLOG 108: LOKI_AUTO_PR pushes a branch whose history still holds user files | autonomy/run.sh (create_session_pr only; does not overlap S-175 or S-195), tests/test-auto-pr-agent-committed-refuse.sh (new) | HIGH | bash tests/test-auto-pr-agent-committed-refuse.sh exits 0: auto path with a record makes no push, returns 1 and prints git reset --soft; advisory path prints the cleanup before git push -u; empty record pushes; removing the record read in a scratch copy fails leg 1; bash tests/test-trusted-push-agent-config.sh exits 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
385
+ | S-195 | BACKLOG 17: /api/focus POST fires with the dashboard disabled | autonomy/run.sh (dashboard focus notify block only; does not overlap S-175 or S-194), tests/test-focus-post-dashboard-off.sh (new) | MEDIUM | bash tests/test-focus-post-dashboard-off.sh exits 0: ENABLE_DASHBOARD=false leaves the curl shim log empty, true records one POST; removing the guard in a scratch copy fails the false leg | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
386
+ | S-196 | council_managed_should_stop diffs Loki's install tree and runs -E readers | autonomy/completion-council.sh (council_managed_should_stop only), tests/moat/p2-honest-verdict.sh (case_council_readers_no_user_site_pth plus the ~858 comment), tests/test-council-managed-diff-target.sh (new) | HIGH | bash tests/moat/p2-honest-verdict.sh prints CASE P2.council-readers-no-user-site-pth PASS and CASE P2.council-readers-not-shadowed PASS; bash tests/test-council-managed-diff-target.sh exits 0 with the target-only file in _CC_DIFF; reverting the readers to -E makes the pth case FAIL; bash tests/moat/run.sh reports no rule failed | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
387
+ | S-197 | BACKLOG 51: NOT CHECKED text should name PYTHONPATH and PYTHONUSERBASE | autonomy/loki (loki_remote_verify_receipt attestation NOT CHECKED branch and cmd_proof --jwks NOT CHECKED echo only; does not overlap S-179), tests/test-proof-verify-jwks.sh (case 4 only) | LOW | bash tests/test-proof-verify-jwks.sh exits 0 with case 4 asserting PYTHONUSERBASE in the NOT CHECKED output; grep -c PYTHONUSERBASE autonomy/loki prints 2 or more; bash tests/test-remote-attestation-verdict.sh exits 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
388
+ | S-198 | BACKLOG 145: P7 Array.from arm loses an outer generator's index when generators nest | tests/moat/p7-no-fabricated-data.sh (Array.from generator arm ~1934-1994 and one fixture beside the B-8 fixture ~3301 only; does not overlap S-180) | HIGH | bash tests/moat/p7-no-fabricated-data.sh prints PASS for every P7 case and flags the nested fixture; removing the outer substitution lets it through; tests/moat/cases.txt unchanged | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
389
+ | S-199 | py-tool identity test covers every guarded _loki_snapshot_py_tool copy under autonomy/ | tests/test-council-py-tool-identity.sh | LOW | bash tests/test-council-py-tool-identity.sh exits 0 and prints the number of copies compared (at least 1 besides run.sh); a one-byte change to completion-council.sh's copy in a scratch tree exits 1 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
390
+ | S-200 | BACKLOG 54: done-recognition readers load user-site .pth (-E only) | autonomy/lib/done-recognition.sh (python3 sites plus guarded helper copy), tests/test-done-recognition-no-user-site-pth.sh (new) | HIGH | bash tests/test-done-recognition-no-user-site-pth.sh exits 0 and reverting one site to -E in a scratch copy fails it; bash tests/test-done-recognition-tests-axis.sh and bash tests/test-reuse-done-recognition.sh exit 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
391
+ | S-201 | BACKLOG 54: council-v2 readers load .pth; a crashed sycophancy check skips the devil's advocate | autonomy/council-v2.sh (python3 sites, Step 4 and 5 fallbacks, guarded helper copy), tests/test-council-v2-no-user-site-pth.sh (new) | MEDIUM | bash tests/test-council-v2-no-user-site-pth.sh exits 0 (pth leg and detector-failure leg run the devil's advocate on a unanimous approve); reverting either change fails its leg; bash tests/test-council-v2-quorum.sh exits 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
392
+ | S-202 | BACKLOG 54: voter-agents readers load user-site .pth (-E only) | autonomy/lib/voter-agents.sh (3 python3 sites plus guarded helper copy), tests/test-voter-agents-no-user-site-pth.sh (new) | HIGH | bash tests/test-voter-agents-no-user-site-pth.sh exits 0 and reverting fails it; bash tests/test-voter-agents-json.sh and bash tests/test-sdk-voter-agents.sh exit 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
393
+ | S-203 | BACKLOG 54: proof-check readers load user-site .pth (-E only) | autonomy/lib/proof-check.sh (3 heredoc readers plus guarded helper copy), tests/test-proof-check-no-user-site-pth.sh (new) | MEDIUM | bash tests/test-proof-check-no-user-site-pth.sh exits 0 and reverting fails it; bash tests/test-proven-pr-check.sh exits 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
394
+ | S-204 | BACKLOG 53: checklist verification loads user-site .pth (-E only) | autonomy/prd-checklist.sh (python3 sites incl. checklist-verify.py call and oracle heredoc, plus guarded helper copy), tests/test-prd-checklist-no-user-site-pth.sh (new) | HIGH | bash tests/test-prd-checklist-no-user-site-pth.sh exits 0 and reverting fails it; bash tests/moat/p2-honest-verdict.sh prints CASE P2.checklist-verify-not-shadowed PASS; bash tests/test-prd-checklist-interval-w4.sh exits 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
395
+ | S-205 | Bun loki proof verify runs a PATH python3 -E (user-site .pth reaches the verdict) | loki-ts/src/commands/proof.ts (verifier spawn only), loki-ts/src/util/python.ts (extend existing helper only if used), loki-ts/tests/commands/proof_verify_interpreter.test.ts (new) | HIGH | cd loki-ts && bun test tests/commands/proof_verify_interpreter.test.ts tests/commands/proof_verify_parity.test.ts exits 0; reverting to python3 -E fails the new test; no resolvable interpreter exits 2 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
396
+ | S-206 | BACKLOG 114: cockpit actions say working tree is clean and no run in progress after a failed fetch | web-app/src/cockpit/FinalActions.tsx (disabled reasons only), web-app/src/cockpit/FinalActions.reasons.test.mjs (new) | LOW | node --test web-app/src/cockpit/FinalActions.reasons.test.mjs passes: git null lacks The working tree is clean and Nothing to push, status null lacks No run in progress, real data keeps both; cd web-app && npx tsc -b exits 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
397
+ | S-207 | BACKLOG 114: cockpit evidence says No gate results recorded when the checklist request failed | web-app/src/cockpit/useCockpitState.ts (getChecklist via settle only), web-app/src/cockpit/EvidencePanel.tsx (empty branch only), web-app/src/cockpit/ExecutionCockpit.tsx (EvidencePanel props only), web-app/src/cockpit/EvidencePanel.state.test.mjs (new) | LOW | node --test web-app/src/cockpit/EvidencePanel.state.test.mjs passes: an error renders Could not load gate results, items [] keeps No gate results recorded; node web-app/src/cockpit/run-derive-view-test.mjs exits 0; cd web-app && npx tsc -b exits 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
398
+ | S-208 | BACKLOG 118: trust trajectory reads any non-pass council verdict as a failure | autonomy/lib/trust_trajectory.py (_verdict_is_pass only), tests/test_trust_trajectory_unknown_verdict.py (new) | MEDIUM | python3 -m pytest -q tests/test_trust_trajectory_unknown_verdict.py tests/test_trust_trajectory.py tests/dashboard/test_trust_trajectory_endpoint.py passes: an unknown verdict adds no data point, REJECTED stays 0.0; reverting fails case 1 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
399
+ | S-209 | BACKLOG 99 (Bun): codex, cline and aider get the prompt without the commit-hygiene line | loki-ts/src/runner/providers.ts (codex, cline, aider invokers only), loki-ts/tests/runner/provider_commit_hygiene.test.ts (new), loki-ts/tests/runner/providers.test.ts (argv asserts only if moved) | MEDIUM | cd loki-ts && bun test tests/runner/provider_commit_hygiene.test.ts tests/runner/providers.test.ts exits 0: all three argvs carry the providers/codex.sh literal, claude not double-prefixed; removing the prefix fails the three legs | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
400
+ | S-210 | Velocity: help-discoverability (29s) and completion-coverage (30s) probe loops under 10s each | tests/test-help-discoverability.sh, tests/test-completion-coverage.sh (probe loops only) | LOW | time bash tests/test-help-discoverability.sh and time bash tests/test-completion-coverage.sh each exit 0 with real under 10s; real_count and pass counts match the pre-change capture; both timings reported | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
401
+ | S-211 | BACKLOG 75: register the four suites no runner executes (Captain, after S-174 merges) | tests/run-all-tests.sh (four run_test lines only, after S-174 merges), tests/shard-durations.tsv (four lines) | LOW | grep -c -e test_managed_completion_flag -e test_managed_review_flag -e test-evidence-gate-no-tests -e test-voter-agents-json tests/run-all-tests.sh prints 8; bash tests/test-shard-coverage.sh exits 0; each suite ran 3 of 3 from a scratch cwd with no .loki/state/provider left | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
402
+ | S-212 | GUARDS 5, 12 and 13 still read PENDING after S-138, S-139 and S-154 landed (after S-181 merges) | docs/v10/GUARDS.md (sections 5, 12, 13 only; does not overlap S-181 section 11) | LOW | awk '/^## 12\./,0' docs/v10/GUARDS.md piped to grep -c 'PENDING, no slice cut' prints 0; bash tests/test-no-ambient-gitconfig-writes.sh and bash tests/test-v10-pulse.sh exit 0 | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |
403
+ | S-213 | BACKLOG 123: cost.html and proofs.html council-vote labels have no test of their own | dashboard-ui/tests/static-council-vote-label.node.test.mjs (new) | LOW | node --test dashboard-ui/tests/static-council-vote-label.node.test.mjs passes (proofs.html council prefix and title, cost.html Council vote header); renaming the header or dropping the title in a scratch copy fails it | ready@2026-09-27T20:12Z | Source: 20:12Z cut. |