loki-mode 11.2.0 → 11.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/skill-link-heal.sh +54 -0
  4. package/autonomy/loki +156 -1201
  5. package/autonomy/provider-offer.sh +4 -4
  6. package/autonomy/run.sh +11 -3
  7. package/bin/loki +22 -0
  8. package/bin/loki-postinstall.js +3 -1
  9. package/dashboard/__init__.py +1 -1
  10. package/docs/CLI-REFERENCE.md +1 -1
  11. package/docs/INSTALLATION.md +1 -1
  12. package/docs/KNOWN-ISSUES.md +0 -1
  13. package/docs/v10/ADOPTION-PLAN.md +2 -2
  14. package/docs/v10/DECISIONS.md +2 -0
  15. package/docs/v10/FAILURE-CLASSES.md +37 -1
  16. package/loki-ts/dist/loki.js +729 -704
  17. package/mcp/__init__.py +1 -1
  18. package/package.json +1 -1
  19. package/packages/control-plane/dist/server.js +252 -65
  20. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  21. package/web-app/dist/assets/{AdminPage-Bs4Vyd9R.js → AdminPage-C-iMLNxe.js} +1 -1
  22. package/web-app/dist/assets/{Avatar-Dmf_rtmv.js → Avatar-C7VUvwsO.js} +1 -1
  23. package/web-app/dist/assets/{Badge-DCEdgEN8.js → Badge-tGvQEhnw.js} +1 -1
  24. package/web-app/dist/assets/{Button-BpGXXEPk.js → Button-_iHlH_yL.js} +1 -1
  25. package/web-app/dist/assets/{CockpitPage-CzssVmim.js → CockpitPage-J_nNjbcR.js} +1 -1
  26. package/web-app/dist/assets/{ComparePage-BU4XtB9F.js → ComparePage-B05ui6Xt.js} +1 -1
  27. package/web-app/dist/assets/{ErrorBoundary-Bkjf4KP2.js → ErrorBoundary-Bxex6DLn.js} +1 -1
  28. package/web-app/dist/assets/{EvidenceReceiptPanel-CcGMfaq7.js → EvidenceReceiptPanel-BsgWfdD1.js} +1 -1
  29. package/web-app/dist/assets/{GitHubIssuesPanel-D53leEFJ.js → GitHubIssuesPanel-DZBhZ8_X.js} +1 -1
  30. package/web-app/dist/assets/{GitHubPRsPanel-UVsPtV3-.js → GitHubPRsPanel-BaxlAl6R.js} +1 -1
  31. package/web-app/dist/assets/{HomePage-D9wO-XTX.js → HomePage-mVJvzqbF.js} +2 -2
  32. package/web-app/dist/assets/{LoginPage-ChxBSele.js → LoginPage-B0zd4A_G.js} +1 -1
  33. package/web-app/dist/assets/{MagicPage-BqQDKX2L.js → MagicPage-DXLQ-y_R.js} +1 -1
  34. package/web-app/dist/assets/{MetricsPage-CA22NkPl.js → MetricsPage-CLS34Xvr.js} +1 -1
  35. package/web-app/dist/assets/{NotFoundPage-oYt5qdHQ.js → NotFoundPage-BXG8PbNV.js} +1 -1
  36. package/web-app/dist/assets/{ProjectPage-tSAdI6A6.js → ProjectPage-D3kFFC0F.js} +3 -3
  37. package/web-app/dist/assets/{ProjectsPage-Dk22bicF.js → ProjectsPage-2bFn-gnC.js} +1 -1
  38. package/web-app/dist/assets/{SettingsPage-CwDSvMn6.js → SettingsPage-CBM0ziWE.js} +1 -1
  39. package/web-app/dist/assets/{ShowcasePage-By3IMy9k.js → ShowcasePage-BTaXhjI3.js} +1 -1
  40. package/web-app/dist/assets/{SystemSettingsPage-DcWNR28_.js → SystemSettingsPage-BrC2hyAr.js} +1 -1
  41. package/web-app/dist/assets/{TeamsPage-CFawyYQg.js → TeamsPage-oqxdbVbY.js} +1 -1
  42. package/web-app/dist/assets/{TemplatesPage-D7pCD-63.js → TemplatesPage-CEbUIY21.js} +1 -1
  43. package/web-app/dist/assets/{TerminalOutput-CT2v1Vm2.js → TerminalOutput-aIoWLbJA.js} +1 -1
  44. package/web-app/dist/assets/{activity-DW6jRbjj.js → activity-CPyi45gt.js} +1 -1
  45. package/web-app/dist/assets/{bell-CJOu9IIM.js → bell-BhaNBaJp.js} +1 -1
  46. package/web-app/dist/assets/{bot-dVt0Xw_X.js → bot-Da8REaEP.js} +1 -1
  47. package/web-app/dist/assets/{check-BVC4A8YK.js → check-DPj99ArM.js} +1 -1
  48. package/web-app/dist/assets/{chevron-left-Djgr5pg-.js → chevron-left-CkxonN0X.js} +1 -1
  49. package/web-app/dist/assets/{circle-alert-OE2reifn.js → circle-alert-C0JkB1y5.js} +1 -1
  50. package/web-app/dist/assets/{clock-BUUN5rWU.js → clock-COzFIiWi.js} +1 -1
  51. package/web-app/dist/assets/{cloud-CzPIksNz.js → cloud-bjkac4kn.js} +1 -1
  52. package/web-app/dist/assets/{code-xml-DlE7vlGY.js → code-xml-hlKlvXUJ.js} +1 -1
  53. package/web-app/dist/assets/{database-DUauMcIC.js → database-D555S5Km.js} +1 -1
  54. package/web-app/dist/assets/{dollar-sign-CBx9DWbM.js → dollar-sign-CVcApzcD.js} +1 -1
  55. package/web-app/dist/assets/{file-code-corner-hTmaP9Mz.js → file-code-corner-UORaMIcO.js} +1 -1
  56. package/web-app/dist/assets/{file-plus-DimFEqtO.js → file-plus-ALJtDoI2.js} +1 -1
  57. package/web-app/dist/assets/{globe-BV7yxcQ_.js → globe-CPlrM_IC.js} +1 -1
  58. package/web-app/dist/assets/{hammer-CW6Ol_ju.js → hammer-qaGZHpja.js} +1 -1
  59. package/web-app/dist/assets/{index-83w75NC1.js → index-BcXljIAO.js} +3 -3
  60. package/web-app/dist/assets/{layers-DMAqCHxh.js → layers-BV8R9VUK.js} +1 -1
  61. package/web-app/dist/assets/{loader-circle-B-TJNPo5.js → loader-circle-6TlDS9Tu.js} +1 -1
  62. package/web-app/dist/assets/{lock-DbvZW7dV.js → lock-B9t6oWI0.js} +1 -1
  63. package/web-app/dist/assets/{package-BWFcV2kS.js → package-2gEUAtZJ.js} +1 -1
  64. package/web-app/dist/assets/{plus-CjPzASFv.js → plus-DXocxPjU.js} +1 -1
  65. package/web-app/dist/assets/{refresh-cw-DJL2nFq0.js → refresh-cw-D3dWHBd6.js} +1 -1
  66. package/web-app/dist/assets/{rotate-ccw-Bq3vK-Tu.js → rotate-ccw-CvuWqaSB.js} +1 -1
  67. package/web-app/dist/assets/{scroll-text-CAMgtDYH.js → scroll-text-ktmD_xJY.js} +1 -1
  68. package/web-app/dist/assets/{server-D71_Bywf.js → server-E7pZtj5A.js} +1 -1
  69. package/web-app/dist/assets/{shield-alert-DqQ5wzQ6.js → shield-alert-968j9z0V.js} +1 -1
  70. package/web-app/dist/assets/{thumbs-up-DXE_IT36.js → thumbs-up-Tz_8rpU_.js} +1 -1
  71. package/web-app/dist/assets/{trash-2-VYAPKajq.js → trash-2-Drh9IIj8.js} +1 -1
  72. package/web-app/dist/assets/{trending-down-CuE-J2LH.js → trending-down-DCQXtVSL.js} +1 -1
  73. package/web-app/dist/assets/{trending-up-BvdD9XmQ.js → trending-up-BIiwQbWV.js} +1 -1
  74. package/web-app/dist/assets/{upload-OM2YFziE.js → upload-sCbPkfmy.js} +1 -1
  75. package/web-app/dist/assets/{usePolling-Bk39fp_d.js → usePolling-BMIg6ZVT.js} +1 -1
  76. package/web-app/dist/assets/{user-CWmKQURs.js → user-kGzXC3V2.js} +1 -1
  77. package/web-app/dist/index.html +1 -1
@@ -401,10 +401,10 @@ _provider_gate_emit_blocked() {
401
401
 
402
402
  # render_provider_availability: print the "Provider Availability" doctor section.
403
403
  #
404
- # WHY IT LIVES HERE. `loki doctor` stdout is compared byte for byte between the
405
- # bash route and the Bun route (tests/test-doctor-blocker-parity.sh, and the
406
- # bun-parity workflow). Two independent renderers drift; one shared renderer
407
- # cannot. This is the same parity-by-construction seam the install offer and the
404
+ # WHY IT LIVES HERE. The provider priority order lives in ONE bash function;
405
+ # doctor (the Bun implementation, loki-ts/src/commands/doctor.ts) calls this
406
+ # rather than carrying a second copy. Two independent renderers drift; one shared
407
+ # renderer cannot. This is the same parity-by-construction seam the install offer and the
408
408
  # detect-sdk probe already use, so doctor.ts calls this instead of reimplementing
409
409
  # the priority list in TypeScript.
410
410
  #
package/autonomy/run.sh CHANGED
@@ -22698,16 +22698,24 @@ build_prompt() {
22698
22698
  # configuration there), or LOKI_GOAL_SCORING=0. Advisory only: it never
22699
22699
  # blocks a build and never rewrites the user's goal.
22700
22700
  local goal_sharpening_instruction=""
22701
- if [ -n "${COMPLETION_PROMISE:-}" ] \
22701
+ # Bash-native trim, matching (goal ?? "").trim() in goal_score.ts: a
22702
+ # whitespace-only promise is an absent goal, not an unmeasurable one.
22703
+ local _goal_trim="${COMPLETION_PROMISE:-}"
22704
+ _goal_trim="${_goal_trim#"${_goal_trim%%[![:space:]]*}"}"
22705
+ _goal_trim="${_goal_trim%"${_goal_trim##*[![:space:]]}"}"
22706
+ if [ -n "$_goal_trim" ] \
22702
22707
  && [ "${LOKI_GOAL_SCORING:-}" != "0" ] \
22703
22708
  && [ "${AUTONOMY_MODE:-}" != "perpetual" ] \
22704
22709
  && [ "${PERPETUAL_MODE:-}" != "true" ] && [ "${PERPETUAL_MODE:-}" != "1" ]; then
22705
22710
  local _goal_lc _goal_dims
22706
- _goal_lc="$(printf '%s' "$COMPLETION_PROMISE" | tr '[:upper:]' '[:lower:]')"
22711
+ _goal_lc="$(printf '%s' "$_goal_trim" | tr '[:upper:]' '[:lower:]')"
22707
22712
  _goal_dims=0
22708
22713
  # Mirrors the four DIMENSIONS regexes in goal_score.ts, in the same order.
22709
22714
  printf '%s' "$_goal_lc" | grep -qE '[0-9]+(\.[0-9]+)?[[:space:]]*(%|ms|s\b|sec|second|min|minute|hour|day|kb|mb|gb|rps|qps|req|x\b|users?|items?|rows?)' && _goal_dims=$((_goal_dims + 1))
22710
- printf '%s' "$_goal_lc" | grep -qE '\b(under|below|less than|no more than|at most|over|above|greater than|at least|within|between|<=?|>=?)\b' && _goal_dims=$((_goal_dims + 1))
22715
+ # The symbol alternatives (<, <=, >, >=) cannot use grep's \b: BSD and GNU
22716
+ # disagree on it next to a non-word character. TS \b(<=?)\b means a word
22717
+ # character on BOTH sides of the symbol, so spell that out explicitly.
22718
+ printf '%s' "$_goal_lc" | grep -qE -e '\b(under|below|less than|no more than|at most|over|above|greater than|at least|within|between)\b' -e '[A-Za-z0-9_](<=?|>=?)[A-Za-z0-9_]' && _goal_dims=$((_goal_dims + 1))
22711
22719
  printf '%s' "$_goal_lc" | grep -qE '\b(latency|throughput|p50|p95|p99|uptime|error rate|conversion|coverage|score|accuracy|precision|recall|bundle size|load time|response time|memory|cpu|cost)\b' && _goal_dims=$((_goal_dims + 1))
22712
22720
  printf '%s' "$_goal_lc" | grep -qE '\b(tests? pass|builds? clean|endpoint|returns? [0-9]{3}|exit code|schema|migration|deploys?|renders?|compiles?)\b' && _goal_dims=$((_goal_dims + 1))
22713
22721
  # Only a goal with ZERO measurable dimensions is flagged (score == 0),
package/bin/loki CHANGED
@@ -115,6 +115,14 @@ else
115
115
  exec "$BASH_CLI" "$@"
116
116
  fi
117
117
 
118
+ # FC-30: an install artifact must not outlive the install that made it. If a provider
119
+ # skill link Loki created is dangling or points at another Loki install, repoint it
120
+ # to this one. A few lstat/readlink calls, no network, silent when already correct.
121
+ if [ -f "$REPO_ROOT/autonomy/lib/skill-link-heal.sh" ]; then
122
+ # shellcheck source=../autonomy/lib/skill-link-heal.sh
123
+ source "$REPO_ROOT/autonomy/lib/skill-link-heal.sh" 2>/dev/null && loki_skill_link_heal "$REPO_ROOT" || true
124
+ fi
125
+
118
126
  # Shell completions install themselves: once per VERSION, on the first interactive
119
127
  # run after an install or upgrade (stamp: ~/.loki/completions-version). Never needed
120
128
  # by the user; skipped under CI, LOKI_NO_COMPLETIONS=1 and non-TTY stdout. Failure
@@ -529,6 +537,20 @@ if [ "${1:-}" = "doctor" ]; then
529
537
  exec "$BASH_CLI" "$@"
530
538
  fi
531
539
  done
540
+ # FC-DUP: bun is the single doctor. A crashing or missing Bun route must not
541
+ # leave the user with a stack trace and no verdict, so preflight it here and
542
+ # fall to bash, whose cmd_doctor prints the minimal diagnostic and exits 1.
543
+ # Uses the _loki_bun resolver (PATH, then @oven/bun-*), not bare `command -v`.
544
+ _doctor_bun="$(_loki_bun)"
545
+ if [ -z "$_doctor_bun" ] || [ -z "${BUN_CLI:-}" ]; then
546
+ exec "$BASH_CLI" "$@"
547
+ fi
548
+ _doctor_t=$(command -v timeout || command -v gtimeout || true)
549
+ if [ -n "$_doctor_t" ]; then
550
+ "$_doctor_t" -k 1 5 "$_doctor_bun" "$BUN_CLI" version >/dev/null 2>&1 || exec "$BASH_CLI" "$@"
551
+ else
552
+ "$_doctor_bun" "$BUN_CLI" version >/dev/null 2>&1 || exec "$BASH_CLI" "$@"
553
+ fi
532
554
  fi
533
555
 
534
556
  # CLI consolidation (Phase B): `report kpis` is the canonical form of the
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
- // npm postinstall: install shell completions. Must never fail the install.
2
+ // npm postinstall: repoint stale skill links (FC-30) and install shell completions. Must never fail the install.
3
3
  try {
4
4
  const { spawnSync } = require("node:child_process");
5
5
  const path = require("node:path");
6
+ const root = path.join(__dirname, "..");
7
+ spawnSync("bash", ["-c", '. "$1/autonomy/lib/skill-link-heal.sh" && loki_skill_link_heal "$1"', "_", root], { stdio: ["ignore", "ignore", "inherit"], timeout: 10000 });
6
8
  if (!process.env.CI && process.env.LOKI_NO_COMPLETIONS !== "1") {
7
9
  spawnSync(path.join(__dirname, "loki"), ["completion", "--postinstall"], { stdio: "ignore", timeout: 20000 });
8
10
  }
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "11.2.0"
10
+ __version__ = "11.2.2"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -1,6 +1,6 @@
1
1
  # CLI reference
2
2
 
3
- Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.2.0. Do not edit by hand.
3
+ Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.2.2. Do not edit by hand.
4
4
  Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for the full inventory.
5
5
 
6
6
  | Command | Description |
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v11.2.0
5
+ **Version:** v11.2.2
6
6
 
7
7
  **Engine note:** `loki "<task>"`, `loki owner/repo#N` and `loki quick` run the Loki 10 engine. `loki start` still routes to the legacy engine, which is being removed (planned work resumes 2026-10-07); prefer `loki owner/repo#N`. See [docs/v10/GUIDE.md](v10/GUIDE.md).
8
8
 
@@ -11,7 +11,6 @@ autonomy/loki: line 14066: status: command not found
11
11
  **Not user-affecting, and not a JSON defect.** stdout is 5176 bytes of valid
12
12
  JSON; only stderr is polluted. Every `doctor --json` assertion in the repo
13
13
  discards stderr (`tests/test-airgap-commands.sh:50`,
14
- `tests/test-doctor-blocker-parity.sh:129,132`,
15
14
  `tests/test-doctor-ci-gateable.sh:46,84,86`).
16
15
  `tests/test-e2e-features.sh:104` was the lone outlier using `2>&1` and has been
17
16
  brought into line.
@@ -235,11 +235,11 @@ Architect: opus. Source: the founder directive (2026-09-30, from building to pro
235
235
  - The last line is exactly one of:
236
236
  - `Ready: <provider> (<model>), receipts signed (kid <8>)`
237
237
  - the one blocking reason.
238
- - Files: loki-ts/src/commands/doctor.ts, autonomy/loki (cmd_doctor only, for bash parity), loki-ts/tests/commands/doctor.test.ts, tests/test-doctor-providers.sh, tests/test-doctor-optional-skill-not-blocking.sh, tests/test-doctor-blocker-parity.sh.
238
+ - Files: loki-ts/src/commands/doctor.ts, autonomy/loki (cmd_doctor only, for bash parity), loki-ts/tests/commands/doctor.test.ts, tests/test-doctor-providers.sh, tests/test-doctor-optional-skill-not-blocking.sh, tests/test-doctor-single-impl.sh (replaces the deleted tests/test-doctor-blocker-parity.sh).
239
239
  - Wall check:
240
240
  - A HOME with a broken ~/.cline skill symlink and LOKI_PROVIDER=claude: doctor exits 0 and prints one Ready line.
241
241
  - With claude missing, it exits non-zero and names the blocker.
242
- - Commands: `cd loki-ts && bun test tests/commands/doctor.test.ts && cd .. && bash tests/test-doctor-providers.sh && bash tests/test-doctor-optional-skill-not-blocking.sh && bash tests/test-doctor-blocker-parity.sh`
242
+ - Commands: `cd loki-ts && bun test tests/commands/doctor.test.ts && cd .. && bash tests/test-doctor-providers.sh && bash tests/test-doctor-optional-skill-not-blocking.sh && bash tests/test-doctor-single-impl.sh`
243
243
  - Tier: MEDIUM. Budget: net 0 or less.
244
244
  - Depends: A-122 (doctor.ts, autonomy/loki). Wave 3.
245
245
 
@@ -731,3 +731,5 @@ PROGRESS.md (the FC-25 P9 entry) cites a D88 CI waiver that was never recorded i
731
731
  10. Accepted risk (CTO, 2026-10-07): B9 measures about 5 of the 14 or more Repo Shape Matrix shapes, so an unmeasured shape starts on Haiku without shape-level parity evidence; the backstops are the in-run escalation ladder (Haiku -> Sonnet 5.5 before STALLED, so a miss costs time and money, not correctness) and the H4 history floor, the receipt records shape parity as NOT PROVEN for any shape with no B9 row, and each matrix shape gains a B9 row as it becomes runnable.
732
732
 
733
733
  Amendment to D89 (2026-10-07 21:40Z, CTO decision, refined by the founder at 21:45Z, CoS records). This amends only R1-19, the default-on flip that follows the CTO gate; 11.1.0 and 11.2.0 still ship the router OFF. (1) The default executor is Sonnet 5.5; Haiku 5.5 per work unit when Opus assigns it. (2) Roles: Opus 5.5 is the architect, router and advisor, plans and assigns each unit, and is consulted on hard calls. Sonnet is the senior engineer and the default for implementation and fixes. Haiku does tests, docs and small low-risk changes only when Opus assigns them in the plan. Haiku is never the blanket default. (3) L0 holds: the harness never routes by task type or path regex; it only executes, verifies and escalates. (4) The route schema is `units: [{id, kind, executor, reason}]` plus a run default of sonnet; a missing or invalid unit route goes to sonnet with NOT PROVEN. A haiku unit that fails a code-owned check is redone on sonnet, then escalates to opus; a Wall assigned to haiku gets an Opus review before implement. The receipt lists the executor, assigner and reason per unit. The per-unit schema lands in 11.2.0 or 11.2.1 depending on what is merged. (5) Escalation stays evidence-gated (haiku -> sonnet -> opus, and sonnet -> opus), and `LOKI_ROUTER=0` equals pre-router behavior exactly (the R1-21 golden). (6) The R1-16 shape-defaults semantics are inverted (a shape is listed as `haiku` once it has earned it; an absent listing means Sonnet), and R1-16 history is an input to Opus, not a harness rule. Rationale: a wrong PR costs the user far more than the token savings, B9 covers only about 5 of 14 shapes (item 10), and under L0 a model earns the cheaper tier with evidence. Items 3, 5, 7 and 9 above are read with this amendment; their text is kept as recorded. See docs/v11/ROUTER-1.md sections 4.1, 4.3 and 4.6.
734
+
735
+ Amendment 2 to D89 (2026-10-08 00:15Z, steering finding, CoS records; tightens a gate, so it binds until the CTO relaxes it). R1-19 gains one more gate line: on the trivial fixture (the steering Node off-by-one fixture), router-on cost must be <= router-off cost x 1.05, measured with n>=3 per arm and recorded per arm as cache-read input tokens, fresh input tokens, output tokens, dollars and wall time (R1-17 B9). Evidence that prompted it (n=1, may be variance): 11.2.0 smoke, router on cost +15% tokens, +27% dollars and +38s with zero advisor calls and no route change. If the advisor tool definition costs tokens on sessions that never consult it, the advisor is enabled only on the sessions Opus marks as needing it (plan and fix rounds), not on every stage. Measure before changing code.
@@ -331,6 +331,42 @@ L0 review rule (ENGINE-LAWS.md L0): a fix that adds an `if` or a regex about the
331
331
  - User saw: tests/test-proof-verify-jwks.sh passed 108/0 in 33s in a fresh worktree and hung until killed when run from the main checkout. The CI-style run was green and the founder's own run was red, so the suite read as flaky.
332
332
  - Mechanism (measured): `loki proof verify` and `loki_remote_verify_receipt` run `proof-verify.py "$pj" "${TARGET_DIR:-.}"`. With TARGET_DIR unset the verifier runs `compute_tree_digest` on the caller's cwd, whose filesystem walk visited 1,054,266 paths (13.3s just to enumerate, then it hashes them) because `.claude/worktrees` (190 worktrees, 32G) sits inside the checkout. A fresh worktree has a few thousand files.
333
333
  - Law: L2 (a verdict is about the tree it names, never an ambient one), and the test hermeticity rule: a test owns its fixture tree.
334
- - Siblings: swept every tests/*.sh spawn of `proof verify` and `loki_remote_verify_receipt`; only the jwks test lacked TARGET_DIR. Direct `proof-verify.py` runs pass the repo as an argument. Other `${TARGET_DIR:-.}` readers in autonomy/loki (HANDOFF.md, failover.json, .loki dir) are not swept yet.
334
+ - Siblings: swept every tests/*.sh spawn of `proof verify` and `loki_remote_verify_receipt`; only the jwks test lacked TARGET_DIR. Direct `proof-verify.py` runs pass the repo as an argument. Sibling sweep (11.2.0 lane 10): of every `${TARGET_DIR:-.}` reader in autonomy/, five spawn sites were unsafe: the Bun `loki proof verify` (loki-ts/src/commands/proof.ts, the default route for a plain `verify <id>`), the bash `loki proof verify` (human and JSON, reached by flags or LOKI_LEGACY_BASH), `loki_remote_verify_receipt` and `_deploy_receipt_verdict` all handed the verifier the caller's cwd (it digests the tree when cwd is a repo). Safe, each reads or writes fixed paths and walks nothing: cmd_deploy detectors (test -f on named files), cmd_failover (one failover.json), `loki handoff --md` (writes one HANDOFF.md), crash.sh (writes .loki/crash), spec-interrogation.sh (five named manifests). app-runner.sh and completion-council.sh run only inside a `loki start` run, where run.sh sets TARGET_DIR to the project.
335
335
  - Mechanism of the fix: every verifier spawn in the jwks test sets TARGET_DIR to its fixture; tests/test-verifier-spawn-sets-target-dir.sh fails any test that spawns the verifier without TARGET_DIR or a cd into an owned directory.
336
+ - Shared mechanism for the sweep: `loki_verify_root` in autonomy/loki and its port `resolveVerifyRoot` in loki-ts/src/commands/proof.ts are the one resolver (TARGET_DIR, parent of an absolute LOKI_DIR, cwd or repo top-level holding .loki, else a plain refusal and exit 2 NOT CHECKED; HOME and / are never roots, a trailing slash is normalized first). The first sweep fixed only the bash copy; the HIGH review found the Bun route still defaulting to ".".
337
+ - Sweep fixture: tests/test-verifier-root-not-ambient-cwd.sh (runs `proof verify` through bin/loki on both the default and legacy routes; source guards cover autonomy/loki and loki-ts/src) and loki-ts/tests/commands/proof_verify_root.test.ts (the TS resolver, red before the port). The bash source guard goes red when a spawn passes `${TARGET_DIR:-.}` again.
336
338
  - Fixture: tests/test-verifier-spawn-sets-target-dir.sh, mutated against the pre-fix jwks test (7 hits). tests/test-proof-verify-jwks.sh run with cwd = the main checkout.
339
+
340
+ ## FC-30 An install artifact outlives the install that made it
341
+ - User saw: after upgrading to 11.1.0, ~/.claude/skills/loki-mode was a symlink to ~/.bun/install/global/node_modules/loki-mode, which no longer existed. `loki doctor` FAILed and told the user to run `loki setup-skill`, an extra command the product should never ask for.
342
+ - Mechanism (measured): `cmd_setup_skill` creates the provider skill links once and only on request. Nothing re-checked them on install, upgrade or first run, so a package-manager move or reinstall left a dangling link (or one to the previous install). Raw cause is a one-shot writer with no owner for the link's lifetime.
343
+ - Law: L0 (the harness repairs what it can verify is its own instead of delegating to the user), L2 (the doctor verdict is about the host as it is, and the fix must make that true without a command).
344
+ - Siblings: swept every place Loki writes an install-time path. Provider skill links ~/.claude, ~/.codex, ~/.cline, ~/.aider skills/loki-mode (all four covered by the shared mechanism); `cmd_remote` ln -sf of the Claude skill link in autonomy/loki (only creates when SKILL.md is absent, covered by the same repair on the next run); shell completions via `completion --postinstall` and the shim (already self-healing, stamped by VERSION in ~/.loki/completions-version); no PATH shims or bin links are written by Loki (the package manager owns bin/loki). Real directories and foreign links are deliberately left to doctor.
345
+ - Mechanism of the fix: autonomy/lib/skill-link-heal.sh `loki_skill_link_heal` repoints a symlink only when its target is a Loki install path (node_modules/loki-mode or a Homebrew loki-mode dir) that is missing or is a different install than the running one, and only when the running root is itself an install path. Atomic temp link plus rename, one stderr line, silent no-op when correct, LOKI_NO_SKILL_LINK_HEAL=1 opts out. Called from bin/loki-postinstall.js (install and upgrade) and the bin/loki shim (first run).
346
+ - Fixture: tests/test-skill-link-heal.sh (dangling, other install, real dir, foreign link, correct link, opt-out, dev checkout), red before autonomy/lib/skill-link-heal.sh existed.
347
+
348
+ ## FC-31 A slice gate skips the suites that guard its own files, and main goes red in Tier B
349
+ - User saw: main 27447cf46 failed Tier B on tests/test-doctor-blocker-parity.sh (run 37697291383). The slice R1-20 (8ff988516) changed loki-ts/src/commands/doctor.ts and gated only on tsc and bun test. The local-ci fast tier had timed out (rc=124) on a loaded host, so no shell suite ran.
350
+ - Law: L2 (a verdict is about the tree it names; a gate that did not run the guarding suites says nothing about them) and the evidence rule (a timeout is not a pass).
351
+ - Cause (measured): scripts/select-tests.sh rule R4 handled loki-ts/src files with `continue` after selecting bun tests, so rule R3 (shell suites that reference the file) never ran for them. test-doctor-blocker-parity.sh runs `bin/loki doctor`, never the string doctor.ts, so no path or basename match could have found it anyway.
352
+ - Mechanism: one shared mapping, scripts/select-tests.sh. R4 now also selects shell suites that name the full path, and for loki-ts/src/commands/X.ts those that run `loki X`. One gate command, `bash scripts/local-ci.sh --impacted <base>` (scripts/impacted-gate.sh), runs only the selected shell and moat suites, each under `timeout -k`, prints per-suite rc, and exits non-zero on any failure or timeout.
353
+ - Siblings swept (file-to-suite mapping by source tree): loki-ts/src was the only tree whose rule skipped the shell grep (fixed). mcp/, providers/, autonomy/ (other than run.sh and loki), scripts/ get R3 path and basename grep. dashboard/ gets R3 plus the R5 pytest area. web-app/ gets R3 plus R5. Still thin: a loki-ts/src file that is not a command module (runner/, util/) maps to shell suites by full path only, not by the subcommand a suite drives; dashboard and web-app shell suites that drive an API route by URL are not mapped to the handler file; mcp/server.py tools are not mapped to suites that call the tool by name. Follow-up: a per-tree needle table once a second miss appears.
354
+ - Fixture: tests/test-select-tests.sh, "FC-31: doctor.ts selects doctor parity shell suite" (red before the fix: 39 passed, 1 failed).
355
+
356
+ ## FC-32 Hand-maintained file lists drift from the import graph
357
+ - User saw: Tier B run 37706008817 on main 6a01413bd failed two suites: "no hardcoded home-directory paths in tests" (and its cascade, "Structural checks catch planted defects (D44-C)") and "Loki control plane wiring (CP-04)". Both slices had passed tsc and the full bun test.
358
+ - Law: L2 (a verdict is about the tree it names; a gate that did not run the guarding suites says nothing about them).
359
+ - Cause (measured): faf1d0ace added tests/test-parity-goal-score.sh with hardcoded /opt/homebrew/bin and /usr/local/bin grep paths (flagged by tests/test-no-hardcoded-paths.sh). The R1 router slices (0f3e3d55a, c15b8157b, 1b9e479c1) added loki-ts/src/engine10/cost.ts, runner/router/flag.ts and runner/router/route_block.ts to the control-plane value-import closure, but Dockerfile.control-plane lists its COPY files by hand. RC-SLICE-SHELL-GATE: the FC-31 impacted gate selected neither shell structural guard, including for a touched tests/*.sh.
360
+ - Mechanism (this slice, minimal): the parity test discovers greps with `type -ap grep ggrep` (no absolute paths); the three files are added to Dockerfile.control-plane. 11.2.2 follow-up (root fix): replace the hand-maintained COPY list with a directory COPY or a generated list, make CP-04 build the image and import the bundle, and extend scripts/select-tests.sh so a touched tests/*.sh selects the hardcoded-path and structural-checks suites and a loki-ts/src file reachable from packages/control-plane selects CP-04.
361
+ - Siblings swept: tests/test-no-hardcoded-paths.sh scans every tests/ file (3 passed, 0 failed after the fix); the CP-04 closure guard covers every loki-ts/src value import (25 passed, 0 failed); no other hits.
362
+ - Fixture: the two existing guards, red on 6a01413bd and green here.
363
+
364
+ ## FC-33 Duplicate implementations kept in sync by tests (FC-DUP)
365
+ - User saw: `loki doctor` printed different checks, counts and verdicts depending on the route (bash vs bun), which `bin/loki` picks from bun on PATH, LOKI_LEGACY_BASH, a missing dist, LOKI_TS_ENTRY or `--airgap`. Two parity tests (test-doctor-blocker-parity.sh, test-doctor-install-integrity-parity.sh) existed only to keep the two copies equal, and each new doctor check had to be written twice. Raw Claude Code has one doctor.
366
+ - Law: L2 (one verdict has one author), L0 (the harness must not hold two answers to one question).
367
+ - Siblings (twin sweep):
368
+ - Command twins and their guards: version, provider, memory (loki-ts/tests/all-ported.test.ts); stats (tests/test-bash-bun-parity.sh check 6); proof verify (loki-ts/tests/proof_verify_parity.test.ts); crash (crash.test.ts:308); status (status.test.ts:398); rollback, trust, wiki (unguarded).
369
+ - Runner twins: run.sh vs loki-ts/src/runner, guarded by test-bash-bun-parity.sh, test-parity-*.sh, the gate-failures-cap parity test and test-pricing-parity. goal_score.ts vs run.sh has NO guard. These collapse when the D57 default-loop flip lands.
370
+ - Mechanism: `_loki_bun_delegate <cmd> "$@"` in autonomy/loki (bun resolved PATH then @oven/bun-*, dist then src, `version` preflight under 5s, then exec). Doctor is the first command moved onto it; bash keeps only a minimal "bun route unavailable" diagnostic. `cmd_control` is the precedent. Remaining twins move onto the same helper as their slices land.
371
+ - Known gap: the delegate preflight only runs `<cli> version`. A throw inside doctor.ts AFTER the CLI loads (during the doctor run itself) is not covered by the preflight and surfaces as that crash, not the minimal fallback.
372
+ - Fixture: tests/test-doctor-single-impl.sh.