loki-mode 11.90.0 → 11.92.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 (78) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/completion-council.sh +16 -2
  4. package/autonomy/council-v2.sh +10 -2
  5. package/autonomy/grill.sh +4 -1
  6. package/autonomy/lib/done-recognition.sh +12 -4
  7. package/autonomy/lib/prd-enrich.sh +5 -2
  8. package/autonomy/lib/voter-agents.sh +1 -1
  9. package/autonomy/loki +128 -45
  10. package/autonomy/quickstart.sh +13 -2
  11. package/completions/_loki +7 -4
  12. package/completions/loki.bash +3 -3
  13. package/dashboard/__init__.py +1 -1
  14. package/docs/CLI-REFERENCE.md +1 -1
  15. package/docs/INSTALLATION.md +1 -1
  16. package/docs/v11/VERIFY-PR-SANDBOX.md +1 -1
  17. package/loki-ts/dist/loki.js +1421 -1414
  18. package/mcp/__init__.py +1 -1
  19. package/package.json +2 -2
  20. package/packages/control-plane/dist/server.js +20346 -20211
  21. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  22. package/web-app/dist/assets/{AdminPage-CZL0cONl.js → AdminPage-2eApQhkp.js} +1 -1
  23. package/web-app/dist/assets/{Avatar-tJZxSP21.js → Avatar-V-WXQ0hZ.js} +1 -1
  24. package/web-app/dist/assets/{Badge-B-uFJM_y.js → Badge-uw9uv8IY.js} +1 -1
  25. package/web-app/dist/assets/{Button-DfKU7ctr.js → Button-34AooGBc.js} +1 -1
  26. package/web-app/dist/assets/{CockpitPage-CRNp09jN.js → CockpitPage-DHVPvw4u.js} +1 -1
  27. package/web-app/dist/assets/{ComparePage-Cdvjcs57.js → ComparePage-CXuFkRpn.js} +1 -1
  28. package/web-app/dist/assets/{ErrorBoundary-C_benNK3.js → ErrorBoundary-DpPNWkHN.js} +1 -1
  29. package/web-app/dist/assets/{EvidenceReceiptPanel-Ch_6YkUO.js → EvidenceReceiptPanel-C3Mc9c1x.js} +1 -1
  30. package/web-app/dist/assets/{GitHubIssuesPanel-C_a7mAlw.js → GitHubIssuesPanel-Bsqa3l6c.js} +1 -1
  31. package/web-app/dist/assets/{GitHubPRsPanel-xMr7IKsr.js → GitHubPRsPanel-B0xZbM-K.js} +1 -1
  32. package/web-app/dist/assets/{HomePage-BVe6s1kz.js → HomePage-D0aizXzO.js} +2 -2
  33. package/web-app/dist/assets/{LoginPage-DKlLaNw0.js → LoginPage-DjQ5-faQ.js} +1 -1
  34. package/web-app/dist/assets/{MagicPage-StmxQunX.js → MagicPage-mzwFEhXD.js} +1 -1
  35. package/web-app/dist/assets/{MetricsPage-C7e2CioT.js → MetricsPage-Be61Umuv.js} +1 -1
  36. package/web-app/dist/assets/{NotFoundPage-B2IopV0V.js → NotFoundPage-CjmTN3Z6.js} +1 -1
  37. package/web-app/dist/assets/{ProjectPage-CXs8d6Cx.js → ProjectPage-Bj1dJlmV.js} +3 -3
  38. package/web-app/dist/assets/{ProjectsPage-BfgMJYYj.js → ProjectsPage-DPgt37Ev.js} +1 -1
  39. package/web-app/dist/assets/{SettingsPage-CZ8lwCF2.js → SettingsPage-CtaHS1ob.js} +1 -1
  40. package/web-app/dist/assets/{ShowcasePage-iNCfKZj0.js → ShowcasePage-DD7t6JHO.js} +1 -1
  41. package/web-app/dist/assets/{SystemSettingsPage-FMV2pNNz.js → SystemSettingsPage-CzgVPzMw.js} +1 -1
  42. package/web-app/dist/assets/{TeamsPage-D_SzTjuD.js → TeamsPage-CBouFUwW.js} +1 -1
  43. package/web-app/dist/assets/{TemplatesPage-BMF_6X7R.js → TemplatesPage-BArudpPa.js} +1 -1
  44. package/web-app/dist/assets/{TerminalOutput-ClZtwhW8.js → TerminalOutput-DxA5HoZ5.js} +1 -1
  45. package/web-app/dist/assets/{activity-DSOGDSlj.js → activity-BR76ZMBh.js} +1 -1
  46. package/web-app/dist/assets/{bell-BQaMS_Ge.js → bell-Br9K5dz8.js} +1 -1
  47. package/web-app/dist/assets/{bot-Iw0qLbSN.js → bot-DWOsO7gw.js} +1 -1
  48. package/web-app/dist/assets/{check-BNNpt5tE.js → check-CbHxZskh.js} +1 -1
  49. package/web-app/dist/assets/{chevron-left-DDFdacve.js → chevron-left-B7VJtFeE.js} +1 -1
  50. package/web-app/dist/assets/{circle-alert-CiRTbi7c.js → circle-alert-DFxljQkZ.js} +1 -1
  51. package/web-app/dist/assets/{clock-hudCD7-Z.js → clock-BDhyJZBv.js} +1 -1
  52. package/web-app/dist/assets/{cloud-Brn55LA3.js → cloud-BxVf6gss.js} +1 -1
  53. package/web-app/dist/assets/{code-xml-kiS4rISP.js → code-xml-BsBy-SZC.js} +1 -1
  54. package/web-app/dist/assets/{database-BD1l1Tuw.js → database-Bqz0cKPA.js} +1 -1
  55. package/web-app/dist/assets/{dollar-sign-DT5e3PIo.js → dollar-sign-Nma2_5tQ.js} +1 -1
  56. package/web-app/dist/assets/{file-code-corner-DmixJzQ5.js → file-code-corner-DDiKT74Y.js} +1 -1
  57. package/web-app/dist/assets/{file-plus-CeQieVbR.js → file-plus-Bsi1eh4g.js} +1 -1
  58. package/web-app/dist/assets/{globe-BayFHork.js → globe-BrDJU35Q.js} +1 -1
  59. package/web-app/dist/assets/{hammer-BzJ_7lDH.js → hammer-Cc0dGdYB.js} +1 -1
  60. package/web-app/dist/assets/{index-DgDlNUmy.js → index-CwRUHGRH.js} +3 -3
  61. package/web-app/dist/assets/{layers-CfWES4J2.js → layers-dBYYRarz.js} +1 -1
  62. package/web-app/dist/assets/{loader-circle-CH0UbcAO.js → loader-circle-Bgp8OiC8.js} +1 -1
  63. package/web-app/dist/assets/{lock-BuKglb_x.js → lock-mYd7M_sm.js} +1 -1
  64. package/web-app/dist/assets/{package-BkpU_Qb_.js → package-Bl6u282j.js} +1 -1
  65. package/web-app/dist/assets/{plus-BFMNyc0G.js → plus-H-VgX3FJ.js} +1 -1
  66. package/web-app/dist/assets/{refresh-cw-DQnQVKNC.js → refresh-cw-emJQn2ZS.js} +1 -1
  67. package/web-app/dist/assets/{rotate-ccw-B_M9AdjB.js → rotate-ccw-CTDYewXA.js} +1 -1
  68. package/web-app/dist/assets/{scroll-text-1P39rc_L.js → scroll-text-Bx_yfYId.js} +1 -1
  69. package/web-app/dist/assets/{server-BjGwVEsb.js → server-CfCbWR2b.js} +1 -1
  70. package/web-app/dist/assets/{shield-alert-CKcYjHu8.js → shield-alert-ODyUhCBO.js} +1 -1
  71. package/web-app/dist/assets/{thumbs-up-BC_5mpCw.js → thumbs-up-D4rG_0Db.js} +1 -1
  72. package/web-app/dist/assets/{trash-2-CjtQl9fg.js → trash-2-CLW0eSji.js} +1 -1
  73. package/web-app/dist/assets/{trending-down-D9MnFNOD.js → trending-down-B_9FnZty.js} +1 -1
  74. package/web-app/dist/assets/{trending-up-DrHz29bk.js → trending-up-fEvDswGA.js} +1 -1
  75. package/web-app/dist/assets/{upload-D7jmahPb.js → upload-DvgEs5QL.js} +1 -1
  76. package/web-app/dist/assets/{usePolling-C-zxuzqz.js → usePolling-KK1QY8nW.js} +1 -1
  77. package/web-app/dist/assets/{user-DE2Mkf9B.js → user-Cp4-qLBC.js} +1 -1
  78. package/web-app/dist/index.html +1 -1
package/autonomy/loki CHANGED
@@ -143,6 +143,17 @@ _loki_caveman_narration_level() {
143
143
  # claude-flags.sh (like the caveman wrapper above) and fills _LOKI_PERM_ARGV; unset
144
144
  # LOKI_PERMISSION_POLICY=legacy keeps --dangerously-skip-permissions byte-identically.
145
145
  _loki_perm_argv_for() {
146
+ # Also sets _LOKI_CLAUDE_SHELL, the SHELL value every claude spawn in this CLI
147
+ # pins (/bin/bash; LOKI_E10_KEEP_USER_SHELL=1 keeps the user's). Computed in a
148
+ # subshell so the helper's top-level exports never leak. Falls back to the
149
+ # user's SHELL when the lib is unavailable. Spawn sites that need only the
150
+ # shell call this with output discarded.
151
+ _LOKI_CLAUDE_SHELL="$(
152
+ # shellcheck source=autonomy/lib/claude-flags.sh
153
+ . "${_LOKI_SCRIPT_DIR}/lib/claude-flags.sh" >/dev/null 2>&1 || exit 1
154
+ _loki_claude_shell_value
155
+ )" || _LOKI_CLAUDE_SHELL=""
156
+ [ -n "$_LOKI_CLAUDE_SHELL" ] || _LOKI_CLAUDE_SHELL="${SHELL-}"
146
157
  _LOKI_PERM_ARGV=(--dangerously-skip-permissions)
147
158
  # Legacy policy: return before sourcing anything (claude-flags.sh exports caveman vars).
148
159
  [ "${LOKI_PERMISSION_POLICY:-}" = "legacy" ] && return 0
@@ -19851,7 +19862,7 @@ Tasks:
19851
19862
  _loki_perm_argv_for "$codebase_path"
19852
19863
  case "$provider_name" in
19853
19864
  claude)
19854
- { (cd "$codebase_path" && if [ -n "$_loki_mig_cm" ]; then CAVEMAN_DEFAULT_MODE="$_loki_mig_cm" claude "${_LOKI_PERM_ARGV[@]}" -p "$phase_prompt" --output-format stream-json --verbose 2>&1; else claude "${_LOKI_PERM_ARGV[@]}" -p "$phase_prompt" --output-format stream-json --verbose 2>&1; fi) | \
19865
+ { (cd "$codebase_path" && if [ -n "$_loki_mig_cm" ]; then SHELL="$_LOKI_CLAUDE_SHELL" CAVEMAN_DEFAULT_MODE="$_loki_mig_cm" claude "${_LOKI_PERM_ARGV[@]}" -p "$phase_prompt" --output-format stream-json --verbose 2>&1; else SHELL="$_LOKI_CLAUDE_SHELL" claude "${_LOKI_PERM_ARGV[@]}" -p "$phase_prompt" --output-format stream-json --verbose 2>&1; fi) | \
19855
19866
  while IFS= read -r line; do
19856
19867
  # Extract text from stream-json
19857
19868
  if echo "$line" | python3 -c "
@@ -20029,7 +20040,7 @@ IMPORTANT RULES:
20029
20040
  _loki_perm_argv_for "$codebase_path"
20030
20041
  case "$provider_name" in
20031
20042
  claude)
20032
- { (cd "$codebase_path" && if [ -n "$_loki_doc_cm" ]; then CAVEMAN_DEFAULT_MODE="$_loki_doc_cm" claude "${_LOKI_PERM_ARGV[@]}" -p "$doc_prompt" --output-format stream-json --verbose 2>&1; else claude "${_LOKI_PERM_ARGV[@]}" -p "$doc_prompt" --output-format stream-json --verbose 2>&1; fi) | \
20043
+ { (cd "$codebase_path" && if [ -n "$_loki_doc_cm" ]; then SHELL="$_LOKI_CLAUDE_SHELL" CAVEMAN_DEFAULT_MODE="$_loki_doc_cm" claude "${_LOKI_PERM_ARGV[@]}" -p "$doc_prompt" --output-format stream-json --verbose 2>&1; else SHELL="$_LOKI_CLAUDE_SHELL" claude "${_LOKI_PERM_ARGV[@]}" -p "$doc_prompt" --output-format stream-json --verbose 2>&1; fi) | \
20033
20044
  while IFS= read -r line; do
20034
20045
  if echo "$line" | python3 -c "
20035
20046
  import sys, json
@@ -21095,7 +21106,7 @@ Begin healing now. Follow the RARV cycle for each action."
21095
21106
  claude)
21096
21107
  _loki_perm_argv_for "$codebase_path"
21097
21108
  local run_args=("${_LOKI_PERM_ARGV[@]}" -p "$heal_prompt" --output-format stream-json --verbose)
21098
- (cd "$codebase_path" && if [ -n "$_loki_heal_cm" ]; then CAVEMAN_DEFAULT_MODE="$_loki_heal_cm" claude "${run_args[@]}" 2>&1; else claude "${run_args[@]}" 2>&1; fi) | \
21109
+ (cd "$codebase_path" && if [ -n "$_loki_heal_cm" ]; then SHELL="$_LOKI_CLAUDE_SHELL" CAVEMAN_DEFAULT_MODE="$_loki_heal_cm" claude "${run_args[@]}" 2>&1; else SHELL="$_LOKI_CLAUDE_SHELL" claude "${run_args[@]}" 2>&1; fi) | \
21099
21110
  while IFS= read -r line; do
21100
21111
  if echo "$line" | python3 -c "
21101
21112
  import sys, json
@@ -23135,6 +23146,28 @@ print(target)
23135
23146
  PY
23136
23147
  }
23137
23148
 
23149
+ # One line per supported provider (names from SUPPORTED_PROVIDERS in providers/loader.sh), printed once.
23150
+ try_print_provider_setup() {
23151
+ local want="$1" known p hint
23152
+ known="$(sed -n 's/^SUPPORTED_PROVIDERS=(\(.*\))$/\1/p' "$_LOKI_SCRIPT_DIR/../providers/loader.sh" 2>/dev/null | tr -d '"' | head -1)"
23153
+ if [ -z "$known" ]; then
23154
+ echo "loki try: cannot read SUPPORTED_PROVIDERS from providers/loader.sh" >&2
23155
+ return 1
23156
+ fi
23157
+ echo "loki try: no provider is ready for '$want'. Set up one of these, then run loki try again (or preview with --dry-run):"
23158
+ for p in $known; do
23159
+ case "$p" in
23160
+ claude) hint="claude login (or set ANTHROPIC_API_KEY)" ;;
23161
+ codex) hint="codex login (or set OPENAI_API_KEY)" ;;
23162
+ opencode) hint="opencode auth login" ;;
23163
+ aider) hint="install aider and set OPENAI_API_KEY or ANTHROPIC_API_KEY" ;;
23164
+ cline) hint="install the cline CLI and configure its API key" ;;
23165
+ *) hint="install the $p CLI (no login command is mapped for it)" ;;
23166
+ esac
23167
+ echo " $p: $hint"
23168
+ done
23169
+ }
23170
+
23138
23171
  _cmd_try_body() {
23139
23172
  local tmp="$1" url="$2" dry_run="$3" provider="$4" max_cost="$5"
23140
23173
  local dest="$tmp/repo" task
@@ -23167,7 +23200,7 @@ _cmd_try_body() {
23167
23200
  if receipt="$(try_keep_receipt "$dest" "$stamp")"; then
23168
23201
  echo ""
23169
23202
  echo "Receipt: $receipt"
23170
- echo "Check it: loki verify $receipt"
23203
+ echo "Check it: npx loki-mode verify \"$receipt\""
23171
23204
  else
23172
23205
  echo ""
23173
23206
  echo "No receipt was written (run exited $rc)."
@@ -23252,8 +23285,8 @@ cmd_try() {
23252
23285
  *) echo "loki try: unknown provider '$use_provider' (use claude, opencode, codex, cline or aider)" >&2; return 2 ;;
23253
23286
  esac
23254
23287
  if [ "$dry_run" != true ] && ! wow_provider_ready "$use_provider"; then
23255
- echo "loki try: no provider ready for '$use_provider'. Run 'loki doctor', or preview with --dry-run." >&2
23256
- return 1
23288
+ try_print_provider_setup "$use_provider" >&2
23289
+ return 2
23257
23290
  fi
23258
23291
 
23259
23292
  local tmp rc=0
@@ -24270,7 +24303,8 @@ _review_ultra() {
24270
24303
  echo -e "${DIM}Running: claude ${ur_argv[*]}${NC}" >&2
24271
24304
  echo "" >&2
24272
24305
  local ur_rc=0
24273
- claude "${ur_argv[@]}" || ur_rc=$?
24306
+ _loki_perm_argv_for "$PWD" >/dev/null 2>&1 || true
24307
+ SHELL="$_LOKI_CLAUDE_SHELL" claude "${ur_argv[@]}" || ur_rc=$?
24274
24308
  if [ "$ur_rc" -ne 0 ] && [ "$trust_ws" != true ] && [ "$ur_trust_ok" = true ]; then
24275
24309
  echo "If claude reported an untrusted folder, re-run: loki review --ultra --yes --trust-workspace" >&2
24276
24310
  fi
@@ -24397,7 +24431,8 @@ _run_ultracode() {
24397
24431
 
24398
24432
  echo -e "${DIM}Running: claude -p \"ultracode: ...\" --model opus${NC}" >&2
24399
24433
  echo "" >&2
24400
- claude "${uc_argv[@]}"
24434
+ _loki_perm_argv_for "$PWD" >/dev/null 2>&1 || true
24435
+ SHELL="$_LOKI_CLAUDE_SHELL" claude "${uc_argv[@]}"
24401
24436
  return $?
24402
24437
  }
24403
24438
 
@@ -28178,13 +28213,14 @@ _pr_record_merge() {
28178
28213
  # exactly 0 reverts in git history. A revert count of null (history
28179
28214
  # unreadable), a missing record, an unreadable trust output or an unreadable or
28180
28215
  # truncated file list or an unreadable diff all refuse, as does any PR whose
28181
- # diff touches a symlink (120000) or submodule (160000) mode. Protected paths (.github/, .loki/, .env*,
28216
+ # diff touches a symlink (120000) or submodule (160000) mode, including a pure
28217
+ # rename of one (modes read from git on the base and head). Protected paths (.github/, .loki/, .env*,
28182
28218
  # .envrc, CODEOWNERS, plus the space-separated globs in
28183
28219
  # LOKI_EARNED_MERGE_PROTECTED), matched case-insensitively, always stay
28184
28220
  # PR-only. The reason for a refusal goes to stdout; the return code is 1.
28185
28221
  _pr_earned_merge_check() {
28186
28222
  local pr="$1" min="${LOKI_EARNED_MERGE_MIN_VERIFIED:-20}" meta="" num="" want="" lines="" seals=""
28187
- local kind p lp g row v r got=0 globs=() diff="" bad_mode=""
28223
+ local kind p lp g row v r got=0 globs=() diff="" bad_mode="" renames="" refs="" base="" head="" from to m_old m_new
28188
28224
  case "$min" in ''|*[!0-9]*) min=20 ;; esac
28189
28225
  [ "$min" -ge 1 ] || min=1
28190
28226
  command -v jq >/dev/null 2>&1 || { echo "jq is required to read the Seal record"; return 1; }
@@ -28229,6 +28265,28 @@ EOF_FILES
28229
28265
  [ -n "$diff" ] || { echo "the PR's file modes are unreadable"; return 1; }
28230
28266
  bad_mode="$(printf '%s\n' "$diff" | awk '/^diff --git /{h=$0} /^(new file mode|deleted file mode|old mode|new mode) (120000|160000)$/{print h; exit} /^index [0-9a-f.]+ (120000|160000)$/{print h; exit}')"
28231
28267
  [ -z "$bad_mode" ] || { echo "${bad_mode#diff --git } involves a symlink or submodule"; return 1; }
28268
+ # A pure rename (100% similarity) carries no mode or index line, so the scan
28269
+ # above cannot see a symlink moving. Read both sides' modes from git.
28270
+ renames="$(printf '%s\n' "$diff" | awk '/^rename from /{f=substr($0,13)} /^rename to /{print f "\t" substr($0,11); f=""}')"
28271
+ if [ -n "$renames" ]; then
28272
+ refs="$(gh pr view "$pr" --json baseRefOid,headRefOid -q '.baseRefOid + " " + .headRefOid' 2>/dev/null)" || refs=""
28273
+ base="${refs%% *}"; head="${refs#* }"
28274
+ case "$base$head" in ''|*[!0-9a-f]*) echo "the PR's rename modes are unreadable"; return 1 ;; esac
28275
+ if ! git cat-file -e "${base}^{commit}" 2>/dev/null || ! git cat-file -e "${head}^{commit}" 2>/dev/null; then
28276
+ GIT_TERMINAL_PROMPT=0 git fetch -q origin "pull/${num}/head" >/dev/null 2>&1 || true
28277
+ fi
28278
+ while IFS="$(printf '\t')" read -r from to; do
28279
+ [ -n "$to" ] || continue
28280
+ m_old="$(git ls-tree --full-tree "$base" -- "$from" 2>/dev/null | awk '{print $1; exit}')"
28281
+ m_new="$(git ls-tree --full-tree "$head" -- "$to" 2>/dev/null | awk '{print $1; exit}')"
28282
+ case "$m_old:$m_new" in
28283
+ 120000:*|160000:*|*:120000|*:160000) echo "rename from $from to $to involves a symlink or submodule"; return 1 ;;
28284
+ '':*|*:'') echo "rename from $from to $to has unreadable file modes"; return 1 ;;
28285
+ esac
28286
+ done <<EOF_RENAMES
28287
+ $renames
28288
+ EOF_RENAMES
28289
+ fi
28232
28290
  return 0
28233
28291
  }
28234
28292
 
@@ -30531,7 +30589,8 @@ cmd_remote() {
30531
30589
  # If workspace isn't trusted, it exits immediately with an error.
30532
30590
  # Use || true to prevent set -e from killing the script before auto-recovery.
30533
30591
  local rc_exit=0
30534
- claude remote-control ${rc_flags[@]+"${rc_flags[@]}"} || rc_exit=$?
30592
+ _loki_perm_argv_for "$PWD" >/dev/null 2>&1 || true
30593
+ SHELL="$_LOKI_CLAUDE_SHELL" claude remote-control ${rc_flags[@]+"${rc_flags[@]}"} || rc_exit=$?
30535
30594
 
30536
30595
  # Exit 0 = normal session end
30537
30596
  if [ $rc_exit -eq 0 ]; then
@@ -30571,7 +30630,8 @@ print('ok')
30571
30630
  echo ""
30572
30631
  echo -e "${GREEN}Launching remote control...${NC}"
30573
30632
  echo ""
30574
- exec claude remote-control ${rc_flags[@]+"${rc_flags[@]}"}
30633
+ _loki_perm_argv_for "$PWD" >/dev/null 2>&1 || true
30634
+ SHELL="$_LOKI_CLAUDE_SHELL" exec claude remote-control ${rc_flags[@]+"${rc_flags[@]}"}
30575
30635
  }
30576
30636
 
30577
30637
  # Syslog/SIEM integration management
@@ -31250,6 +31310,7 @@ cmd_metrics() {
31250
31310
  local last_n=0
31251
31311
  local save_file=false
31252
31312
  local share_flag=false
31313
+ local share_visibility=""
31253
31314
 
31254
31315
  while [[ $# -gt 0 ]]; do
31255
31316
  case "$1" in
@@ -31266,7 +31327,8 @@ cmd_metrics() {
31266
31327
  echo " --json Machine-readable JSON output"
31267
31328
  echo " --last N Analyze only the last N sessions (default: all)"
31268
31329
  echo " --save Write report to METRICS.md in project root"
31269
- echo " --share Upload report as GitHub Gist (via loki share)"
31330
+ echo " --share Upload report as a secret GitHub Gist (via loki share)"
31331
+ echo " --public With --share, create a PUBLIC gist instead"
31270
31332
  echo " --help, -h Show this help"
31271
31333
  echo ""
31272
31334
  echo "Subcommands:"
@@ -31277,7 +31339,7 @@ cmd_metrics() {
31277
31339
  echo " loki metrics --json # Machine-readable output"
31278
31340
  echo " loki metrics --last 5 # Last 5 sessions only"
31279
31341
  echo " loki metrics --save # Write METRICS.md"
31280
- echo " loki metrics --share # Share as GitHub Gist"
31342
+ echo " loki metrics --share # Share as a secret GitHub Gist"
31281
31343
  echo " loki metrics prometheus # Prometheus metrics from dashboard"
31282
31344
  exit 0
31283
31345
  ;;
@@ -31286,6 +31348,7 @@ cmd_metrics() {
31286
31348
  --last=*) last_n="${1#*=}"; shift ;;
31287
31349
  --save) save_file=true; shift ;;
31288
31350
  --share) share_flag=true; shift ;;
31351
+ --public) share_visibility="--public"; shift ;;
31289
31352
  prometheus)
31290
31353
  # Legacy Prometheus metrics subcommand
31291
31354
  shift
@@ -31685,21 +31748,22 @@ METRICS_SCRIPT
31685
31748
 
31686
31749
  # Handle --share flag
31687
31750
  if [ "$share_flag" = true ]; then
31688
- local tmpfile
31689
- tmpfile=$(mktemp "/tmp/loki-metrics-XXXXXX.md")
31751
+ local tmpdir tmpfile
31752
+ tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/loki-metrics.XXXXXXXX") || exit 1
31753
+ tmpfile="$tmpdir/metrics.md"
31690
31754
  echo "$metrics_output" > "$tmpfile"
31691
31755
 
31692
31756
  if ! command -v gh &>/dev/null; then
31693
31757
  echo -e "${RED}gh CLI not found -- cannot share${NC}"
31694
31758
  echo "Install: brew install gh"
31695
- rm -f "$tmpfile"
31759
+ rm -f "$tmpfile"; rmdir "$tmpdir" 2>/dev/null || true
31696
31760
  exit 1
31697
31761
  fi
31698
31762
 
31699
31763
  if ! gh auth status &>/dev/null 2>&1; then
31700
31764
  echo -e "${RED}GitHub CLI not authenticated${NC}"
31701
31765
  echo "Run 'gh auth login' to authenticate."
31702
- rm -f "$tmpfile"
31766
+ rm -f "$tmpfile"; rmdir "$tmpdir" 2>/dev/null || true
31703
31767
  exit 1
31704
31768
  fi
31705
31769
 
@@ -31708,9 +31772,12 @@ METRICS_SCRIPT
31708
31772
  project_name=$(basename "$(pwd)")
31709
31773
  local gist_desc="Loki Mode productivity report - ${project_name:-project} ($(date +%Y-%m-%d))"
31710
31774
  local gist_url
31711
- gist_url=$(gh gist create "$tmpfile" --desc "$gist_desc" --public 2>&1)
31775
+ if [ "$share_visibility" = "--public" ]; then
31776
+ echo -e "${YELLOW}Warning: this gist is PUBLIC and visible to anyone with the URL.${NC}" >&2
31777
+ fi
31778
+ gist_url=$(gh gist create "$tmpfile" --desc "$gist_desc" $share_visibility 2>&1)
31712
31779
  local gist_exit=$?
31713
- rm -f "$tmpfile"
31780
+ rm -f "$tmpfile"; rmdir "$tmpdir" 2>/dev/null || true
31714
31781
 
31715
31782
  if [ $gist_exit -ne 0 ]; then
31716
31783
  echo -e "${RED}Failed to create gist${NC}"
@@ -32805,7 +32872,8 @@ USER TASK: ${prompt}"
32805
32872
  local agent_exit=0
32806
32873
  case "$provider" in
32807
32874
  claude)
32808
- claude -p "$full_prompt" 2>&1 || agent_exit=$?
32875
+ _loki_perm_argv_for "$PWD" >/dev/null 2>&1 || true
32876
+ SHELL="$_LOKI_CLAUDE_SHELL" claude -p "$full_prompt" 2>&1 || agent_exit=$?
32809
32877
  ;;
32810
32878
  codex)
32811
32879
  codex exec --sandbox workspace-write "$full_prompt" 2>&1 || agent_exit=$?
@@ -32946,7 +33014,7 @@ $diff"
32946
33014
  [ -f ".loki/state/provider" ] && provider=$(cat ".loki/state/provider" 2>/dev/null)
32947
33015
 
32948
33016
  case "$provider" in
32949
- claude) claude -p "$review_prompt" 2>&1 ;;
33017
+ claude) _loki_perm_argv_for "$PWD" >/dev/null 2>&1 || true; SHELL="$_LOKI_CLAUDE_SHELL" claude -p "$review_prompt" 2>&1 ;;
32950
33018
  codex) codex exec --sandbox workspace-write "$review_prompt" 2>&1 ;;
32951
33019
  cline) cline -y "$review_prompt" 2>&1 ;;
32952
33020
  aider) aider --message "$review_prompt" --yes-always --no-auto-commits < /dev/null 2>&1 ;;
@@ -34714,7 +34782,8 @@ _docs_invoke_provider() {
34714
34782
  declare -F loki_child_ledger_append >/dev/null 2>&1 && _ledger=true
34715
34783
  local _fmt=()
34716
34784
  [ "$_ledger" = true ] && _fmt=(--output-format json)
34717
- result=$($t_prefix env CAVEMAN_DEFAULT_MODE=off claude -p ${_lean[@]+"${_lean[@]}"} ${_fmt[@]+"${_fmt[@]}"} --model "${LOKI_DOCS_MODEL:-sonnet}" "$prompt" 2>/dev/null) || exit_code=$?
34785
+ _loki_perm_argv_for "$PWD" >/dev/null 2>&1 || true
34786
+ result=$($t_prefix env "SHELL=$_LOKI_CLAUDE_SHELL" CAVEMAN_DEFAULT_MODE=off claude -p ${_lean[@]+"${_lean[@]}"} ${_fmt[@]+"${_fmt[@]}"} --model "${LOKI_DOCS_MODEL:-sonnet}" "$prompt" 2>/dev/null) || exit_code=$?
34718
34787
  if [ "$_ledger" = true ] && [ -n "$result" ]; then
34719
34788
  loki_child_ledger_append docs "$result" >/dev/null 2>&1 || true
34720
34789
  result=$(printf '%s' "$result" | python3 -c 'import json,sys
@@ -38915,7 +38984,8 @@ REPORT_SCRIPT
38915
38984
 
38916
38985
  cmd_share() {
38917
38986
  local format="markdown"
38918
- local visibility="--public"
38987
+ # Secret by default: --public is passed to gh only on an explicit --public.
38988
+ local visibility=""
38919
38989
 
38920
38990
  while [[ $# -gt 0 ]]; do
38921
38991
  case "$1" in
@@ -38928,20 +38998,22 @@ cmd_share() {
38928
38998
  echo "returning a shareable URL."
38929
38999
  echo ""
38930
39000
  echo "Options:"
38931
- echo " --private Create a secret gist (default: public)"
39001
+ echo " --public Create a PUBLIC gist, visible to anyone with the URL"
39002
+ echo " --private Create a secret gist (default: secret; accepted for compatibility)"
38932
39003
  echo " --format <fmt> Report format: text, markdown, html (default: markdown)"
38933
39004
  echo " --help, -h Show this help"
38934
39005
  echo ""
38935
39006
  echo "Examples:"
38936
- echo " loki share # Public markdown gist"
38937
- echo " loki share --private # Secret gist"
38938
- echo " loki share --format html # Public HTML gist"
38939
- echo " loki share --private --format text # Secret text gist"
39007
+ echo " loki share # Secret markdown gist (default)"
39008
+ echo " loki share --public # Public gist"
39009
+ echo " loki share --format html # Secret HTML gist"
39010
+ echo " loki share --public --format text # Public text gist"
38940
39011
  echo ""
38941
39012
  echo "Requires: gh CLI (https://cli.github.com)"
38942
39013
  exit 0
38943
39014
  ;;
38944
39015
  --private) visibility=""; shift ;;
39016
+ --public) visibility="--public"; shift ;;
38945
39017
  --format) format="${2:-markdown}"; shift 2 ;;
38946
39018
  --format=*) format="${1#*=}"; shift ;;
38947
39019
  *) echo -e "${RED}Unknown option: $1${NC}" >&2; echo "Run 'loki share --help' for usage." >&2; exit 1 ;;
@@ -38991,20 +39063,21 @@ cmd_share() {
38991
39063
  esac
38992
39064
 
38993
39065
  # Generate report to temp file
38994
- local tmpfile
38995
- tmpfile=$(mktemp "/tmp/loki-share-XXXXXX.$ext")
39066
+ local tmpdir tmpfile
39067
+ tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/loki-share.XXXXXXXX") || exit 1
39068
+ tmpfile="$tmpdir/session-report.$ext"
38996
39069
 
38997
39070
  echo "Generating session report..."
38998
39071
  if ! "$0" report --format "$format" > "$tmpfile" 2>/dev/null; then
38999
39072
  echo -e "${RED}Failed to generate session report${NC}"
39000
- rm -f "$tmpfile"
39073
+ rm -f "$tmpfile"; rmdir "$tmpdir" 2>/dev/null || true
39001
39074
  exit 1
39002
39075
  fi
39003
39076
 
39004
39077
  # Verify report is not empty
39005
39078
  if [ ! -s "$tmpfile" ]; then
39006
39079
  echo -e "${RED}Generated report is empty${NC}"
39007
- rm -f "$tmpfile"
39080
+ rm -f "$tmpfile"; rmdir "$tmpdir" 2>/dev/null || true
39008
39081
  exit 1
39009
39082
  fi
39010
39083
 
@@ -39015,8 +39088,8 @@ cmd_share() {
39015
39088
  }
39016
39089
 
39017
39090
  # Internal helper: upload a single file as a GitHub Gist.
39018
- # Args: $1=file path, $2=description, $3=visibility ("--public", "--private",
39019
- # or "" for gh's default). NOTE: $3 is intentionally left unquoted at the
39091
+ # Args: $1=file path, $2=description, $3=visibility ("--public" for a public
39092
+ # gist, or "" for gh's default, which is a secret gist). NOTE: $3 is intentionally left unquoted at the
39020
39093
  # gh call site so an empty value collapses (the --private case sets it to "").
39021
39094
  # Removes the input file after the attempt. Prints "Shared: <url>" on success;
39022
39095
  # on failure prints the gh error and exits 1.
@@ -39031,6 +39104,9 @@ _loki_gist_upload() {
39031
39104
  # "Shared:" line and the ready-to-post hook with multi-line gh chatter.
39032
39105
  local gist_out gist_err gist_exit
39033
39106
  local errfile
39107
+ if [ "$visibility" = "--public" ]; then
39108
+ echo -e "${YELLOW}Warning: this gist is PUBLIC and visible to anyone with the URL.${NC}" >&2
39109
+ fi
39034
39110
  # Fall back to /dev/null if mktemp fails (disk full / unwritable /tmp) so the
39035
39111
  # redirect target is always a real path -- never an empty string (2>"" is a
39036
39112
  # bash error). We lose the captured stderr text in that rare case, not the
@@ -39041,8 +39117,12 @@ _loki_gist_upload() {
39041
39117
  gist_err=$(cat "$errfile" 2>/dev/null)
39042
39118
  [ "$errfile" != "/dev/null" ] && rm -f "$errfile"
39043
39119
 
39044
- # Cleanup temp file
39120
+ # Cleanup temp file and its private loki-* directory (rmdir only removes
39121
+ # an empty directory).
39045
39122
  rm -f "$file"
39123
+ case "$(basename "$(dirname "$file")")" in
39124
+ loki-*) rmdir "$(dirname "$file")" 2>/dev/null || true ;;
39125
+ esac
39046
39126
 
39047
39127
  if [ $gist_exit -ne 0 ]; then
39048
39128
  echo -e "${RED}Failed to create gist${NC}"
@@ -39165,8 +39245,9 @@ PYEOF
39165
39245
 
39166
39246
  # POST the redacted HTML. Auth header is sent only if a license key exists;
39167
39247
  # OSS users with their own endpoint need no key.
39168
- local tmp_body tmp_code
39169
- tmp_body=$(mktemp "/tmp/loki-hosted-XXXXXX.out")
39248
+ local tmp_body tmp_code tmp_body_dir
39249
+ tmp_body_dir=$(mktemp -d "${TMPDIR:-/tmp}/loki-hosted.XXXXXXXX") || return 1
39250
+ tmp_body="$tmp_body_dir/response.out"
39170
39251
  local -a curl_args=(-sS -o "$tmp_body" -w '%{http_code}' -X POST
39171
39252
  -H "Content-Type: text/html"
39172
39253
  -H "X-Loki-Proof-Id: ${id}"
@@ -39180,7 +39261,7 @@ PYEOF
39180
39261
  if [ "$curl_exit" -ne 0 ]; then
39181
39262
  echo -e "${RED}Failed to reach hosted endpoint (curl exit ${curl_exit}).${NC}" >&2
39182
39263
  echo "Check LOKI_HOSTED_ENDPOINT or publish to a gist: loki proof share ${id}" >&2
39183
- rm -f "$tmp_body"
39264
+ rm -f "$tmp_body"; rmdir "$tmp_body_dir" 2>/dev/null || true
39184
39265
  return 1
39185
39266
  fi
39186
39267
 
@@ -39207,7 +39288,7 @@ except Exception:
39207
39288
  print(url)
39208
39289
  PYEOF
39209
39290
  )
39210
- rm -f "$tmp_body"
39291
+ rm -f "$tmp_body"; rmdir "$tmp_body_dir" 2>/dev/null || true
39211
39292
  if [ -n "$published_url" ]; then
39212
39293
  echo -e "${GREEN}Published: ${published_url}${NC}"
39213
39294
  else
@@ -39224,7 +39305,7 @@ PYEOF
39224
39305
  echo "" >&2
39225
39306
  fi
39226
39307
  echo "Nothing was published. Or publish to a gist: loki proof share ${id}" >&2
39227
- rm -f "$tmp_body"
39308
+ rm -f "$tmp_body"; rmdir "$tmp_body_dir" 2>/dev/null || true
39228
39309
  return 1
39229
39310
  ;;
39230
39311
  esac
@@ -39495,7 +39576,8 @@ cmd_proof() {
39495
39576
  echo ""
39496
39577
  echo "Options for 'share':"
39497
39578
  echo " --yes Skip the redaction-preview confirmation prompt"
39498
- echo " --private Create a secret gist (default: public)"
39579
+ echo " --public Create a PUBLIC gist, visible to anyone with the URL"
39580
+ echo " --private Create a secret gist (default: secret; accepted for compatibility)"
39499
39581
  echo " --hosted Publish to LOKI_HOSTED_ENDPOINT (open-core seam; no official backend yet)"
39500
39582
  echo ""
39501
39583
  echo "Proofs are generated automatically at run completion (LOKI_PROOF=0 to opt out)."
@@ -40153,7 +40235,7 @@ print(json.dumps(doc, indent=2))
40153
40235
  share)
40154
40236
  local id=""
40155
40237
  local skip_confirm=0
40156
- local visibility="--public"
40238
+ local visibility=""
40157
40239
  local hosted=0
40158
40240
  # every python3 invocation below reads proof.json from inside
40159
40241
  # the checkout being shared. Resolve an absolute path once so an
@@ -40258,8 +40340,9 @@ PYEOF
40258
40340
  fi
40259
40341
 
40260
40342
  # Copy to a temp file so _loki_gist_upload can remove it after.
40261
- local tmpfile
40262
- tmpfile=$(mktemp "/tmp/loki-proof-XXXXXX.html")
40343
+ local tmpfile tmpdir
40344
+ tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/loki-proof.XXXXXXXX") || exit 1
40345
+ tmpfile="$tmpdir/proof.html"
40263
40346
  cp "$html" "$tmpfile"
40264
40347
  echo "Uploading proof page..."
40265
40348
  local gist_desc="Loki Mode proof-of-run ${id}"
@@ -825,6 +825,17 @@ _qs_classify_invoke() {
825
825
  if command -v timeout >/dev/null 2>&1; then to="timeout"
826
826
  elif command -v gtimeout >/dev/null 2>&1; then to="gtimeout"; fi
827
827
  _LOKI_PERM_ARGV=(--dangerously-skip-permissions)
828
+ # Pin SHELL=/bin/bash for the claude child (LOKI_E10_KEEP_USER_SHELL=1 keeps the user
829
+ # shell). Computed in a subshell so the helper's top-level exports never leak.
830
+ local _qs_shell _qs_shell_lib
831
+ _qs_shell_lib="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)/lib/claude-flags.sh"
832
+ _qs_shell="$(
833
+ # shellcheck source=/dev/null
834
+ type _loki_claude_shell_value >/dev/null 2>&1 \
835
+ || . "$_qs_shell_lib" >/dev/null 2>&1 || exit 1
836
+ _loki_claude_shell_value
837
+ )" || _qs_shell=""
838
+ [ -n "$_qs_shell" ] || _qs_shell="${SHELL-}"
828
839
  if [ "${LOKI_PERMISSION_POLICY:-}" != "legacy" ]; then
829
840
  # P12-26b: nothing on this path sources claude-flags.sh. Compute in a subshell so its
830
841
  # top-level exports (caveman vars) never leak; fail closed to plan when unavailable.
@@ -844,10 +855,10 @@ _qs_classify_invoke() {
844
855
  fi
845
856
  fi
846
857
  if [ -n "$to" ]; then
847
- out=$(CAVEMAN_DEFAULT_MODE=off "$to" "${LOKI_QUICKSTART_CLASSIFY_TIMEOUT}" \
858
+ out=$(SHELL="$_qs_shell" CAVEMAN_DEFAULT_MODE=off "$to" "${LOKI_QUICKSTART_CLASSIFY_TIMEOUT}" \
848
859
  claude "${_LOKI_PERM_ARGV[@]}" -p "$prompt" 2>/dev/null) || rc=$?
849
860
  else
850
- out=$(CAVEMAN_DEFAULT_MODE=off \
861
+ out=$(SHELL="$_qs_shell" CAVEMAN_DEFAULT_MODE=off \
851
862
  claude "${_LOKI_PERM_ARGV[@]}" -p "$prompt" 2>/dev/null) || rc=$?
852
863
  fi
853
864
  [ "$rc" -ne 0 ] && return 1
package/completions/_loki CHANGED
@@ -100,7 +100,7 @@ function _loki {
100
100
  estimate)
101
101
  _arguments \
102
102
  '--json[Output machine-readable JSON]' \
103
- '--model[Model whose rough prior to use]:model:' \
103
+ '--model[Model whose first-run prior to use]:model:' \
104
104
  '--provider[Provider whose default model is used]:provider:' \
105
105
  '--help[Show help]'
106
106
  ;;
@@ -185,7 +185,8 @@ function _loki {
185
185
  '--json[Machine-readable JSON output]' \
186
186
  '--last[Analyze last N sessions]:number:' \
187
187
  '--save[Write report to METRICS.md]' \
188
- '--share[Upload report as GitHub Gist]' \
188
+ '--share[Upload report as a secret GitHub Gist]' \
189
+ '--public[With --share, create a PUBLIC gist]' \
189
190
  '--help[Show help]' \
190
191
  '1:subcommand:(prometheus)'
191
192
  ;;
@@ -199,14 +200,16 @@ function _loki {
199
200
  ;;
200
201
  share)
201
202
  _arguments \
202
- '--private[Create a secret gist]' \
203
+ '--public[Create a PUBLIC gist, visible to anyone with the URL]' \
204
+ '--private[Create a secret gist (default)]' \
203
205
  '--format[Report format]:format:(text markdown html)' \
204
206
  '--help[Show help]'
205
207
  ;;
206
208
  proof)
207
209
  _arguments \
208
210
  '--yes[Skip redaction-preview confirmation]' \
209
- '--private[Create a secret gist when sharing]' \
211
+ '--public[Create a PUBLIC gist when sharing]' \
212
+ '--private[Create a secret gist when sharing (default)]' \
210
213
  '--hosted[Hosted publishing (coming in R9)]' \
211
214
  '--dry-run[Scan and list what share would publish; publishes nothing]' \
212
215
  '--help[Show help]' \
@@ -355,7 +355,7 @@ _loki_completion() {
355
355
 
356
356
  metrics)
357
357
  if [[ "$cur" == -* ]]; then
358
- COMPREPLY=( $(compgen -W "--json --last --save --share --help" -- "$cur") )
358
+ COMPREPLY=( $(compgen -W "--json --last --save --share --public --help" -- "$cur") )
359
359
  return 0
360
360
  fi
361
361
  local metrics_cmds="prometheus"
@@ -373,7 +373,7 @@ _loki_completion() {
373
373
 
374
374
  share)
375
375
  if [[ "$cur" == -* ]]; then
376
- COMPREPLY=( $(compgen -W "--private --format --help" -- "$cur") )
376
+ COMPREPLY=( $(compgen -W "--public --private --format --help" -- "$cur") )
377
377
  return 0
378
378
  fi
379
379
  if [[ "$prev" == "--format" ]]; then
@@ -384,7 +384,7 @@ _loki_completion() {
384
384
 
385
385
  proof)
386
386
  if [[ "$cur" == -* ]]; then
387
- COMPREPLY=( $(compgen -W "--yes --private --hosted --dry-run --out --help" -- "$cur") )
387
+ COMPREPLY=( $(compgen -W "--yes --public --private --hosted --dry-run --out --help" -- "$cur") )
388
388
  return 0
389
389
  fi
390
390
  if [[ $cword -eq 2 ]]; then
@@ -7,6 +7,6 @@ Modules:
7
7
  control: Session control helpers (status, atomic writes, origin check)
8
8
  """
9
9
 
10
- __version__ = "11.90.0"
10
+ __version__ = "11.92.0"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -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.90.0. Do not edit by hand.
3
+ Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.92.0. Do not edit by hand.
4
4
  Hidden and legacy-only commands are omitted; see `docs/CLI-INVENTORY.md` for the full inventory.
5
5
 
6
6
  | Command | Description |
@@ -2,7 +2,7 @@
2
2
 
3
3
  Loki Mode, by [Autonomi](https://www.autonomi.dev/), turns a spec or an issue into a verified pull request with a receipt you can check. This page covers installing it, a first run, and the options around it.
4
4
 
5
- **Version:** v11.90.0
5
+ **Version:** v11.92.0
6
6
 
7
7
  **Engine note:** `loki start` is the primary command. `loki start`, `loki "<task>"`, `loki owner/repo#N` and `loki quick` all run the Loki 10 engine, whatever the flag order or input form (PRD file, issue ref, task or one-word brief). The previous loop runs only on `--engine legacy`, `LOKI_ENGINE=legacy`, or for one call when a flag Loki 10 cannot honor is present. See the [user guide](v10/GUIDE.md) and the [5-minute quickstart](QUICKSTART.md).
8
8
 
@@ -36,7 +36,7 @@ Deviation from the card: there is no mounted output dir. A writable host mount w
36
36
  - Oversized output: capped by `--max-output`; `output_truncated` is set.
37
37
  - Fork bomb: `--pids-limit 256`. Memory bomb: `--memory`. Disk fill: tmpfs size caps.
38
38
  - Forged result: the checked code can print a fake `LOKI_VPR_END` line. A per-run 128-bit nonce is sent to the wrapper on stdin and the checked command runs with stdin from /dev/null; the host accepts only the last line carrying that nonce. Docker's default seccomp profile does NOT block ptrace or process_vm_readv on kernels >= 4.8, and the checked command runs as the same uid as the wrapper inside the container, so a hostile process can read the wrapper's memory and recover the nonce. The nonce therefore only stops accidental or naive forgery; it is not a security boundary. One cross-check is added: the wrapper exits with the check's code and the host rejects (status ERROR) a sentinel that disagrees with docker's own exit code. The PR code can still make its own check report success, which is why `result.json` is a signal about the check and not the final verdict (VPR-2 owns that, and it must not treat a bare COMPLETED exit 0 as proof). A custom seccomp profile denying ptrace is a possible hardening and is not shipped here.
39
- - Forged report: the report path is a per-run random name given only on the runner's command line, so printed totals cannot forge it. A hostile test that reads its parent's command line (or the nonce, see above) and rewrites the file after the runner exits can still forge a pass; the same ptrace-class residual applies.
39
+ - Forged report: the report path is a per-run random name given only on the runner's command line, so printed totals cannot forge it. The checked tests run inside the runner's process or its worker processes, so a hostile test that reads the path from the process command line can register an exit hook that rewrites the file after the runner has written it, and can still forge a pass; the same ptrace-class residual applies.
40
40
  - Docker pool exhaustion: reported as BLOCKED, never as a failing check.
41
41
 
42
42
  ## Not covered (residual, tracked for VPR-2 review)