loki-mode 11.2.0 → 11.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) 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 +60 -6
  5. package/autonomy/run.sh +11 -3
  6. package/bin/loki +8 -0
  7. package/bin/loki-postinstall.js +3 -1
  8. package/dashboard/__init__.py +1 -1
  9. package/docs/CLI-REFERENCE.md +1 -1
  10. package/docs/INSTALLATION.md +1 -1
  11. package/docs/v10/DECISIONS.md +2 -0
  12. package/docs/v10/FAILURE-CLASSES.md +27 -1
  13. package/loki-ts/dist/loki.js +612 -609
  14. package/mcp/__init__.py +1 -1
  15. package/package.json +1 -1
  16. package/packages/control-plane/dist/server.js +252 -65
  17. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  18. package/web-app/dist/assets/{AdminPage-Bs4Vyd9R.js → AdminPage-BZ1QNSju.js} +1 -1
  19. package/web-app/dist/assets/{Avatar-Dmf_rtmv.js → Avatar-Dnzzsk2U.js} +1 -1
  20. package/web-app/dist/assets/{Badge-DCEdgEN8.js → Badge-DWtsAw6M.js} +1 -1
  21. package/web-app/dist/assets/{Button-BpGXXEPk.js → Button-zEMFjJEr.js} +1 -1
  22. package/web-app/dist/assets/{CockpitPage-CzssVmim.js → CockpitPage-Ddo5Zhnz.js} +1 -1
  23. package/web-app/dist/assets/{ComparePage-BU4XtB9F.js → ComparePage-kCkyRrrz.js} +1 -1
  24. package/web-app/dist/assets/{ErrorBoundary-Bkjf4KP2.js → ErrorBoundary-BZrVi8Ui.js} +1 -1
  25. package/web-app/dist/assets/{EvidenceReceiptPanel-CcGMfaq7.js → EvidenceReceiptPanel-B9eDIWUr.js} +1 -1
  26. package/web-app/dist/assets/{GitHubIssuesPanel-D53leEFJ.js → GitHubIssuesPanel-DX5A2UkV.js} +1 -1
  27. package/web-app/dist/assets/{GitHubPRsPanel-UVsPtV3-.js → GitHubPRsPanel-DKKxBe6-.js} +1 -1
  28. package/web-app/dist/assets/{HomePage-D9wO-XTX.js → HomePage-BSn_-fHq.js} +2 -2
  29. package/web-app/dist/assets/{LoginPage-ChxBSele.js → LoginPage-C1BGwj0s.js} +1 -1
  30. package/web-app/dist/assets/{MagicPage-BqQDKX2L.js → MagicPage-B9JJw_BQ.js} +1 -1
  31. package/web-app/dist/assets/{MetricsPage-CA22NkPl.js → MetricsPage-B-lNQr8Q.js} +1 -1
  32. package/web-app/dist/assets/{NotFoundPage-oYt5qdHQ.js → NotFoundPage-DkktBNvI.js} +1 -1
  33. package/web-app/dist/assets/{ProjectPage-tSAdI6A6.js → ProjectPage-DUAraQw9.js} +3 -3
  34. package/web-app/dist/assets/{ProjectsPage-Dk22bicF.js → ProjectsPage-DgMo4QHm.js} +1 -1
  35. package/web-app/dist/assets/{SettingsPage-CwDSvMn6.js → SettingsPage-fnL3nqGC.js} +1 -1
  36. package/web-app/dist/assets/{ShowcasePage-By3IMy9k.js → ShowcasePage-Bn7rWTXn.js} +1 -1
  37. package/web-app/dist/assets/{SystemSettingsPage-DcWNR28_.js → SystemSettingsPage-DxrytIXT.js} +1 -1
  38. package/web-app/dist/assets/{TeamsPage-CFawyYQg.js → TeamsPage-BQDC12Mn.js} +1 -1
  39. package/web-app/dist/assets/{TemplatesPage-D7pCD-63.js → TemplatesPage-C_JEygcZ.js} +1 -1
  40. package/web-app/dist/assets/{TerminalOutput-CT2v1Vm2.js → TerminalOutput-H0584wn0.js} +1 -1
  41. package/web-app/dist/assets/{activity-DW6jRbjj.js → activity-Bse7FP2H.js} +1 -1
  42. package/web-app/dist/assets/{bell-CJOu9IIM.js → bell-BMtgCGSW.js} +1 -1
  43. package/web-app/dist/assets/{bot-dVt0Xw_X.js → bot-6SW_XTZN.js} +1 -1
  44. package/web-app/dist/assets/{check-BVC4A8YK.js → check-C3f9aTHx.js} +1 -1
  45. package/web-app/dist/assets/{chevron-left-Djgr5pg-.js → chevron-left-Bi60XjBc.js} +1 -1
  46. package/web-app/dist/assets/{circle-alert-OE2reifn.js → circle-alert-Bepfb6dn.js} +1 -1
  47. package/web-app/dist/assets/{clock-BUUN5rWU.js → clock-jQIU5AuJ.js} +1 -1
  48. package/web-app/dist/assets/{cloud-CzPIksNz.js → cloud-BLZn6J9A.js} +1 -1
  49. package/web-app/dist/assets/{code-xml-DlE7vlGY.js → code-xml-CRgKi92z.js} +1 -1
  50. package/web-app/dist/assets/{database-DUauMcIC.js → database-DWg9XT6Y.js} +1 -1
  51. package/web-app/dist/assets/{dollar-sign-CBx9DWbM.js → dollar-sign-Cfif5nlf.js} +1 -1
  52. package/web-app/dist/assets/{file-code-corner-hTmaP9Mz.js → file-code-corner-DSiLXED8.js} +1 -1
  53. package/web-app/dist/assets/{file-plus-DimFEqtO.js → file-plus-8lIzYfAS.js} +1 -1
  54. package/web-app/dist/assets/{globe-BV7yxcQ_.js → globe-DZkvR077.js} +1 -1
  55. package/web-app/dist/assets/{hammer-CW6Ol_ju.js → hammer-BtQyfpf_.js} +1 -1
  56. package/web-app/dist/assets/{index-83w75NC1.js → index-B-uQiPRR.js} +3 -3
  57. package/web-app/dist/assets/{layers-DMAqCHxh.js → layers-Dlfo3jwV.js} +1 -1
  58. package/web-app/dist/assets/{loader-circle-B-TJNPo5.js → loader-circle-BQ2hL7m5.js} +1 -1
  59. package/web-app/dist/assets/{lock-DbvZW7dV.js → lock-LzpJKEAP.js} +1 -1
  60. package/web-app/dist/assets/{package-BWFcV2kS.js → package-zp-qZxpp.js} +1 -1
  61. package/web-app/dist/assets/{plus-CjPzASFv.js → plus-CuiDkGsm.js} +1 -1
  62. package/web-app/dist/assets/{refresh-cw-DJL2nFq0.js → refresh-cw-Dx57uk3-.js} +1 -1
  63. package/web-app/dist/assets/{rotate-ccw-Bq3vK-Tu.js → rotate-ccw-Bp1YOkM6.js} +1 -1
  64. package/web-app/dist/assets/{scroll-text-CAMgtDYH.js → scroll-text-Dc8LjRT5.js} +1 -1
  65. package/web-app/dist/assets/{server-D71_Bywf.js → server-CE0Mzyfz.js} +1 -1
  66. package/web-app/dist/assets/{shield-alert-DqQ5wzQ6.js → shield-alert-DBj71EDP.js} +1 -1
  67. package/web-app/dist/assets/{thumbs-up-DXE_IT36.js → thumbs-up-C0q9J-0y.js} +1 -1
  68. package/web-app/dist/assets/{trash-2-VYAPKajq.js → trash-2-YNPSnz_h.js} +1 -1
  69. package/web-app/dist/assets/{trending-down-CuE-J2LH.js → trending-down-BlyQB_Gh.js} +1 -1
  70. package/web-app/dist/assets/{trending-up-BvdD9XmQ.js → trending-up-CStScgAm.js} +1 -1
  71. package/web-app/dist/assets/{upload-OM2YFziE.js → upload-CLs-rKu9.js} +1 -1
  72. package/web-app/dist/assets/{usePolling-Bk39fp_d.js → usePolling-BmFkptqQ.js} +1 -1
  73. package/web-app/dist/assets/{user-CWmKQURs.js → user-CV0TBz6A.js} +1 -1
  74. package/web-app/dist/index.html +1 -1
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: An autonomous software factory that knows what it is supposed to deliver, and proves it did. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
4
4
  ---
5
5
 
6
- # Loki Mode v11.2.0
6
+ # Loki Mode v11.2.1
7
7
 
8
8
  **Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the legacy engine, which still ships and is being removed (`loki start owner/repo#N` still routes to it; prefer `loki owner/repo#N`).
9
9
 
@@ -483,4 +483,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
483
483
 
484
484
  ---
485
485
 
486
- **v11.2.0 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
486
+ **v11.2.1 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 11.2.0
1
+ 11.2.1
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bash
2
+ # FC-30: keep the provider skill links Loki created pointing at the running
3
+ # install. One shared mechanism, called from npm postinstall and from the bin/loki
4
+ # shim (first invocation after an install or upgrade). Cost: a few lstat/readlink
5
+ # calls, no network, no subprocess beyond readlink/ln/mv on the repair path.
6
+ #
7
+ # Opt out with LOKI_NO_SKILL_LINK_HEAL=1.
8
+ #
9
+ # A link is repointed only when ALL hold:
10
+ # - it is a symlink (a real directory or file is never touched)
11
+ # - its target is a Loki install path (…/node_modules/loki-mode, or a Homebrew
12
+ # Cellar/opt loki-mode dir), so a foreign link is never touched
13
+ # - its target is missing, or resolves to a different install than the running one
14
+ # - the running root is itself an install path with a SKILL.md, so a dev checkout
15
+ # or worktree never hijacks the user's global link
16
+
17
+ _loki_skill_is_install_path() {
18
+ case "${1%/}" in
19
+ */node_modules/loki-mode | */Cellar/loki-mode/* | */opt/loki-mode | */opt/loki-mode/libexec) return 0 ;;
20
+ esac
21
+ return 1
22
+ }
23
+
24
+ loki_skill_link_heal() {
25
+ [ "${LOKI_NO_SKILL_LINK_HEAL:-}" = "1" ] && return 0
26
+ local root="${1:-}" home="${HOME:-}" root_p rel link tgt live_p tmp
27
+ [ -n "$root" ] && [ -n "$home" ] || return 0
28
+ [ -f "$root/SKILL.md" ] || return 0
29
+ root_p="$(cd "$root" 2>/dev/null && pwd -P)" || return 0
30
+ _loki_skill_is_install_path "$root_p" || _loki_skill_is_install_path "$root" || return 0
31
+
32
+ for rel in .claude .codex .cline .aider; do
33
+ link="$home/$rel/skills/loki-mode"
34
+ [ -L "$link" ] || continue
35
+ tgt="$(readlink "$link" 2>/dev/null)" || continue
36
+ case "$tgt" in /*) ;; *) tgt="$(dirname "$link")/$tgt" ;; esac
37
+ _loki_skill_is_install_path "$tgt" || continue
38
+ if [ -e "$link" ]; then
39
+ live_p="$(cd "$link" 2>/dev/null && pwd -P)" || live_p=""
40
+ [ "$live_p" = "$root_p" ] && continue
41
+ fi
42
+ tmp="${link}.heal.$$"
43
+ rm -f "$tmp" 2>/dev/null
44
+ ln -s "$root_p" "$tmp" 2>/dev/null || continue
45
+ # Atomic replace. -T (GNU) / -h (BSD) stop mv from descending into a
46
+ # link that points at a directory.
47
+ if mv -fT "$tmp" "$link" 2>/dev/null || mv -fh "$tmp" "$link" 2>/dev/null; then
48
+ printf 'loki: repointed skill link %s -> %s (was %s)\n' "$link" "$root_p" "$tgt" >&2
49
+ else
50
+ rm -f "$tmp" 2>/dev/null
51
+ fi
52
+ done
53
+ return 0
54
+ }
package/autonomy/loki CHANGED
@@ -1150,6 +1150,53 @@ except Exception:
1150
1150
  PY
1151
1151
  }
1152
1152
 
1153
+ # loki_verify_root: the ONE place that decides which tree the receipt verifier
1154
+ # hashes (FC-29). The verifier walks and digests the directory it is given, so
1155
+ # an ambient cwd (a huge checkout, $HOME, a non-project directory) must never be
1156
+ # the default. Order: explicit TARGET_DIR; the parent of an absolute LOKI_DIR;
1157
+ # the cwd if it holds .loki; the repo top-level if it holds .loki. Anything else
1158
+ # prints one plain sentence on stderr and returns 1 (callers report NOT CHECKED).
1159
+ loki_verify_root() {
1160
+ # Helpers live inside so callers that extract this one function stay whole.
1161
+ _loki_verify_trim() {
1162
+ local p="$1"
1163
+ while [ "${#p}" -gt 1 ] && [ "${p%/}" != "$p" ]; do p="${p%/}"; done
1164
+ printf '%s' "$p"
1165
+ }
1166
+
1167
+ _loki_verify_canon() {
1168
+ local p r
1169
+ p="$(_loki_verify_trim "$1")"
1170
+ if [ -n "$p" ] && [ -d "$p" ] && r="$(cd "$p" 2>/dev/null && pwd -P)"; then p="$r"; fi
1171
+ printf '%s' "$p"
1172
+ }
1173
+ local d="${TARGET_DIR:-}" top="" _cd _ch
1174
+ d="$(_loki_verify_trim "$d")"
1175
+ if [ -z "$d" ]; then
1176
+ case "${LOKI_DIR:-}" in
1177
+ /*) [ -d "$LOKI_DIR" ] && d="$(dirname "$LOKI_DIR")" ;;
1178
+ esac
1179
+ fi
1180
+ if [ -z "$d" ]; then
1181
+ if [ -d "$PWD/.loki" ]; then
1182
+ d="$PWD"
1183
+ else
1184
+ top="$(git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || true)"
1185
+ if [ -n "$top" ] && [ -d "$top/.loki" ]; then d="$top"; fi
1186
+ fi
1187
+ fi
1188
+ # Compare canonical paths: every trailing slash stripped, symlinks resolved,
1189
+ # identically for the candidate and HOME (mirrored by resolveVerifyRoot in
1190
+ # loki-ts/src/commands/proof.ts).
1191
+ _cd="$(_loki_verify_canon "$d")"
1192
+ _ch="$(_loki_verify_canon "${HOME:-}")"
1193
+ if [ -z "$d" ] || [ "$_cd" = "/" ] || { [ -n "$_ch" ] && [ "$_cd" = "$_ch" ]; }; then
1194
+ echo "Cannot tell which project tree to verify against: set TARGET_DIR or run from a project directory that contains .loki." >&2
1195
+ return 1
1196
+ fi
1197
+ printf '%s\n' "$d"
1198
+ }
1199
+
1153
1200
  loki_remote_verify_receipt() {
1154
1201
  local pj="$1"
1155
1202
  local verifier="${_LOKI_SCRIPT_DIR}/lib/proof-verify.py"
@@ -1181,7 +1228,9 @@ loki_remote_verify_receipt() {
1181
1228
  # empty for a local receipt, which simply means no attestation is checked.
1182
1229
  local _attest_url="${2:-}" _attest=""
1183
1230
  _attest="$(loki_remote_attestation_status "$pj" "$_attest_url")"
1184
- out="$(python3 -E "$verifier" "$pj" "${TARGET_DIR:-.}" 2>/dev/null)"
1231
+ local _vroot
1232
+ _vroot="$(loki_verify_root)" || { echo " NOT VERIFIED: no project tree to check against" >&2; return 0; }
1233
+ out="$(python3 -E "$verifier" "$pj" "$_vroot" 2>/dev/null)"
1185
1234
  hash_ok="$(printf '%s' "$out" | jq -r '.hash_ok // false' 2>/dev/null)"
1186
1235
  gpg_ok="$(printf '%s' "$out" | jq -r '.gpg_ok | tostring' 2>/dev/null)"
1187
1236
 
@@ -9547,8 +9596,9 @@ _deploy_receipt_verdict() {
9547
9596
  printf '%s' "UNCHECKED"; return 0
9548
9597
  fi
9549
9598
 
9550
- local out hash_ok
9551
- out="$(python3 -E "$verifier" "$pj" "${TARGET_DIR:-.}" 2>/dev/null || true)"
9599
+ local out hash_ok _vroot
9600
+ _vroot="$(loki_verify_root 2>/dev/null)" || { printf '%s' "UNCHECKED"; return 0; }
9601
+ out="$(python3 -E "$verifier" "$pj" "$_vroot" 2>/dev/null || true)"
9552
9602
 
9553
9603
  # "The verifier did not RUN" and "the verifier says the hash is bad" are
9554
9604
  # different facts and must not collapse. If they do, a broken python3
@@ -37656,7 +37706,11 @@ print(json.dumps(json.load(open(os.environ['LOKI_PROOF_JSON'])), indent=2))
37656
37706
  echo -e "${YELLOW}NOT CHECKED${NC}: python3 is not on PATH, so the verifier could not run (exit 2)." >&2
37657
37707
  exit 2
37658
37708
  fi
37659
- local _pv_rc=0 _pv_out="" _pv_base_rc=0
37709
+ local _pv_rc=0 _pv_out="" _pv_base_rc=0 _pv_root=""
37710
+ _pv_root="$(loki_verify_root)" || {
37711
+ echo -e "${YELLOW}NOT CHECKED${NC}: no project tree to verify against (exit 2)." >&2
37712
+ exit 2
37713
+ }
37660
37714
  # Captured, not printed directly, so that --jwks (below) can patch
37661
37715
  # it: BACKLOG 49 -- when --jwks later downgrades _pv_rc to 1
37662
37716
  # (ABSENT) or 2 (NOT CHECKED), this output's own "ok: true"/
@@ -37666,9 +37720,9 @@ print(json.dumps(json.load(open(os.environ['LOKI_PROOF_JSON'])), indent=2))
37666
37720
  # route (proof.ts verifyProof) -- ok already agrees with the exit
37667
37721
  # code there, so there is nothing to patch.
37668
37722
  if [ "$_pv_human" -eq 1 ]; then
37669
- _pv_out="$(python3 -E "$verifier" --human "$pj" "${TARGET_DIR:-.}")" || _pv_rc=$?
37723
+ _pv_out="$(python3 -E "$verifier" --human "$pj" "$_pv_root")" || _pv_rc=$?
37670
37724
  else
37671
- _pv_out="$(python3 -E "$verifier" "$pj" "${TARGET_DIR:-.}")" || _pv_rc=$?
37725
+ _pv_out="$(python3 -E "$verifier" "$pj" "$_pv_root")" || _pv_rc=$?
37672
37726
  fi
37673
37727
  _pv_base_rc="$_pv_rc"
37674
37728
 
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
@@ -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.1"
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.1. 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.1
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
 
@@ -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,32 @@ 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.