loki-mode 11.17.49 → 11.17.51

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 (71) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/secure-scan.py +85 -5
  4. package/dashboard/__init__.py +1 -1
  5. package/docs/CLI-REFERENCE.md +1 -1
  6. package/docs/INSTALLATION.md +1 -1
  7. package/docs/v10/BOARD.md +30 -0
  8. package/docs/v10/DEPS.md +1 -0
  9. package/docs/v10/PROGRESS.md +6 -0
  10. package/loki-ts/dist/loki.js +1170 -1154
  11. package/mcp/__init__.py +1 -1
  12. package/package.json +2 -2
  13. package/packages/control-plane/dist/server.js +146 -106
  14. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  15. package/web-app/dist/assets/{AdminPage-s0UfQ54A.js → AdminPage-CDSL6lzQ.js} +1 -1
  16. package/web-app/dist/assets/{Avatar-C65GqVWB.js → Avatar-BG02zRCM.js} +1 -1
  17. package/web-app/dist/assets/{Badge-Bx6KyeF8.js → Badge-DZAAUjis.js} +1 -1
  18. package/web-app/dist/assets/{Button-Gp5vTh3P.js → Button-BXHri3Bg.js} +1 -1
  19. package/web-app/dist/assets/{CockpitPage-BknRMDid.js → CockpitPage-DzSQmo7Q.js} +1 -1
  20. package/web-app/dist/assets/{ComparePage-c-mU2XHA.js → ComparePage-Bo_nMN8K.js} +1 -1
  21. package/web-app/dist/assets/{ErrorBoundary-ngcvdCD8.js → ErrorBoundary-CTQU5X7k.js} +1 -1
  22. package/web-app/dist/assets/{EvidenceReceiptPanel-fKH6e1EW.js → EvidenceReceiptPanel-C4lQ99Wp.js} +1 -1
  23. package/web-app/dist/assets/{GitHubIssuesPanel-BpZ2v9n0.js → GitHubIssuesPanel-DkKxlaTB.js} +1 -1
  24. package/web-app/dist/assets/{GitHubPRsPanel-C6D5PLlR.js → GitHubPRsPanel-Br02sXlA.js} +1 -1
  25. package/web-app/dist/assets/{HomePage-BykPt2x_.js → HomePage-DjXhkhFt.js} +2 -2
  26. package/web-app/dist/assets/{LoginPage-CXNSVFwK.js → LoginPage-C4xmRk7J.js} +1 -1
  27. package/web-app/dist/assets/{MagicPage-BPm8lvGR.js → MagicPage-D_r38Yde.js} +1 -1
  28. package/web-app/dist/assets/{MetricsPage-B-Zue4zV.js → MetricsPage-B9Y1p-FE.js} +1 -1
  29. package/web-app/dist/assets/{NotFoundPage-CfbRFDzs.js → NotFoundPage-BTNNHLAC.js} +1 -1
  30. package/web-app/dist/assets/{ProjectPage-BJJ5ilR5.js → ProjectPage-DUUDqjbc.js} +3 -3
  31. package/web-app/dist/assets/{ProjectsPage-BihWTnjx.js → ProjectsPage-CxX842kz.js} +1 -1
  32. package/web-app/dist/assets/{SettingsPage-DEVFSLVd.js → SettingsPage-IUQeYuCB.js} +1 -1
  33. package/web-app/dist/assets/{ShowcasePage-BBkoZFps.js → ShowcasePage-DGYLnI8h.js} +1 -1
  34. package/web-app/dist/assets/{SystemSettingsPage-CzOE74Q7.js → SystemSettingsPage-Dl8x3Woj.js} +1 -1
  35. package/web-app/dist/assets/{TeamsPage-BdPX6dgE.js → TeamsPage-CJ4TPxjB.js} +1 -1
  36. package/web-app/dist/assets/{TemplatesPage-C8mVITpx.js → TemplatesPage-O8LCjJtq.js} +1 -1
  37. package/web-app/dist/assets/{TerminalOutput-Bc00RcJc.js → TerminalOutput-DGTWYoJB.js} +1 -1
  38. package/web-app/dist/assets/{activity-CDLuOW9_.js → activity-CNqE-ixm.js} +1 -1
  39. package/web-app/dist/assets/{bell-025wj1sV.js → bell-BEi_il9M.js} +1 -1
  40. package/web-app/dist/assets/{bot-DWh-OnmY.js → bot-CkFZ33Q-.js} +1 -1
  41. package/web-app/dist/assets/{check-l7LspX4l.js → check-C_qLIaF-.js} +1 -1
  42. package/web-app/dist/assets/{chevron-left-DKO1-YmL.js → chevron-left-B5XC_KnW.js} +1 -1
  43. package/web-app/dist/assets/{circle-alert-BfV2uDVQ.js → circle-alert-64ZS4s9j.js} +1 -1
  44. package/web-app/dist/assets/{clock-CCakP4hk.js → clock-BHdB3bZI.js} +1 -1
  45. package/web-app/dist/assets/{cloud-CaMXUXfG.js → cloud-dnsbryqT.js} +1 -1
  46. package/web-app/dist/assets/{code-xml-DUvRZwB9.js → code-xml-iLJ48no4.js} +1 -1
  47. package/web-app/dist/assets/{database-BHuS_k85.js → database-D2uC8Rxd.js} +1 -1
  48. package/web-app/dist/assets/{dollar-sign-BhXGrkPP.js → dollar-sign-B47Mg178.js} +1 -1
  49. package/web-app/dist/assets/{file-code-corner-A7mPiq0J.js → file-code-corner-Dlp_xnun.js} +1 -1
  50. package/web-app/dist/assets/{file-plus-CGbNX6r_.js → file-plus-VfERZYg5.js} +1 -1
  51. package/web-app/dist/assets/{globe-bPOnAxns.js → globe-BBduzaTh.js} +1 -1
  52. package/web-app/dist/assets/{hammer-BJg7R73E.js → hammer-QSASdDEK.js} +1 -1
  53. package/web-app/dist/assets/{index-DXT0GsX7.js → index-C0weGbNK.js} +3 -3
  54. package/web-app/dist/assets/{layers-CTHygTS7.js → layers-BAfPZ8HU.js} +1 -1
  55. package/web-app/dist/assets/{loader-circle-DKdXgG0q.js → loader-circle-DXqBgYWH.js} +1 -1
  56. package/web-app/dist/assets/{lock-053N_MGE.js → lock-LCsOKAUi.js} +1 -1
  57. package/web-app/dist/assets/{package-SA20Nj5k.js → package-BLUwVAd0.js} +1 -1
  58. package/web-app/dist/assets/{plus-D6Ay8g2_.js → plus-LHz4MkJH.js} +1 -1
  59. package/web-app/dist/assets/{refresh-cw-BWJ5g4al.js → refresh-cw-CnGPNK6c.js} +1 -1
  60. package/web-app/dist/assets/{rotate-ccw-FpwQ4jBk.js → rotate-ccw-D7EWVgA8.js} +1 -1
  61. package/web-app/dist/assets/{scroll-text-BfSXM3oJ.js → scroll-text-vVVQ3jTP.js} +1 -1
  62. package/web-app/dist/assets/{server-CWiRm4Ss.js → server-CVmxaWog.js} +1 -1
  63. package/web-app/dist/assets/{shield-alert-nFmFAjOa.js → shield-alert-DCrN0N7j.js} +1 -1
  64. package/web-app/dist/assets/{thumbs-up-DRcA4ke1.js → thumbs-up-BVtgLRAB.js} +1 -1
  65. package/web-app/dist/assets/{trash-2-ClXTj6YH.js → trash-2-BmSBvfQk.js} +1 -1
  66. package/web-app/dist/assets/{trending-down-Dd_CU85r.js → trending-down-BIIzpX-l.js} +1 -1
  67. package/web-app/dist/assets/{trending-up-Csbq5ve6.js → trending-up-D8z8AEPW.js} +1 -1
  68. package/web-app/dist/assets/{upload-D2aKAKqW.js → upload-50BgK2yG.js} +1 -1
  69. package/web-app/dist/assets/{usePolling-Cn5Z9_7V.js → usePolling-B33v_P8Z.js} +1 -1
  70. package/web-app/dist/assets/{user-Bt-F2IL3.js → user-DWe9X5rS.js} +1 -1
  71. 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.17.49
6
+ # Loki Mode v11.17.51
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 previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
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.17.49 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
486
+ **v11.17.51 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 11.17.49
1
+ 11.17.51
@@ -18,7 +18,7 @@ The scanner is honest by construction:
18
18
  .git, vendor, bundled dist of dependencies) and oversized files, so it is
19
19
  cheap and deterministic.
20
20
 
21
- The v1 rules (rules 6 and 7 added later) (see internal/LOOP4-SECURE-BY-DEFAULT-PLAN.md):
21
+ The v1 rules (rules 6, 7 and 8 added later) (see internal/LOOP4-SECURE-BY-DEFAULT-PLAN.md):
22
22
  1. private-key-committed (HIGH) a PEM private-key block in any text file
23
23
  2. secret-in-client-file (HIGH) a real secret literal in a browser-served file
24
24
  3. world-open-datastore (HIGH) a literal world-open datastore rule
@@ -44,9 +44,10 @@ CLI (mirrors dashboard/audit.py / proof-verify.py shim style):
44
44
  import json
45
45
  import os
46
46
  import re
47
+ import subprocess
47
48
  import sys
48
49
 
49
- RULES_VERSION = "1.2"
50
+ RULES_VERSION = "1.4"
50
51
 
51
52
  # --- scan limits -----------------------------------------------------------
52
53
  # Cap per-file size so a giant generated bundle cannot stall the gate. 2 MiB is
@@ -122,6 +123,16 @@ _SECRET_PATTERNS = [
122
123
  ("google-api-key", re.compile(r"\bAIza[0-9A-Za-z_-]{35}\b")),
123
124
  # Slack bot token: xoxb- + token body
124
125
  ("slack-token", re.compile(r"\bxox[baprs]-[0-9A-Za-z-]{10,}\b")),
126
+ # GitLab personal access token
127
+ ("gitlab-token", re.compile(r"\bglpat-[A-Za-z0-9_-]{20,}")),
128
+ # npm automation/publish token: npm_ + 36 alphanumerics
129
+ ("npm-token", re.compile(r"(?<![A-Za-z0-9])npm_[A-Za-z0-9]{36}(?![A-Za-z0-9])")),
130
+ # Hugging Face user access token
131
+ ("huggingface-token", re.compile(r"(?<![A-Za-z0-9_])hf_[A-Za-z0-9]{34,}")),
132
+ # Stripe live secret / restricted key
133
+ ("stripe-key", re.compile(r"(?<![A-Za-z0-9])[sr]k_live_[A-Za-z0-9]{24,}")),
134
+ # SendGrid API key: SG.<22>.<43>
135
+ ("sendgrid-key", re.compile(r"(?<![A-Za-z0-9])SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}")),
125
136
  ]
126
137
 
127
138
  # Placeholder / example markers. If the matched secret (or its surrounding
@@ -146,7 +157,7 @@ def _is_placeholder_secret(secret):
146
157
  return True
147
158
  # strip the known prefix so we examine just the secret body
148
159
  body = secret
149
- for prefix in ("akia", "sk-", "ghp_", "aiza", "xoxb-"):
160
+ for prefix in ("akia", "sk-", "ghp_", "aiza", "xoxb-", "glpat-", "npm_", "hf_", "sk_live_", "rk_live_"):
150
161
  if low.startswith(prefix):
151
162
  body = secret[len(prefix):]
152
163
  break
@@ -450,6 +461,39 @@ def _shell_injection_hit(text, node_file):
450
461
  return qualified or node_file
451
462
 
452
463
 
464
+ # ---------------------------------------------------------------------------
465
+ # rule 8: committed real env file with a secret assignment
466
+ # ---------------------------------------------------------------------------
467
+ # Only real env files (.env, .env.local, .env.production and the prod/staging
468
+ # siblings); .env.example/.sample/.template never match the name regex. The
469
+ # assigned name must end in KEY, SECRET, TOKEN or PASSWORD and the value must be
470
+ # non-empty and not a placeholder or a ${VAR} reference. NEXT_PUBLIC_/PUBLIC
471
+ # names are public by design and skipped. The value is never echoed.
472
+ _REAL_ENVFILE_RE = re.compile(
473
+ r"(?:^|/)\.env(?:\.(?:local|production|prod|staging))?$", re.IGNORECASE)
474
+ _ENV_SECRET_ASSIGN_RE = re.compile(
475
+ r"^\s*(?:export\s+)?([A-Za-z][A-Za-z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD))"
476
+ r"\s*=\s*(.*?)\s*$")
477
+
478
+
479
+ def _env_secret_assignment(text):
480
+ """Name of a secret-shaped env assignment with a real value, else None."""
481
+ m = _ENV_SECRET_ASSIGN_RE.match(text)
482
+ if not m:
483
+ return None
484
+ name, value = m.group(1), m.group(2)
485
+ if "PUBLIC" in name.upper():
486
+ return None
487
+ if value[:1] in ("'", '"') and value[-1:] == value[:1] and len(value) >= 2:
488
+ value = value[1:-1]
489
+ elif " #" in value:
490
+ value = value.split(" #", 1)[0].rstrip()
491
+ value = value.strip()
492
+ if not value or _PLACEHOLDER_RE.search(value) or _is_placeholder_secret(value):
493
+ return None
494
+ return name
495
+
496
+
453
497
  # ---------------------------------------------------------------------------
454
498
  # file walking / reading
455
499
  # ---------------------------------------------------------------------------
@@ -477,6 +521,20 @@ def _iter_files(target_dir):
477
521
  yield abs_path, rel_path
478
522
 
479
523
 
524
+ def _git_tracked_files(target_dir):
525
+ """Return the set of git-tracked rel paths under target_dir, or None when
526
+ target_dir is not inside a git work tree (or git is unavailable)."""
527
+ try:
528
+ r = subprocess.run(
529
+ ["git", "-C", target_dir, "ls-files", "-z"],
530
+ capture_output=True, timeout=30)
531
+ except (OSError, subprocess.SubprocessError):
532
+ return None
533
+ if r.returncode != 0:
534
+ return None
535
+ return set(os.fsdecode(x) for x in r.stdout.split(b"\0") if x)
536
+
537
+
480
538
  def _read_lines(abs_path):
481
539
  """Read a file as text lines. Returns a list of (lineno, text) or None if the
482
540
  file is not decodable as UTF-8 text (treat as binary, skip)."""
@@ -507,13 +565,19 @@ def _finding(rule, rel_path, line, severity, message, fix):
507
565
  }
508
566
 
509
567
 
510
- def _scan_file(rel_path, lines, secrets_everywhere=False):
568
+ def _scan_file(rel_path, lines, secrets_everywhere=False, env_tracked=None):
511
569
  """Return a list of findings for one file. `lines` is [(lineno, text), ...]."""
512
570
  findings = []
513
571
  browser_served = _is_browser_served(rel_path)
514
572
  prod_config = _is_production_config(rel_path)
515
573
  tls_scan = _tls_rule_applies(rel_path)
516
574
  shell_scan = _shell_rule_applies(rel_path)
575
+ real_envfile = bool(_REAL_ENVFILE_RE.search(rel_path))
576
+ # env_tracked: None = not a git repo (report as a present env file);
577
+ # a set = only git-tracked env files are in version control.
578
+ env_in_git = env_tracked is not None
579
+ if real_envfile and env_in_git and rel_path not in env_tracked:
580
+ real_envfile = False
517
581
  node_file = shell_scan and any(_NODE_CP_FILE.search(t) for _, t in lines)
518
582
 
519
583
  # rule 3 / rule 5 need whole-file context for the AND conditions.
@@ -659,6 +723,21 @@ def _scan_file(rel_path, lines, secrets_everywhere=False):
659
723
  "interpolate input into a command string.",
660
724
  ))
661
725
 
726
+ # --- rule 8: committed real env file with a secret value ---------
727
+ if real_envfile:
728
+ env_name = _env_secret_assignment(text)
729
+ if env_name:
730
+ findings.append(_finding(
731
+ "committed-env-secret", rel_path, lineno, "HIGH",
732
+ ("A committed env file assigns a value to %s; the value is "
733
+ "in version control history." % env_name) if env_in_git
734
+ else ("An env file assigns a value to %s; it is not in a "
735
+ "git repo so it cannot be confirmed ignored." % env_name),
736
+ "Remove the file from git (git rm --cached), add it to "
737
+ ".gitignore, rotate the credential, and commit only a "
738
+ ".env.example with empty or placeholder values.",
739
+ ))
740
+
662
741
  # --- rule 5: CORS wildcard + credentials (whole-file AND) --------
663
742
  # Skip prose/doc lines (_rule3_skip = comment-only line or .md/.rst/.txt
664
743
  # doc file): a README or comment WARNING about ACAO * + credentials is not
@@ -710,12 +789,13 @@ def scan(target_dir, secrets_everywhere=False):
710
789
 
711
790
  findings = []
712
791
  files_scanned = 0
792
+ env_tracked = _git_tracked_files(target_dir)
713
793
  for abs_path, rel_path in _iter_files(target_dir):
714
794
  lines = _read_lines(abs_path)
715
795
  if lines is None:
716
796
  continue
717
797
  files_scanned += 1
718
- findings.extend(_scan_file(rel_path, lines, secrets_everywhere))
798
+ findings.extend(_scan_file(rel_path, lines, secrets_everywhere, env_tracked))
719
799
 
720
800
  # deterministic ordering: by file, then line, then rule
721
801
  findings.sort(key=lambda f: (f["file"], f["line"], f["rule"]))
@@ -7,6 +7,6 @@ Modules:
7
7
  control: Session control helpers (status, atomic writes, origin check)
8
8
  """
9
9
 
10
- __version__ = "11.17.49"
10
+ __version__ = "11.17.51"
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.17.49. Do not edit by hand.
3
+ Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.17.51. 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
  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.17.49
5
+ **Version:** v11.17.51
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
 
package/docs/v10/BOARD.md CHANGED
@@ -1276,3 +1276,33 @@ Source: ~/git/autonomi-dev/research/2026-09-30-adoption/SWARM-PROMPT-ADOPTION.md
1276
1276
  | TEN-SECURITY-05 | enhancement: redactSecrets strips secret query parameters (token, access_token, api_key, apikey, password, secret, sig) from URLs in logged and receipted output | loki-ts/src/util/redact.ts, loki-ts/tests/util/redact.test.ts | HIGH | red: https://h/x?access_token=abc123 survives; green: value replaced, parameter name and other params kept, non-secret params untouched; cd loki-ts && bunx tsc --noEmit && bun test tests/util/redact.test.ts | released v11.17.41 | Gap: TEN-SECURITY-02 covers URL userinfo and Authorization only (redact.ts:12-13); grep -nE 'access_token or api_key or \?' loki-ts/src/util/redact.ts returned 0 query-string rules. Serialize after TEN-SECURITY-04 (same two files). |
1277
1277
  | TEN-SECURITY-06 | feature: secure-scan rule 6 flags disabled TLS verification in committed code (verify=False, rejectUnauthorized: false, InsecureSkipVerify: true, NODE_TLS_REJECT_UNAUTHORIZED=0) | autonomy/lib/secure-scan.py, tests/test-secure-scan.sh | HIGH | red: a fixture with requests.get(url, verify=False) scans clean; green: finding rule tls-verify-disabled with file and line, test and fixture paths and comments excluded like rule 4; bash tests/test-secure-scan.sh | released v11.17.41 | Gap: secure-scan.py has rules 1-5 only (rule comments at lines 93, 105, 193, 238, 345); grep -nE 'verify=False or rejectUnauthorized or InsecureSkipVerify or NODE_TLS' autonomy/lib/secure-scan.py returned 0 hits. Bump RULES_VERSION (line 49). Serialize after TEN-GATES-20 and before TEN-SECURITY-07. |
1278
1278
  | TEN-SECURITY-07 | feature: secure-scan rule 7 flags shell injection shapes (subprocess with shell=True and an f-string or format argument, os.system with concatenation, child_process.exec with a template literal) | autonomy/lib/secure-scan.py, tests/test-secure-scan.sh | HIGH | red: subprocess.run(f"rm {x}", shell=True) scans clean; green: finding rule shell-injection with file and line, constant-string commands and test paths not flagged; bash tests/test-secure-scan.sh | released v11.17.44 | Gap: grep -nE 'shell=True or os\.system or child_process' autonomy/lib/secure-scan.py returned 0 hits. Precision first (the file's stated design: literal forms only), measure false positives on this repo's own tests before shipping. Serialize after TEN-SECURITY-06. |
1279
+ | TEN-SECURITY-13 | enhancement: redactSecrets also strips PyPI (pypi-AgEI...), Shopify (shpat_), Postman (PMAK-), Databricks (dapi + 32 hex) and Mailgun (key- + 32 hex) tokens | loki-ts/src/util/redact.ts, loki-ts/tests/util/redact.test.ts | HIGH | red: each of the five shapes survives redactSecrets; green: replaced with [REDACTED:...] markers, existing rules, ordering and the fixpoint loop unchanged; cd loki-ts && bun test tests/util/redact.test.ts -t "vendor tokens" | released v11.17.50 | Gap: grep -cE 'pypi-/shpat_/PMAK/dapi/key-' loki-ts/src/util/redact.ts returned 0 (RULES lines 3-22 stop at Stripe and PEM). Anchor each shape with a length and boundary so ordinary words never match. |
1280
+ | TEN-SECURITY-14 | enhancement: secure-scan.py _SECRET_PATTERNS (rule 2 and the pre-PR added-line scan) lacks GitLab glpat-, npm_, Hugging Face hf_, Stripe sk_live_/rk_live_ and SendGrid SG. shapes that export_sarif.ts and secret-scan.sh already know, so a key added in a PR passes the Seal scan | autonomy/lib/secure-scan.py, tests/test-secure-scan.sh | HIGH | red: a fixture with a glpat- token in a browser-served file scans clean; green: finding secret-in-client-file naming the kind for each shape, placeholders still excluded; bash tests/test-secure-scan.sh | released v11.17.50 | Gap: secure-scan.py:104-122 _SECRET_PATTERNS lists AWS, openai, github, anthropic, google, slack only; grep -nE 'glpat/npm_/hf_/sk_live/SG\.' autonomy/lib/secure-scan.py returned 0. Bump RULES_VERSION (line 49). Chain: same two files as TEN-SECURITY-15, do this first. |
1281
+ | TEN-SECURITY-15 | feature: secure-scan rule 8 flags a committed real env file (.env, .env.local, .env.production) that assigns a non-empty, non-placeholder value to a *_KEY, *_SECRET, *_TOKEN or *_PASSWORD name | autonomy/lib/secure-scan.py, tests/test-secure-scan.sh | HIGH | red: a fixture .env containing STRIPE_SECRET=abc123def456 scans clean; green: finding committed-env-secret with file and line, value never echoed, .env.example and .env.sample and empty values not flagged; bash tests/test-secure-scan.sh | released v11.17.50 | Gap: grep -n 'env' autonomy/lib/secure-scan.py shows only the .env.example exclusion (line 129) and rule 4's .env.production debug check (line 257); no rule inspects secret assignments in env files. Serialize after TEN-SECURITY-14. |
1282
+ | TEN-GATES-27 | fix: the Seal stage writes receipt.json and receipt.md with plain writeFileSync, so a kill mid-write leaves a truncated receipt that `loki verify` reads as unreadable; write both through util/atomic.ts | loki-ts/src/engine10/stages/seal.ts, loki-ts/tests/engine10/seal.test.ts | HIGH | red: cd loki-ts && bun test tests/engine10/seal.test.ts -t "atomic receipt" fails (a simulated write failure leaves a partial receipt.json); green: passes, receipt bytes and sha identical on the success path | released v11.17.50 | Gap: seal.ts:315-316 writeFileSync(path, ...) and writeFileSync(receipt.md); util/atomic.ts atomicWriteText exists and is unused here (grep -n atomic loki-ts/src/engine10/stages/seal.ts returned 0). |
1283
+ | TEN-GATES-28 | fix: `loki keys export extra --bogus` ignores everything after the verb; reject extra args and unknown flags with the usage line and exit 2 (export alone, --help and -h unchanged) | loki-ts/src/engine10/keys_cmd.ts, loki-ts/tests/engine10/verify_cmd.test.ts | LOW | red: cd loki-ts && bun test tests/engine10/verify_cmd.test.ts -t "keys export extra" fails (prints the key, exit 0); green: passes | released v11.17.50 | Gap: keys_cmd.ts main() reads args[0] only (line 16-19); takePubkey already rejects extras for verify but keys export never calls it. |
1284
+ | TEN-GATES-29 | fix: `loki verify-pr <ref> --out --json` takes `--json` as the output directory and creates a directory literally named --json; reject an --out value that starts with a dash or is missing, exit 2 with the usage line | loki-ts/src/commands/verify_pr.ts, loki-ts/tests/commands/verify_pr.test.ts | LOW | red: cd loki-ts && bun test tests/commands/verify_pr.test.ts -t "out needs a value" fails; green: passes, `--out DIR` unchanged | released v11.17.50 | Gap: verify_pr.ts:221-223 outDir = rest[oi + 1] ?? "" with no dash check (only empty string is refused at :227). |
1285
+ | TEN-COST-46 | fix: cost_claim.py accepts `--target nan`, 0 or a negative number; with nan the `hi > target` test is False so the verdict can read PROVEN_AT(nan); require a finite target greater than 0 and exit 2 otherwise | benchmarks/bench/cost_claim.py, benchmarks/bench/tests/test_cost_claim.py | MEDIUM | red: pytest benchmarks/bench/tests/test_cost_claim.py -q -k target_must_be_finite fails; green: passes, valid targets unchanged | released v11.17.50 | Gap: cost_claim.py:218 add_argument("--target", type=float) with no range check; :164 `if hi > target` is the only use. Chain: all COST-46..50 rows share cost_claim.py, run in order. |
1286
+ | TEN-COST-47 | fix: cost_claim.py with --loki-arm equal to --floor-arm compares an arm with itself (ratio 1.0) and can claim PROVEN at a target of 1.0 or more; refuse identical arm names with exit 2 | benchmarks/bench/cost_claim.py, benchmarks/bench/tests/test_cost_claim.py | MEDIUM | red: pytest benchmarks/bench/tests/test_cost_claim.py -q -k same_arm fails; green: passes | released v11.17.50 | Gap: cost_claim.py:233-234 filters both arms by arg equality with no distinctness check. Serialize after TEN-COST-46. |
1287
+ | TEN-COST-48 | enhancement: cost_claim.py checks engine and loki_version are single-valued but not the model, so a floor arm on one model and a loki arm on a cheaper one reads as a cost win; add a model reason (missing, or differing between arms or within an arm) to the NOT PROVEN reasons | benchmarks/bench/cost_claim.py, benchmarks/bench/tests/test_cost_claim.py | MEDIUM | red: pytest benchmarks/bench/tests/test_cost_claim.py -q -k model_mismatch fails; green: passes, equal-model fixtures keep their verdict | released v11.17.50 | Gap: grep -n model benchmarks/bench/cost_claim.py returned 0 although attribution.py:267 writes model on every row (the C-F1 pin). Serialize after TEN-COST-47. |
1288
+ | TEN-COST-49 | feature: cost_claim.py verdict names the loki arm's cost_profile (lean, default, off) and refuses a loki arm that mixes profiles, so a lean-profile cost claim versus Claude Code is explicit evidence rather than an inference | benchmarks/bench/cost_claim.py, benchmarks/bench/tests/test_cost_claim.py | MEDIUM | red: pytest benchmarks/bench/tests/test_cost_claim.py -q -k cost_profile fails (no cost_profile key in the verdict); green: passes, verdict gains cost_profile and a mixed arm adds a NOT PROVEN reason | ready@2026-10-17T12:00Z | Gap: attribution.py:263 and :737 stamp cost_profile on loki rows but grep -n cost_profile benchmarks/bench/cost_claim.py returned 0. Serialize after TEN-COST-48. |
1289
+ | TEN-COST-50 | fix: `cost_claim.py --exclude-task typo` silently excludes nothing and still prints excluded_tasks, so a mistyped exclusion looks applied; warn on stderr for any excluded name that matches no row and list it as unmatched_exclusions in the verdict | benchmarks/bench/cost_claim.py, benchmarks/bench/tests/test_cost_claim.py | LOW | red: pytest benchmarks/bench/tests/test_cost_claim.py -q -k unmatched_exclusion fails; green: passes | ready@2026-10-17T12:00Z | Gap: cost_claim.py:231 builds excl from the flag and :232 filters rows with no membership check. Serialize after TEN-COST-49. |
1290
+ | TEN-STABILITY-39 | enhancement: a non-numeric LOKI_PROVIDER_CALL_TIMEOUT (for example `2h`) silently disables the provider call timeout; print one stderr note naming the bad value and the 7200 default in effect, keep 0 as the deliberate no-timeout opt-out | loki-ts/src/runner/providers.ts, loki-ts/tests/runner/providers.test.ts | MEDIUM | red: cd loki-ts && bun test tests/runner/providers.test.ts -t "bad call timeout" fails (no note, timeout undefined); green: passes, unset and 0 behave as before | released v11.17.50 | Gap: providers.ts:543-547 treats NaN like 0 (comment: non-numeric means no timeout) with no message; a typo removes the safety cap on a paid call. |
1291
+ | TEN-STABILITY-40 | fix: a negative or garbage LOKI_QUEUE_ITEM_TIMEOUT_MIN kills every queued item at once (`Number(x) // 120` keeps a negative); accept only positive finite values and fall back to 120 with one stderr note | loki-ts/src/commands/queue.ts, loki-ts/tests/commands/queue.test.ts | LOW | red: cd loki-ts && bun test tests/commands/queue.test.ts -t "negative item timeout" fails; green: passes, valid values unchanged | released v11.17.50 | Gap: queue.ts:467 const mins = Number(process.env["LOKI_QUEUE_ITEM_TIMEOUT_MIN"]) // 120; setTimeout(proc.kill, mins * 60000) with mins -5 fires immediately. |
1292
+ | TEN-STABILITY-41 | fix: a negative LOKI_COUNCIL_REVIEW_TIMEOUT aborts the council review call immediately and the vote reads as a failure; accept only positive finite seconds, else 600 | loki-ts/src/council/voter_agents.ts, loki-ts/tests/council/voter_agents.test.ts | LOW | red: cd loki-ts && bun test tests/council/voter_agents.test.ts -t "negative review timeout" fails; green: passes | released v11.17.50 | Gap: voter_agents.ts:348 (Number(process.env["LOKI_COUNCIL_REVIEW_TIMEOUT"]) // 600) * 1000 keeps a negative number. |
1293
+ | TEN-STABILITY-42 | fix: a negative LOKI_SDK_REVIEW_TIMEOUT makes the SDK code review time out at once and skip the gate; accept only positive finite seconds, else 180 | loki-ts/src/runner/quality_gates.ts, loki-ts/tests/runner/quality_gates.test.ts | LOW | red: cd loki-ts && bun test tests/runner/quality_gates.test.ts -t "negative sdk review timeout" fails; green: passes | released v11.17.50 | Gap: quality_gates.ts:1679 (Number(process.env["LOKI_SDK_REVIEW_TIMEOUT"]) // 180) * 1000, same shape as TEN-STABILITY-41 but a different owner file. |
1294
+ | TEN-STABILITY-43 | fix: `loki memory lessons --jsno`, `memory list --bogus` and `memory brief --bogus` ignore unknown flags and print the default output; reject with a usage line and exit 2 (--json, --task, help unchanged) | loki-ts/src/commands/memory.ts, loki-ts/tests/commands/memory.test.ts | LOW | red: cd loki-ts && bun test tests/commands/memory.test.ts -t "unknown flag" fails; green: passes | released v11.17.50 | Gap: memory.ts:119, :128 and :144 test only includes("--json") or indexOf("--task"); no arm validates the rest. Other subcommands defer to bash and are out of scope. |
1295
+ | TEN-STABILITY-44 | fix: `loki control stop extra`, `control status --jsno` and `control status extra` ignore the surplus token; a mistyped --json prints the human form. Reject unknown flags and extra args with the usage line and exit 2 | loki-ts/src/commands/control.ts, loki-ts/tests/commands/control.test.ts | LOW | red: cd loki-ts && bun test tests/commands/control.test.ts -t "status extra args" fails; green: passes, --json and bare forms unchanged | released v11.17.50 | Gap: control.ts:154 args.includes("--json") only; the switch at :228-232 passes rest to status and calls stopControlPlane(env) with no arg check. |
1296
+ | TEN-STABILITY-45 | enhancement: the per-repo engine cache under ~/.loki/cache/v10 writes a repomap2-<tree>.json and testmap-<tree>.json for every tree hash and never removes old ones, so it grows without bound; after a write keep the newest 5 files of each kind and delete the rest | loki-ts/src/engine10/cache.ts, loki-ts/tests/engine10/cache.test.ts | MEDIUM | red: cd loki-ts && bun test tests/engine10/cache.test.ts -t "prunes old trees" fails (8 files remain after 8 writes); green: passes, hit and miss behavior unchanged, prune failure never throws | released v11.17.50 | Gap: grep -nE 'readdirSync/unlinkSync/rmSync' loki-ts/src/engine10/cache.ts returned 0; writeJson at :41 only mkdir and write. |
1297
+ | TEN-STABILITY-46 | fix: fetchIssue shells out to bash and gh with execFileSync and no timeout, so a hung gh or network stall blocks Intake forever; add a 60 second timeout and surface it as the existing fetch failure | loki-ts/src/engine10/fetch_issue.ts, loki-ts/tests/engine10/fetch_issue.test.ts (new: no test file owns fetch_issue.ts, grep -rl fetch_issue loki-ts/tests shows only allowlists) | MEDIUM | red: cd loki-ts && bun test tests/engine10/fetch_issue.test.ts -t "timeout" fails (defaultExec passes no timeout); green: passes, injected Execer path unchanged | released v11.17.50 | Gap: fetch_issue.ts:26-28 defaultExec = execFileSync(cmd, args, { encoding, env }) with no timeout option. |
1298
+ | TEN-STABILITY-47 | fix: loki doctor runs several spawnSync helpers (effective-provider, providers-json, providers, detect-sdk, python3 cryptography import, provider model and key probes) with no timeout, so one hung script freezes the whole doctor; add a 15 second timeout to each and report the check as skipped | loki-ts/src/commands/doctor.ts, loki-ts/tests/commands/doctor.test.ts | MEDIUM | red: cd loki-ts && bun test tests/commands/doctor.test.ts -t "hung helper" fails; green: passes, fast helpers byte identical | released v11.17.50 | Gap: doctor.ts:646, :799, :821, :1324, :1595, :1918, :1924 spawnSync without timeout (only :1046 and :1979 set one). |
1299
+ | TEN-STABILITY-48 | fix: invalid or negative LOKI_E10_WALL_SNAPSHOT_TIMEOUT_S and LOKI_E10_WALL_SNAPSHOT_MAX_MB keep the negative value (`Number(x) // default`), so the Wall base snapshot is skipped or capped at once; accept positive finite values only | loki-ts/src/contrib/wall_snapshot.ts, loki-ts/tests/engine10/wall_concurrent_fidelity.test.ts | LOW | red: cd loki-ts && bun test tests/engine10/wall_concurrent_fidelity.test.ts -t "negative snapshot env" fails; green: passes | released v11.17.50 | Gap: wall_snapshot.ts:13 timeoutMs and maxMb use Number(...) // default. |
1300
+ | TEN-SPEED-16 | enhancement: the verify stage runs vitest, jest, tsc and eslint through bare `npx`, which downloads a missing tool and can stall a Wall for minutes; use `npx --no-install` so a missing tool fails fast as a tool-missing check | loki-ts/src/engine10/stages/verify.ts, loki-ts/tests/engine10/verify.test.ts | MEDIUM | red: cd loki-ts && bun test tests/engine10/verify.test.ts -t "npx no install" fails (argv lacks --no-install); green: passes, existing runnerCmd shapes otherwise unchanged | released v11.17.50 | Gap: verify.ts:58-59 ["npx", ["vitest", ...]] and :243-244 npx tsc and npx eslint. |
1301
+ | TEN-UX-37 | enhancement: a mistyped provider subcommand (`loki provider lst`) says only "unknown subcommand"; add an exported suggestWord(typo, names) next to levenshtein in cli/registry.ts and print a "Did you mean 'list'?" line, exit 2 unchanged | loki-ts/src/cli/registry.ts, loki-ts/src/commands/provider.ts, loki-ts/tests/commands/provider.test.ts | LOW | red: cd loki-ts && bun test tests/commands/provider.test.ts -t "did you mean" fails; green: passes, no suggestion when nothing is within distance 2 | released v11.17.50 | Gap: registry.ts:346 suggestCommand covers top-level commands only; provider.ts:208 prints no suggestion. Chain: UX-37 first, then UX-38, UX-39 and TEN-DEPLOY-23 reuse suggestWord. |
1302
+ | TEN-UX-38 | fix: `loki wiki bogus` prints the help on stdout and exits 1 while the sibling commands print errors on stderr and exit 2; exit 2, send the help to stderr and add the Did you mean line from suggestWord | loki-ts/src/commands/wiki.ts, loki-ts/tests/commands/wiki.test.ts | LOW | red: cd loki-ts && bun test tests/commands/wiki.test.ts -t "unknown command exits 2" fails (exit 1); green: passes, known verbs unchanged | released v11.17.50 | Gap: wiki.ts:168-170 writes help to stdout and returns 1; crash.ts:292 and rollback.ts:135 return 2. Serialize after TEN-UX-37. |
1303
+ | TEN-UX-39 | enhancement: `loki crash lst` prints the unknown subcommand and the whole help; add the Did you mean line from suggestWord before the help, exit 2 unchanged | loki-ts/src/commands/crash.ts, loki-ts/tests/commands/crash.test.ts | LOW | red: cd loki-ts && bun test tests/commands/crash.test.ts -t "did you mean" fails; green: passes | released v11.17.50 | Gap: crash.ts:292-294 no suggestion. Serialize after TEN-UX-38. |
1304
+ | TEN-DEPLOY-23 | enhancement: `loki rollback lst` prints Unknown subcommand and the help with no hint toward `list`; add the Did you mean line from suggestWord, exit 2 unchanged (a rollback typo near a destructive verb should be caught before the user retypes) | loki-ts/src/commands/rollback.ts, loki-ts/tests/commands/rollback.test.ts | LOW | red: cd loki-ts && bun test tests/commands/rollback.test.ts -t "did you mean" fails; green: passes, existing unknown-subcommand test (exit 2) unchanged | released v11.17.50 | Gap: rollback.ts:135-137 no suggestion. Serialize after TEN-UX-39. |
1305
+ | TEN-QUALITY-29 | fix: LOKI_E10_WALL_LIMIT_S above 300 is silently clamped to 300 and an invalid LOKI_E10_SMALL_MAX_CHARS silently becomes 600; print one stderr note naming the value used so the user sees the setting was not honored | loki-ts/src/engine10/sizing.ts, loki-ts/tests/engine10/sizing.test.ts | LOW | red: cd loki-ts && bun test tests/engine10/sizing.test.ts -t "clamp note" fails; green: passes, returned numbers byte identical | released v11.17.50 | Gap: sizing.ts:58 Math.min(300, n) and :18 smallMaxChars fall back with no message, unlike envCapS/warnBadCap in engine10/types.ts. |
1306
+ | TEN-QUALITY-30 | fix: an invalid LOKI_E10_REPOMAP_MAX_FILES (abc, 0, -5, 1.5) silently falls back to 2000; print one stderr note naming the bad value and the default | loki-ts/src/engine10/repomap.ts, loki-ts/tests/engine10/repomap.test.ts | LOW | red: cd loki-ts && bun test tests/engine10/repomap.test.ts -t "bad max files note" fails; green: passes, cap value unchanged | released v11.17.50 | Gap: repomap.ts:19 maxFilesCap returns 2000 for any non-positive-integer with no warning. |
1307
+ | TEN-QUALITY-31 | fix: a non-numeric LOKI_E10_SNAPSHOT_MAX makes `st.size > NaN` false, so the intake snapshot hashes every untracked file however large; fall back to the 50 MB default when the value is not a positive finite number | loki-ts/src/e10ext/preexisting_dirty.ts, loki-ts/tests/e10ext/preexisting_dirty.test.ts (new: no test owns snapshotUntracked, only incidental use in seal.test.ts and machine.test.ts) | LOW | red: cd loki-ts && bun test tests/e10ext/preexisting_dirty.test.ts -t "bad snapshot max" fails; green: passes | released v11.17.50 | Gap: preexisting_dirty.ts:38 st.size > Number(process.env.LOKI_E10_SNAPSHOT_MAX ?? 50e6) with no finite check. |
1308
+ | TEN-ADOPTION-27 | enhancement: LOKI_AI_MARKING only turns on for the exact string 1; accept 1, true and yes (any case) like the other opt-in flags, so the compliance marking users set as true is not silently off | loki-ts/src/engine10/pr_body.ts, loki-ts/tests/engine10/ai_marking.test.ts | LOW | red: cd loki-ts && bun test tests/engine10/ai_marking.test.ts -t "true enables" fails; green: passes, unset and 0 stay off | released v11.17.50 | Gap: pr_body.ts:5 env["LOKI_AI_MARKING"] === "1". |
package/docs/v10/DEPS.md CHANGED
@@ -17,6 +17,7 @@ Inventory only. No dependency, lockfile, workflow or image was changed to produc
17
17
  | `loki-ts/tests/fixtures/monorepo-fc01/backend/package.json` | `vitest` | `^1.0.0` | `5.0.3` | MAJOR | test fixture, not a real dependency |
18
18
  | `loki-ts/tests/fixtures/project-model/firelater-17/backend/package.json` | `vitest` | `^1.0.0` | `5.0.3` | MAJOR | test fixture, not a real dependency |
19
19
  | `loki-ts/tests/fixtures/monorepo-fc22/backend/package.json` | `vitest` | `^1.0.0` | `5.0.3` | MAJOR | test fixture, not a real dependency |
20
+ | `loki-ts/todo-app/package.json` | `@types/bun` | `latest` | `1.4.2` | unknown | no numeric version in spec (unpinned or a dist-tag like 'latest'); cannot compute a bump class |
20
21
  | `package.json` | `@resvg/resvg-wasm` | `^2.6.2` | `2.6.2` | up-to-date | |
21
22
  | `package.json` | `@types/node` | `^25.2.0` | `26.6.4` | MAJOR | |
22
23
  | `package.json` | `jest` | `^29.7.0` | `30.5.2` | MAJOR | |
@@ -2527,3 +2527,9 @@ v11.17.21 published (Release success on 462fbc40a; npm latest 11.17.21, next 11.
2527
2527
 
2528
2528
  ## 2026-10-17T12:39Z (CoS) v11.17.49
2529
2529
  - P0 fix-forward for main Tests red on cbded2759, 5a6d770f6, e8e750117 (structural guards, bun, shell shards 1,2,3,4,6,7). Local evidence: help-changelog-drift 51/0, shard-durations-drift rc=0, shard-coverage rc=0, cli-commands rc=0, doctor-ux rc=0, cli/test-tour-verify rc=0, bun contract plus touched tests 66 pass 0 fail. Shard 3 cause is CI-only (bun found via node_modules/@oven), reasoned from the log.
2530
+
2531
+ ## 2026-10-17T13:13Z (CoS) v11.17.50
2532
+ - Wave AO shipped: 28 of 30 TEN rows APPROVE and merged clean in 22 chains. TEN-COST-49 FAILED (builder worktree path missing) so TEN-COST-50 was not built; both stay ready. Checks: tsc rc=0; bun changed tests plus guards 1266 pass 4 fail before the dist rebuild, 2 fail after (full_env_spawn_guard on doctor.ts, engine10 budget 5016 of 5000); secure-scan, help-changelog-drift, shard-durations-drift rc=0. Main Tests on 3756bef0f still red (bun, shell shards 0,3,4); a P0 fix-forward agent covers those and the two local reds. Q18-03e fix round 2 (64d5d906d) is in opus re-review.
2533
+
2534
+ ## 2026-10-17T13:29Z (CoS) v11.17.51
2535
+ - P0 fix-forward for the wave AO reds (doctor spawn env, engine10 budget, deep_wiring, workspace e2e, sigpipe, DEPS) released; evidence: targeted bun 570 pass 0 fail, four shell suites rc=0, tsc clean.