loki-mode 11.17.27 → 11.17.29
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.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/ablation.py +438 -0
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +3 -2
- package/docs/INSTALLATION.md +1 -1
- package/docs/v10/BOARD.md +7 -7
- package/docs/v10/CLI-MODERN.md +1 -1
- package/docs/v10/FAILURE-CLASSES.md +7 -0
- package/docs/v10/PROGRESS.md +6 -0
- package/loki-ts/dist/loki.js +916 -909
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +4 -3
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-qBmqnNpf.js → AdminPage-nOM5_DUk.js} +1 -1
- package/web-app/dist/assets/{Avatar-gkDLzbsy.js → Avatar-CGDH9-gR.js} +1 -1
- package/web-app/dist/assets/{Badge-D3zzd0oQ.js → Badge-BeWbTYVE.js} +1 -1
- package/web-app/dist/assets/{Button-BSChxXUT.js → Button-BNKBYiAG.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-BR-LiXqh.js → CockpitPage-CbEzPdi5.js} +1 -1
- package/web-app/dist/assets/{ComparePage-BoGT4pP6.js → ComparePage-D9Rslj5T.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-DPA3y3ZI.js → ErrorBoundary-B_nrpRwD.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-DxsHTcq-.js → EvidenceReceiptPanel-D4rRYbqn.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-RwJsyQBw.js → GitHubIssuesPanel-BQvLUh5v.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-DBijSig1.js → GitHubPRsPanel-CH7Dyrd6.js} +1 -1
- package/web-app/dist/assets/{HomePage-BWBaG7QX.js → HomePage-BHxJZ3Kl.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BJpH6TJK.js → LoginPage-CrazYYjU.js} +1 -1
- package/web-app/dist/assets/{MagicPage-CzLwDhgk.js → MagicPage-C2FJn91q.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-B0Jmyz4p.js → MetricsPage-Dz59bBAM.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-BRrFLpxC.js → NotFoundPage-N1pabiAk.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-DiBWwd9A.js → ProjectPage-DYoQGOAA.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-CcJFtMHn.js → ProjectsPage-pZ-7z54o.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-BjmpBXO4.js → SettingsPage-DKRvbK1L.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-DECphFgW.js → ShowcasePage-BWgXXOtm.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-nkBxwx6G.js → SystemSettingsPage-Dk4Bzcq_.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-DIiKZyEs.js → TeamsPage-DHZQEjY7.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CTqzPkp4.js → TemplatesPage-C6p9AzQQ.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-OpU_Ldkr.js → TerminalOutput-CtwglGdt.js} +1 -1
- package/web-app/dist/assets/{activity-B9-wYY0f.js → activity-BgirK2TT.js} +1 -1
- package/web-app/dist/assets/{bell-Bi5vT41G.js → bell-NPG8g71D.js} +1 -1
- package/web-app/dist/assets/{bot-BXaP9xNJ.js → bot-CU_mRs9-.js} +1 -1
- package/web-app/dist/assets/{check-CMpexbPr.js → check-D5UCrL7s.js} +1 -1
- package/web-app/dist/assets/{chevron-left-Bo5eh5O1.js → chevron-left-Bfj1xzjy.js} +1 -1
- package/web-app/dist/assets/{circle-alert-A-XVOZGi.js → circle-alert-ZddFscX0.js} +1 -1
- package/web-app/dist/assets/{clock-17R3UYFV.js → clock-CdbiCMDa.js} +1 -1
- package/web-app/dist/assets/{cloud-DZnkLP0O.js → cloud-D4jNgoPd.js} +1 -1
- package/web-app/dist/assets/{code-xml-D7sQ8xWr.js → code-xml-DX2AsHzH.js} +1 -1
- package/web-app/dist/assets/{database-C7HOlz8H.js → database-DpwUzfhE.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-DLUdXm5q.js → dollar-sign-CJPL_5Fy.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-D2hbIpjS.js → file-code-corner-B-s99s3w.js} +1 -1
- package/web-app/dist/assets/{file-plus-YDrT0Vpf.js → file-plus-D5IXpIz8.js} +1 -1
- package/web-app/dist/assets/{globe-PZQOiDvl.js → globe-Dw08p3-o.js} +1 -1
- package/web-app/dist/assets/{hammer-CU5_fxG0.js → hammer-CtBRJvWo.js} +1 -1
- package/web-app/dist/assets/{index-CouXhHi0.js → index-DnYZaSsT.js} +3 -3
- package/web-app/dist/assets/{layers-CUBIAXkd.js → layers-CfxWVRur.js} +1 -1
- package/web-app/dist/assets/{loader-circle-DD3782GH.js → loader-circle-BhR9FwJ1.js} +1 -1
- package/web-app/dist/assets/{lock-8qj9HuCt.js → lock-1kVCQsnD.js} +1 -1
- package/web-app/dist/assets/{package-BLRHBPNT.js → package-qV8-Zyoq.js} +1 -1
- package/web-app/dist/assets/{plus-Do84tsD2.js → plus-CZ6Td-xg.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CDwTugNb.js → refresh-cw-B3C0rY77.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-ZMlEP4m1.js → rotate-ccw-DIvx7x-w.js} +1 -1
- package/web-app/dist/assets/{scroll-text-CgcpCCD7.js → scroll-text-Dd1wqcHK.js} +1 -1
- package/web-app/dist/assets/{server-DD2uHuPB.js → server-BAKsBlPl.js} +1 -1
- package/web-app/dist/assets/{shield-alert-21sB04G8.js → shield-alert-BFP6Yjmc.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-pxRsYD1v.js → thumbs-up-DeYyCkjX.js} +1 -1
- package/web-app/dist/assets/{trash-2-Di8cVbZ_.js → trash-2-CFsKTEeM.js} +1 -1
- package/web-app/dist/assets/{trending-down-CMnybRh_.js → trending-down-BAZYNXIX.js} +1 -1
- package/web-app/dist/assets/{trending-up-C6aVl4id.js → trending-up-CUEAWCMi.js} +1 -1
- package/web-app/dist/assets/{upload-5I2cfPbd.js → upload-BaYVlIfV.js} +1 -1
- package/web-app/dist/assets/{usePolling-Ddx5By2o.js → usePolling-CQ3I38RA.js} +1 -1
- package/web-app/dist/assets/{user-CWgTm2-d.js → user-C7ePVV51.js} +1 -1
- 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.
|
|
6
|
+
# Loki Mode v11.17.29
|
|
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.
|
|
486
|
+
**v11.17.29 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
11.17.
|
|
1
|
+
11.17.29
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Load-bearing ablation producer (moat P8, S-113).
|
|
3
|
+
|
|
4
|
+
Answers one question for the proof: do the recorded acceptance checks actually
|
|
5
|
+
exercise the change? Every changed or added code unit in a non-test source file
|
|
6
|
+
is replaced with a no-op, the recorded check command is re-run, and the working
|
|
7
|
+
tree is restored byte-for-byte (content, mode and mtime).
|
|
8
|
+
|
|
9
|
+
python3 autonomy/lib/ablation.py --repo <dir> --base <sha> --loki-dir <dir>/.loki
|
|
10
|
+
|
|
11
|
+
Reads <loki-dir>/quality/test-results.json ("command", "exit_code").
|
|
12
|
+
Writes <loki-dir>/quality/ablation.json:
|
|
13
|
+
{"status": "load_bearing" | "not_load_bearing" | "not_applicable",
|
|
14
|
+
"reason": "<non-empty text>",
|
|
15
|
+
"command": "<command re-run>",
|
|
16
|
+
"ablated_files": ["<path>", ...],
|
|
17
|
+
"ablated_exit_code": <int or null>}
|
|
18
|
+
|
|
19
|
+
The consumer is proof-generator.py (_collect_ablation copies the record into
|
|
20
|
+
facts.ablation; _compute_headline refuses VERIFIED on not_load_bearing).
|
|
21
|
+
|
|
22
|
+
Deterministic, no network, no model call. Exit 0 whenever a status was
|
|
23
|
+
recorded; exit 2 on bad arguments or when the record cannot be written.
|
|
24
|
+
|
|
25
|
+
A code unit is a Python function or method (ast). A unit is changed when a
|
|
26
|
+
changed line falls in its own lines (lines of nested functions count for the
|
|
27
|
+
nested unit only). Changed code in other languages records not_applicable with
|
|
28
|
+
the reason naming the files, never a guessed verdict.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import argparse
|
|
32
|
+
import ast
|
|
33
|
+
import fnmatch
|
|
34
|
+
import json
|
|
35
|
+
import os
|
|
36
|
+
import re
|
|
37
|
+
import shlex
|
|
38
|
+
import signal
|
|
39
|
+
import subprocess
|
|
40
|
+
import sys
|
|
41
|
+
|
|
42
|
+
STATUS_LOAD_BEARING = "load_bearing"
|
|
43
|
+
STATUS_NOT_LOAD_BEARING = "not_load_bearing"
|
|
44
|
+
STATUS_NOT_APPLICABLE = "not_applicable"
|
|
45
|
+
|
|
46
|
+
# Extensions treated as source code. Anything else (docs, config, data) is not
|
|
47
|
+
# code, so a change touching only those is not_applicable.
|
|
48
|
+
CODE_EXTENSIONS = {
|
|
49
|
+
".py", ".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".go", ".rs", ".java",
|
|
50
|
+
".kt", ".rb", ".php", ".cs", ".c", ".cc", ".cpp", ".h", ".hpp", ".swift",
|
|
51
|
+
".scala", ".sh", ".bash",
|
|
52
|
+
}
|
|
53
|
+
SUPPORTED_EXTENSIONS = {".py"}
|
|
54
|
+
|
|
55
|
+
TEST_DIR_NAMES = {"test", "tests", "__tests__", "spec", "specs", "e2e"}
|
|
56
|
+
TEST_FILE_GLOBS = (
|
|
57
|
+
"test_*", "*_test.*", "*.test.*", "*.spec.*", "*_spec.*", "conftest.py",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
DEFAULT_TIMEOUT_SEC = 600
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _git(repo, *args):
|
|
64
|
+
proc = subprocess.run(
|
|
65
|
+
["git", "-C", repo] + list(args),
|
|
66
|
+
stdout=subprocess.PIPE,
|
|
67
|
+
stderr=subprocess.PIPE,
|
|
68
|
+
check=False,
|
|
69
|
+
)
|
|
70
|
+
if proc.returncode != 0:
|
|
71
|
+
raise RuntimeError(
|
|
72
|
+
"git %s failed: %s"
|
|
73
|
+
% (" ".join(args[:2]), proc.stderr.decode("utf-8", "replace").strip())
|
|
74
|
+
)
|
|
75
|
+
return proc.stdout.decode("utf-8", "replace")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def changed_files(repo, base):
|
|
79
|
+
"""Paths changed since base: committed, staged, unstaged and untracked."""
|
|
80
|
+
tracked = _git(repo, "diff", "--name-only", "--diff-filter=AMR", base, "--")
|
|
81
|
+
untracked = _git(repo, "ls-files", "--others", "--exclude-standard")
|
|
82
|
+
out = []
|
|
83
|
+
for line in (tracked + "\n" + untracked).splitlines():
|
|
84
|
+
p = line.strip()
|
|
85
|
+
if not p or p in out:
|
|
86
|
+
continue
|
|
87
|
+
if p == ".loki" or p.startswith(".loki/"):
|
|
88
|
+
continue
|
|
89
|
+
out.append(p)
|
|
90
|
+
return out
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def untracked_files(repo):
|
|
94
|
+
return set(
|
|
95
|
+
p.strip()
|
|
96
|
+
for p in _git(repo, "ls-files", "--others", "--exclude-standard").splitlines()
|
|
97
|
+
if p.strip()
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
_HUNK_RE = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def changed_lines(repo, base, path, is_untracked):
|
|
105
|
+
"""Set of 1-based line numbers in the working-tree file that are new or
|
|
106
|
+
changed relative to base. None means every line (an added file)."""
|
|
107
|
+
if is_untracked:
|
|
108
|
+
return None
|
|
109
|
+
diff = _git(repo, "diff", "-U0", "--no-color", base, "--", path)
|
|
110
|
+
if not diff.strip():
|
|
111
|
+
return set()
|
|
112
|
+
lines = set()
|
|
113
|
+
saw_hunk = False
|
|
114
|
+
for line in diff.splitlines():
|
|
115
|
+
m = _HUNK_RE.match(line)
|
|
116
|
+
if not m:
|
|
117
|
+
if line.startswith("new file mode"):
|
|
118
|
+
return None
|
|
119
|
+
continue
|
|
120
|
+
saw_hunk = True
|
|
121
|
+
start = int(m.group(1))
|
|
122
|
+
count = int(m.group(2)) if m.group(2) is not None else 1
|
|
123
|
+
if count == 0:
|
|
124
|
+
# Pure deletion: the line after the cut belongs to the edited unit.
|
|
125
|
+
lines.add(max(start, 1))
|
|
126
|
+
lines.add(start + 1)
|
|
127
|
+
else:
|
|
128
|
+
lines.update(range(start, start + count))
|
|
129
|
+
return lines if saw_hunk else set()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def command_paths(repo, command):
|
|
133
|
+
"""Repo-relative files the check command names directly (the check
|
|
134
|
+
scripts themselves are never ablated)."""
|
|
135
|
+
try:
|
|
136
|
+
tokens = shlex.split(command or "")
|
|
137
|
+
except ValueError:
|
|
138
|
+
tokens = (command or "").split()
|
|
139
|
+
out = set()
|
|
140
|
+
for tok in tokens:
|
|
141
|
+
if not tok or tok.startswith("-"):
|
|
142
|
+
continue
|
|
143
|
+
cand = os.path.normpath(os.path.join(repo, tok))
|
|
144
|
+
if os.path.isfile(cand):
|
|
145
|
+
out.add(os.path.relpath(cand, repo))
|
|
146
|
+
return out
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def is_test_path(path, check_paths):
|
|
150
|
+
norm = os.path.normpath(path)
|
|
151
|
+
if norm in check_paths:
|
|
152
|
+
return True
|
|
153
|
+
parts = norm.split(os.sep)
|
|
154
|
+
if any(p in TEST_DIR_NAMES for p in parts[:-1]):
|
|
155
|
+
return True
|
|
156
|
+
name = parts[-1]
|
|
157
|
+
return any(fnmatch.fnmatch(name, g) for g in TEST_FILE_GLOBS)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _function_nodes(tree):
|
|
161
|
+
"""(node, parent_function_or_None) for every function in the module."""
|
|
162
|
+
out = []
|
|
163
|
+
|
|
164
|
+
def walk(node, parent_fn):
|
|
165
|
+
for child in ast.iter_child_nodes(node):
|
|
166
|
+
if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
167
|
+
out.append((child, parent_fn))
|
|
168
|
+
walk(child, child)
|
|
169
|
+
else:
|
|
170
|
+
walk(child, parent_fn)
|
|
171
|
+
|
|
172
|
+
walk(tree, None)
|
|
173
|
+
return out
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def select_units(source, lines):
|
|
177
|
+
"""Function nodes to no-op. lines=None selects every function."""
|
|
178
|
+
tree = ast.parse(source)
|
|
179
|
+
funcs = _function_nodes(tree)
|
|
180
|
+
if lines is None:
|
|
181
|
+
chosen = [n for n, _ in funcs]
|
|
182
|
+
else:
|
|
183
|
+
chosen = []
|
|
184
|
+
for node, _ in funcs:
|
|
185
|
+
start = min([node.lineno] + [d.lineno for d in node.decorator_list])
|
|
186
|
+
own = set(range(start, node.end_lineno + 1))
|
|
187
|
+
for other, parent in funcs:
|
|
188
|
+
if parent is node:
|
|
189
|
+
own -= set(range(other.lineno, other.end_lineno + 1))
|
|
190
|
+
if own & lines:
|
|
191
|
+
chosen.append(node)
|
|
192
|
+
# A unit nested inside another chosen unit is already gone with its parent.
|
|
193
|
+
chosen_ids = set(id(n) for n in chosen)
|
|
194
|
+
parent_of = dict((id(n), p) for n, p in funcs)
|
|
195
|
+
top = []
|
|
196
|
+
for n in chosen:
|
|
197
|
+
p = parent_of.get(id(n))
|
|
198
|
+
nested = False
|
|
199
|
+
while p is not None:
|
|
200
|
+
if id(p) in chosen_ids:
|
|
201
|
+
nested = True
|
|
202
|
+
break
|
|
203
|
+
p = parent_of.get(id(p))
|
|
204
|
+
if not nested:
|
|
205
|
+
top.append(n)
|
|
206
|
+
return top
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def ablate_source(source, units):
|
|
210
|
+
"""Return source with each unit's body replaced by `return None`."""
|
|
211
|
+
lines = source.splitlines(keepends=True)
|
|
212
|
+
# Character offsets per line so (lineno, col) maps into the text. ast
|
|
213
|
+
# col_offset is in UTF-8 bytes, so slice each line as bytes.
|
|
214
|
+
spans = []
|
|
215
|
+
for node in units:
|
|
216
|
+
first, last = node.body[0], node.body[-1]
|
|
217
|
+
spans.append((first.lineno, first.col_offset, last.end_lineno, last.end_col_offset))
|
|
218
|
+
spans.sort(reverse=True)
|
|
219
|
+
blines = [ln.encode("utf-8") for ln in lines]
|
|
220
|
+
for sl, sc, el, ec in spans:
|
|
221
|
+
head = blines[sl - 1][:sc]
|
|
222
|
+
tail = blines[el - 1][ec:]
|
|
223
|
+
blines[sl - 1:el] = [head + b"return None" + tail]
|
|
224
|
+
return b"".join(blines).decode("utf-8")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _record(loki_dir, status, reason, command, ablated_files, exit_code):
|
|
228
|
+
rec = {
|
|
229
|
+
"status": status,
|
|
230
|
+
"reason": reason,
|
|
231
|
+
"command": command or "",
|
|
232
|
+
"ablated_files": list(ablated_files),
|
|
233
|
+
"ablated_exit_code": exit_code,
|
|
234
|
+
}
|
|
235
|
+
qdir = os.path.join(loki_dir, "quality")
|
|
236
|
+
os.makedirs(qdir, exist_ok=True)
|
|
237
|
+
path = os.path.join(qdir, "ablation.json")
|
|
238
|
+
tmp = path + ".tmp"
|
|
239
|
+
with open(tmp, "w", encoding="utf-8") as f:
|
|
240
|
+
json.dump(rec, f, indent=2, sort_keys=True)
|
|
241
|
+
f.write("\n")
|
|
242
|
+
os.replace(tmp, path)
|
|
243
|
+
return rec
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _read_tests(loki_dir):
|
|
247
|
+
try:
|
|
248
|
+
with open(os.path.join(loki_dir, "quality", "test-results.json"), encoding="utf-8") as f:
|
|
249
|
+
raw = json.load(f)
|
|
250
|
+
except (OSError, ValueError):
|
|
251
|
+
return None
|
|
252
|
+
return raw if isinstance(raw, dict) else None
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class _Snapshot(object):
|
|
256
|
+
"""Exact bytes, mode and mtime of each file, restored in restore()."""
|
|
257
|
+
|
|
258
|
+
def __init__(self):
|
|
259
|
+
self.saved = {}
|
|
260
|
+
|
|
261
|
+
def save(self, path):
|
|
262
|
+
st = os.stat(path)
|
|
263
|
+
with open(path, "rb") as f:
|
|
264
|
+
self.saved[path] = (f.read(), st.st_mode, st.st_atime_ns, st.st_mtime_ns)
|
|
265
|
+
|
|
266
|
+
def restore(self):
|
|
267
|
+
errors = []
|
|
268
|
+
for path, (data, mode, atime, mtime) in self.saved.items():
|
|
269
|
+
try:
|
|
270
|
+
with open(path, "wb") as f:
|
|
271
|
+
f.write(data)
|
|
272
|
+
os.chmod(path, mode & 0o7777)
|
|
273
|
+
os.utime(path, ns=(atime, mtime))
|
|
274
|
+
except OSError as e:
|
|
275
|
+
errors.append("%s: %s" % (path, e))
|
|
276
|
+
return errors
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _run_command(repo, command, timeout):
|
|
280
|
+
env = dict(os.environ)
|
|
281
|
+
# The re-run must not leave bytecode for the ablated sources behind.
|
|
282
|
+
env["PYTHONDONTWRITEBYTECODE"] = "1"
|
|
283
|
+
proc = subprocess.Popen(
|
|
284
|
+
command,
|
|
285
|
+
shell=True,
|
|
286
|
+
cwd=repo,
|
|
287
|
+
env=env,
|
|
288
|
+
stdin=subprocess.DEVNULL,
|
|
289
|
+
stdout=subprocess.DEVNULL,
|
|
290
|
+
stderr=subprocess.DEVNULL,
|
|
291
|
+
start_new_session=True,
|
|
292
|
+
)
|
|
293
|
+
try:
|
|
294
|
+
return proc.wait(timeout=timeout), False
|
|
295
|
+
except subprocess.TimeoutExpired:
|
|
296
|
+
try:
|
|
297
|
+
os.killpg(proc.pid, signal.SIGKILL)
|
|
298
|
+
except OSError:
|
|
299
|
+
pass
|
|
300
|
+
proc.wait()
|
|
301
|
+
return None, True
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def ablate(repo, base, loki_dir, timeout=DEFAULT_TIMEOUT_SEC):
|
|
305
|
+
repo = os.path.abspath(repo)
|
|
306
|
+
tests = _read_tests(loki_dir)
|
|
307
|
+
command = str((tests or {}).get("command") or "").strip()
|
|
308
|
+
if not command:
|
|
309
|
+
return _record(loki_dir, STATUS_NOT_APPLICABLE,
|
|
310
|
+
"no recorded check command in quality/test-results.json",
|
|
311
|
+
"", [], None)
|
|
312
|
+
if tests.get("exit_code") != 0:
|
|
313
|
+
return _record(loki_dir, STATUS_NOT_APPLICABLE,
|
|
314
|
+
"the recorded checks did not pass on the unablated change "
|
|
315
|
+
"(exit_code=%r), so an ablation proves nothing" % tests.get("exit_code"),
|
|
316
|
+
command, [], None)
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
files = changed_files(repo, base)
|
|
320
|
+
untracked = untracked_files(repo)
|
|
321
|
+
except RuntimeError as e:
|
|
322
|
+
return _record(loki_dir, STATUS_NOT_APPLICABLE,
|
|
323
|
+
"cannot read the change since base %s: %s" % (base, e),
|
|
324
|
+
command, [], None)
|
|
325
|
+
|
|
326
|
+
check_paths = command_paths(repo, command)
|
|
327
|
+
code = [p for p in files
|
|
328
|
+
if os.path.splitext(p)[1].lower() in CODE_EXTENSIONS
|
|
329
|
+
and not is_test_path(p, check_paths)
|
|
330
|
+
and os.path.isfile(os.path.join(repo, p))]
|
|
331
|
+
if not code:
|
|
332
|
+
return _record(loki_dir, STATUS_NOT_APPLICABLE,
|
|
333
|
+
"no non-test source code changed since base (%d changed "
|
|
334
|
+
"file(s), all docs, config or tests)" % len(files),
|
|
335
|
+
command, [], None)
|
|
336
|
+
unsupported = [p for p in code if os.path.splitext(p)[1].lower() not in SUPPORTED_EXTENSIONS]
|
|
337
|
+
|
|
338
|
+
plan = [] # (path, new_source)
|
|
339
|
+
skipped = []
|
|
340
|
+
for p in code:
|
|
341
|
+
if p in unsupported:
|
|
342
|
+
continue
|
|
343
|
+
full = os.path.join(repo, p)
|
|
344
|
+
try:
|
|
345
|
+
with open(full, "rb") as f:
|
|
346
|
+
source = f.read().decode("utf-8")
|
|
347
|
+
units = select_units(source, changed_lines(repo, base, p, p in untracked))
|
|
348
|
+
except (OSError, UnicodeDecodeError, SyntaxError, ValueError, RuntimeError) as e:
|
|
349
|
+
skipped.append("%s (%s)" % (p, type(e).__name__))
|
|
350
|
+
continue
|
|
351
|
+
if units:
|
|
352
|
+
plan.append((p, ablate_source(source, units)))
|
|
353
|
+
|
|
354
|
+
if not plan:
|
|
355
|
+
why = []
|
|
356
|
+
if unsupported:
|
|
357
|
+
why.append("changed code in a language ablation does not parse: %s"
|
|
358
|
+
% ", ".join(sorted(unsupported)))
|
|
359
|
+
if skipped:
|
|
360
|
+
why.append("unreadable source: %s" % ", ".join(sorted(skipped)))
|
|
361
|
+
if not why:
|
|
362
|
+
why.append("the changed source lines sit outside any function, so "
|
|
363
|
+
"there is no code unit to no-op")
|
|
364
|
+
return _record(loki_dir, STATUS_NOT_APPLICABLE, "; ".join(why), command, [], None)
|
|
365
|
+
|
|
366
|
+
snap = _Snapshot()
|
|
367
|
+
for p, _ in plan:
|
|
368
|
+
snap.save(os.path.join(repo, p))
|
|
369
|
+
|
|
370
|
+
def _on_signal(signum, _frame):
|
|
371
|
+
raise KeyboardInterrupt("signal %d" % signum)
|
|
372
|
+
|
|
373
|
+
old = {}
|
|
374
|
+
for sig in (signal.SIGTERM, signal.SIGHUP):
|
|
375
|
+
try:
|
|
376
|
+
old[sig] = signal.signal(sig, _on_signal)
|
|
377
|
+
except (ValueError, OSError):
|
|
378
|
+
pass
|
|
379
|
+
try:
|
|
380
|
+
for p, new_source in plan:
|
|
381
|
+
with open(os.path.join(repo, p), "wb") as f:
|
|
382
|
+
f.write(new_source.encode("utf-8"))
|
|
383
|
+
exit_code, timed_out = _run_command(repo, command, timeout)
|
|
384
|
+
finally:
|
|
385
|
+
errors = snap.restore()
|
|
386
|
+
for sig, handler in old.items():
|
|
387
|
+
signal.signal(sig, handler)
|
|
388
|
+
if errors:
|
|
389
|
+
raise RuntimeError("working tree restore failed: %s" % "; ".join(errors))
|
|
390
|
+
|
|
391
|
+
ablated = [p for p, _ in plan]
|
|
392
|
+
note = ""
|
|
393
|
+
if unsupported:
|
|
394
|
+
note = " (not ablated, unsupported language: %s)" % ", ".join(sorted(unsupported))
|
|
395
|
+
if timed_out:
|
|
396
|
+
return _record(loki_dir, STATUS_LOAD_BEARING,
|
|
397
|
+
"with the changed code no-oped the checks did not finish "
|
|
398
|
+
"within %ds, so they depend on it%s" % (timeout, note),
|
|
399
|
+
command, ablated, None)
|
|
400
|
+
if exit_code != 0:
|
|
401
|
+
return _record(loki_dir, STATUS_LOAD_BEARING,
|
|
402
|
+
"the checks failed (exit %d) with the changed code no-oped, "
|
|
403
|
+
"so they exercise the change%s" % (exit_code, note),
|
|
404
|
+
command, ablated, exit_code)
|
|
405
|
+
return _record(loki_dir, STATUS_NOT_LOAD_BEARING,
|
|
406
|
+
"the checks still pass with the changed code no-oped, so they "
|
|
407
|
+
"do not exercise the change%s" % note,
|
|
408
|
+
command, ablated, exit_code)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def main(argv=None):
|
|
412
|
+
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
413
|
+
ap.add_argument("--repo", required=True)
|
|
414
|
+
ap.add_argument("--base", required=True)
|
|
415
|
+
ap.add_argument("--loki-dir", required=True)
|
|
416
|
+
ap.add_argument("--timeout", type=int, default=None,
|
|
417
|
+
help="seconds for the ablated re-run (default LOKI_ABLATION_TIMEOUT or 600)")
|
|
418
|
+
args = ap.parse_args(argv)
|
|
419
|
+
timeout = args.timeout
|
|
420
|
+
if timeout is None:
|
|
421
|
+
try:
|
|
422
|
+
timeout = int(os.environ.get("LOKI_ABLATION_TIMEOUT", DEFAULT_TIMEOUT_SEC))
|
|
423
|
+
except ValueError:
|
|
424
|
+
timeout = DEFAULT_TIMEOUT_SEC
|
|
425
|
+
if not os.path.isdir(args.repo):
|
|
426
|
+
print("ablation: --repo is not a directory: %s" % args.repo, file=sys.stderr)
|
|
427
|
+
return 2
|
|
428
|
+
try:
|
|
429
|
+
rec = ablate(args.repo, args.base, args.loki_dir, timeout=max(timeout, 1))
|
|
430
|
+
except (OSError, RuntimeError) as e:
|
|
431
|
+
print("ablation: %s" % e, file=sys.stderr)
|
|
432
|
+
return 2
|
|
433
|
+
print("ablation: %s - %s" % (rec["status"], rec["reason"]))
|
|
434
|
+
return 0
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
if __name__ == "__main__":
|
|
438
|
+
sys.exit(main())
|
package/dashboard/__init__.py
CHANGED
package/docs/CLI-REFERENCE.md
CHANGED
|
@@ -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.
|
|
3
|
+
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.17.29. 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 |
|
|
@@ -44,10 +44,11 @@ Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for th
|
|
|
44
44
|
| `loki wiki generate` | Generate the wiki |
|
|
45
45
|
| `loki wiki show` | Show a section |
|
|
46
46
|
| `loki wiki ask` | Ask a question |
|
|
47
|
-
| `loki control` | Control Plane (serve, backfill, prune, status) |
|
|
47
|
+
| `loki control` | Control Plane (serve, backfill, prune, stop, status) |
|
|
48
48
|
| `loki control serve` | Run the control plane and UI |
|
|
49
49
|
| `loki control backfill` | Ship DIR/.loki/runs to the control plane |
|
|
50
50
|
| `loki control prune` | Delete matching runs |
|
|
51
|
+
| `loki control stop` | Stop the running control plane |
|
|
51
52
|
| `loki control status` | Show control plane reachability |
|
|
52
53
|
| `loki intent` | Show a past run's intent card |
|
|
53
54
|
| `loki intent show` | Print the intent card and answer |
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -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.
|
|
5
|
+
**Version:** v11.17.29
|
|
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
|
@@ -1024,7 +1024,7 @@ Source: ~/git/autonomi-dev/research/2026-09-30-adoption/SWARM-PROMPT-ADOPTION.md
|
|
|
1024
1024
|
| TEN-COST-01 | Opt-in cost profile: loki start --cost lean / LOKI_COST_PROFILE=lean | loki-ts/src/util/cost_profile.ts (new: no existing module owns engine10 env presets; util/tier.ts is the license seam, not a cost preset), loki-ts/src/commands/start.ts (one --cost value-flag row plus one apply call), loki-ts/tests/util/cost_profile.test.ts (new, pairs with the new util), loki-ts/tests/commands/start.test.ts (one case), tests/test-help-start-flags.sh (expected flag list) | MEDIUM | see Wall in note | released v11.17.25@2026-10-17T05:39Z | wave 2; depends: none. Goal: One user-facing switch that turns on the already-built, flag-gated cost levers together, with each lever named on the start line. The lean profile sets LOKI_ROUTER=1 (the router keeps its own D89 haiku-only-with-advisor rule and escalation ladder) and LOKI_E10_TRIM=1. It only fills unset variables, so explicit user env always wins: LOKI_ROUTER=0 stays 0. Profile off or unset leaves the env byte-identical. It also writes .loki/cost-profile.json listing the levers applied, so nothing is reduced silently (D89 item 3). Works for loki start and, through the env var, for loki chat unchanged. Wall: Red first: - cost_profile.test.ts asserts resolveCostProfile('lean', {}) returns {LOKI_ROUTER:'1', LOKI_E10_TRIM:'1'}. - resolveCostProfile('lean', {LOKI_ROUTER:'0'}) keeps '0'. - An unknown profile name throws a usage error. - 'off' and unset return an empty patch. - start.test.ts: --cost lean reaches the child env and prints one line naming both levers. Mutation: remove the user-wins precedence check and confirm the LOKI_ROUTER=0 case goes red; restore. Gate: cd loki-ts && bun test tests/util/cost_profile.test.ts tests/commands/start.test.ts && bunx tsc --noEmit && bun test tests/engine10/budget.test.ts && bash ../tests/test-help-start-flags.sh && bash ../tests/test-help-changelog-drift.sh Rules: FAILURE-CLASSES row first for any bug fix (D86); no CHANGELOG edits (Release Manager writes notes); DECISIONS numbers assigned by the Chief of Staff; reuse util/secure_scan.ts, the shared badge helper, keys_cmd export and the TS claude login probe instead of new copies; new CLI flags update autonomy/completions and the help-drift guard. |
|
|
1025
1025
|
| TEN-COST-02 | raw-lean baseline mode in the claude_code bench adapter | benchmarks/bench/adapters/claude_code.py, benchmarks/bench/tests/test_model_pin.py | LOW | see Wall in note | released@2026-10-17T02:57Z | merge 3a7942aac, dist bd6348d77; TL APPROVE 60dc6d321 (8 passed, mutation red); post-merge tsc 0 errors, 130 pass. wave 1; depends: none. Goal: An honest same-config raw Claude Code baseline that works under OAuth (no API key). run(..., lean=True) adds: --setting-sources project, --strict-mcp-config, --mcp-config '{"mcpServers":{}}', --disable-slash-commands and --tools Bash,Read,Edit,Write,Glob,Grep, the same tool set as ENGINE10_TOOLS in providers.ts:618. lean and bare together raise ValueError, never silently pick one. The row command string records lean. Wall: Red first: - test_model_pin.py asserts the exact lean argv with a stub runner. - lean=True with bare=True raises. - Default lean=False argv is byte-identical to today's. Mutation: drop --strict-mcp-config from the lean argv; the argv test goes red. Gate: python3 -I -m pytest benchmarks/bench/tests/test_model_pin.py -q Rules: FAILURE-CLASSES row first for any bug fix (D86); no CHANGELOG edits (Release Manager writes notes); DECISIONS numbers assigned by the Chief of Staff; reuse util/secure_scan.ts, the shared badge helper, keys_cmd export and the TS claude login probe instead of new copies; new CLI flags update autonomy/completions and the help-drift guard. v11.17.13 npm next 11.17.13 |
|
|
1026
1026
|
| TEN-COST-03 | Cost claim verdict: paired bootstrap CI and acceptance parity | benchmarks/bench/cost_claim.py (new: no bench module owns statistical claim gating; attribution.py is 1025 lines and owned by TEN-COST-04), benchmarks/bench/tests/test_cost_claim.py (new, pairs with the new module) | MEDIUM | see Wall in note | released@2026-10-17T02:57Z | merge 049a16327; TL APPROVE cab8238eb (8 passed, mutation 1 failed). wave 1; depends: none. Goal: A pure function decides whether a cost claim is PROVEN or NOT PROVEN. - Inputs: per-row results for a Loki arm and a floor arm. - Method: paired bootstrap over tasks (fixed seed) for the 95% CI of the cost-per-accepted ratio. - PROVEN_AT(target) only when all of these hold: - the CI upper bound is at most the target; - Loki's acceptance rate is at least the floor's minus 0 (no accepted-task loss); - no row is cost_partial; - engine and loki version are recorded and identical across rows; - n is at least 3 per task. - Otherwise NOT PROVEN, with every failing reason listed. - Unmeasured rows are excluded but counted in the reasons. Wall: Red first, fixtures: - Ratio about 0.4 with tight spread gives PROVEN at 0.5. - Ratio 0.45 with one cost_partial row gives NOT PROVEN, reason partial. - Loki accepts 22/24 vs floor 24/24 gives NOT PROVEN, reason acceptance. - Mixed loki versions give NOT PROVEN. - n=2 gives NOT PROVEN. - Same seed gives an identical CI. Mutation: replace the CI upper bound with the point estimate; the wide-spread fixture flips to PROVEN and goes red. Gate: python3 -I -m pytest benchmarks/bench/tests/test_cost_claim.py -q Rules: FAILURE-CLASSES row first for any bug fix (D86); no CHANGELOG edits (Release Manager writes notes); DECISIONS numbers assigned by the Chief of Staff; reuse util/secure_scan.ts, the shared badge helper, keys_cmd export and the TS claude login probe instead of new copies; new CLI flags update autonomy/completions and the help-drift guard. v11.17.13 npm next 11.17.13 |
|
|
1027
|
-
| TEN-COST-04 | C-F1 arms raw-lean and loki-lean, lever ablation, verdict in the report | benchmarks/bench/attribution.py, benchmarks/bench/adapters/loki.py, benchmarks/bench/tests/test_attribution.py | MEDIUM | see Wall in note |
|
|
1027
|
+
| TEN-COST-04 | C-F1 arms raw-lean and loki-lean, lever ablation, verdict in the report | benchmarks/bench/attribution.py, benchmarks/bench/adapters/loki.py, benchmarks/bench/tests/test_attribution.py | MEDIUM | see Wall in note | released v11.17.28 | wave 3; depends: TEN-COST-01, TEN-COST-02, TEN-COST-03, COST-WALL-e merged (it edits attribution.py). Goal: attribution.py changes: - Adds arms raw-lean (claude_code lean=True) and loki-lean (loki with LOKI_COST_PROFILE=lean). - Adds repeatable --loki-env KEY=VALUE for one-lever ablations (allowlist: LOKI_* keys only). - Adds raw-lean to COST_FLOORS. - render_cost_table prints the TEN-COST-03 verdict per Loki arm against each floor. adapters/loki.py changes: - Accepts profile and extra_env. - Records cost_profile, lever_env and cache_read/cache_creation tokens by phase in the row, so rows from different lever configs never pool. Wall: Red first in test_attribution.py: - --dry-run --arms raw-configured,raw-lean,loki,loki-lean plans all 4 arms. - loki-lean rows carry cost_profile 'lean'. - --loki-env FOO=1 (not LOKI_) exits 2. - The rendered table has a raw-lean column and a verdict line. - A loki row with a different lever_env is not pooled into the loki arm. Mutation: drop lever_env from the pooling key; the mixed-config test goes red. Gate: python3 -I -m pytest benchmarks -q -k 'attribution or model_pin or cost_claim' && python3 -I benchmarks/bench/attribution.py --dry-run --arms raw-configured,raw-lean,loki,loki-lean Rules: FAILURE-CLASSES row first for any bug fix (D86); no CHANGELOG edits (Release Manager writes notes); DECISIONS numbers assigned by the Chief of Staff; reuse util/secure_scan.ts, the shared badge helper, keys_cmd export and the TS claude login probe instead of new copies; new CLI flags update autonomy/completions and the help-drift guard. |
|
|
1028
1028
|
| TEN-COST-05 | Per-stage cache-hit attribution in the cost breakdown | loki-ts/src/util/cost_breakdown.ts, loki-ts/tests/engine10/cost.test.ts (breakdown cases only) | MEDIUM | see Wall in note | released@2026-10-17T03:50Z | TL APPROVE 71a31207f. wave 1; depends: none. Goal: costBreakdown per_stage gains cache_read_tokens, cache_creation_tokens and cache_hit_ratio, computed as read / (read + creation), null when both are 0. This makes the cache-stable-prefix lever (CH-01/CP-01) provable per stage in every receipt and in the bench row. The usd fail-closed rules are unchanged, and the new fields are additive. Wall: Red first: - Two implement records (read 7000/creation 0 and read 0/creation 7000) give ratio 0.5. - A stage with no token keys gives null, never 0. - Existing usd and retry assertions stay green. Mutation: count cache_creation as a hit; the 0.5 case goes red. Gate: cd loki-ts && bun test tests/engine10/cost.test.ts tests/engine10/budget.test.ts && bunx tsc --noEmit Rules: FAILURE-CLASSES row first for any bug fix (D86); no CHANGELOG edits (Release Manager writes notes); DECISIONS numbers assigned by the Chief of Staff; reuse util/secure_scan.ts, the shared badge helper, keys_cmd export and the TS claude login probe instead of new copies; new CLI flags update autonomy/completions and the help-drift guard. Shipped in npm 11.17.15 (latest+next) or 11.17.16 (Release run success on e3d445460). |
|
|
1029
1029
|
| TEN-COST-06 | xreview claude judge: lean flags and child-ledger line | loki-ts/src/engine10/stages/xreview.ts, loki-ts/tests/engine10/xreview.test.ts | MEDIUM | see Wall in note | released@2026-10-17T03:50Z | TL APPROVE 2e40a4be8, merged for v11.17.14 Shipped in npm 11.17.15 (latest+next) or 11.17.16 (Release run success on e3d445460). |
|
|
1030
1030
|
| TEN-COST-07 | Project-model discovery on the fast tier, retry on the run model | loki-ts/src/project_model/discover.ts, loki-ts/tests/project_model/project_model.test.ts | MEDIUM | see Wall in note | released@2026-10-17T03:50Z | TL APPROVE e01e37c5e. wave 1; depends: none. Goal: Discovery attempt 1 runs on fastTierModel(env) (runner/model_downgrades.ts:8). It is sonnet unless LOKI_ALLOW_HAIKU, so it never drops below sonnet without opt-in, and it is never weaker than an explicit LOKI_MODEL_OVERRIDE. The existing validation-error retry (attempt 2) runs on the run model, so a weak answer escalates, never ships. The model used is recorded on the project_model result. The schema validation and the owner=provider fallback are unchanged. Wall: Red first, with a stub SessionRunner: - Attempt 1 opts.model equals fastTierModel. - After an invalid answer, attempt 2 opts.model is undefined (run model). - With LOKI_MODEL_OVERRIDE set, both attempts use the override. Mutation: pin attempt 2 to the fast tier; the escalation test goes red. Gate: cd loki-ts && bun test tests/project_model && bunx tsc --noEmit Rules: FAILURE-CLASSES row first for any bug fix (D86); no CHANGELOG edits (Release Manager writes notes); DECISIONS numbers assigned by the Chief of Staff; reuse util/secure_scan.ts, the shared badge helper, keys_cmd export and the TS claude login probe instead of new copies; new CLI flags update autonomy/completions and the help-drift guard. Shipped in npm 11.17.15 (latest+next) or 11.17.16 (Release run success on e3d445460). |
|
|
@@ -1102,20 +1102,20 @@ Source: ~/git/autonomi-dev/research/2026-09-30-adoption/SWARM-PROMPT-ADOPTION.md
|
|
|
1102
1102
|
| TEN-SEC-GITLEAKS-01-F1 | local-ci gitleaks step uses --diff-merges=first-parent and joins the G1 comparison set; G1 reads the mode at any indentation | scripts/local-ci.sh, tests/test-security-audit-config.sh | LOW | dropping the flag from local-ci.sh turns G1 red; indented reassignment turns G1 red | building@2026-10-17T05:43Z | SEC-GITLEAKS-01 HIGH r2 non-blocking |
|
|
1103
1103
|
| TEN-UX-03-F1 | build_verified payload asserted to carry only headline and route | tests/test-first-run-funnel-coverage.sh | LOW | adding an extra arg to loki_emit_build_verified_enum turns the test red | building@2026-10-17T05:43Z | UX-03 TL review non-blocking 1 |
|
|
1104
1104
|
| TEN-RELEASE-FAST | one-command ship: scripts/release.sh --ship --notes FILE bumps, writes the CHANGELOG section, rebuilds dist, commits, checks ancestry and gitleaks, waits for an in-flight Release run, pushes and verifies origin/main | scripts/release.sh, tests/test-release-sh.sh (extend; it owns release.sh) | HIGH | --ship on a fixture repo with a fake origin completes all phases and prints per-phase timings; a non-ancestor origin, a gitleaks hit, a forbidden staged file and a CHANGELOG c[7] mismatch each abort before push (red mutation per guard) | building@2026-10-17T05:43Z | D103; target under 90s wall for the whole ship |
|
|
1105
|
-
| TEN-SEC-PREPUSH-MERGE-WARN | pre-push .gitleaksignore-growth warning diffs merges against the first parent only, so a merge of an older slice no longer reports lines main already had | .githooks/pre-push (line ~621 diff-tree -m), tests/test-security-audit-config.sh or the existing pre-push test that owns this warning; docs/v10/FAILURE-CLASSES.md (row first) | LOW | pushing a merge of a branch based before an ignore line was added prints no warning; a commit that really adds an ignore line still warns (red mutation: put -m back) |
|
|
1106
|
-
| TEN-COST-16-F1 | haiku-first downgrade row: router-override test, and the cascade override is reported only when cascadeDowngrade(run model) is non-null | loki-ts/src/runner/model_downgrades.ts, loki-ts/tests/engine10/session_l1_model.test.ts | LOW | router+haiku-first names the router override; cascade on a sonnet run reports the haiku-first row, matching implement.ts:106 precedence |
|
|
1105
|
+
| TEN-SEC-PREPUSH-MERGE-WARN | pre-push .gitleaksignore-growth warning diffs merges against the first parent only, so a merge of an older slice no longer reports lines main already had | .githooks/pre-push (line ~621 diff-tree -m), tests/test-security-audit-config.sh or the existing pre-push test that owns this warning; docs/v10/FAILURE-CLASSES.md (row first) | LOW | pushing a merge of a branch based before an ignore line was added prints no warning; a commit that really adds an ignore line still warns (red mutation: put -m back) | released v11.17.28 | v11.17.25 push printed 5 false warnings for merges c6d3894f1..0a16434e1 while git diff 7d4d0ffb2 HEAD -- .gitleaksignore was empty |
|
|
1106
|
+
| TEN-COST-16-F1 | haiku-first downgrade row: router-override test, and the cascade override is reported only when cascadeDowngrade(run model) is non-null | loki-ts/src/runner/model_downgrades.ts, loki-ts/tests/engine10/session_l1_model.test.ts | LOW | router+haiku-first names the router override; cascade on a sonnet run reports the haiku-first row, matching implement.ts:106 precedence | released v11.17.29 | COST-16 r2 TL non-blocking N2, N3 |
|
|
1107
1107
|
| TEN-COST-25 | fix: --cost lean stops forcing a plan session: drop LOKI_ROUTER from the lean profile (router makes plan.ts:65 plan every small task and overrides haiku-first) and add LOKI_IMPLEMENT_HAIKU_FIRST=1 plus the COST-26 sizing knobs | loki-ts/src/util/cost_profile.ts, loki-ts/tests/util/cost_profile.test.ts | MEDIUM | red: resolveCostProfile('lean') yields LOKI_ROUTER=1 and no LOKI_IMPLEMENT_HAIKU_FIRST; green: lean env has LOKI_IMPLEMENT_HAIKU_FIRST=1, no LOKI_ROUTER, and the COST-26 knobs; balanced/max unchanged | ready@2026-10-17T05:54Z | depends: TEN-COST-26, TEN-COST-16-F1. goal: cost-2x lever; C-F1 run4 small tasks were 1.415x raw from fixed plan+Wall cost (cf1-20261017T025219Z, plan_skipped=false on all 31) |
|
|
1108
|
-
| TEN-COST-26 | feature: sizing knobs LOKI_E10_SMALL_MAX_CHARS (default 600, unchanged) and opt-in LOKI_E10_SMALL_NO_RUNNER=1 so a missing test runner no longer forces normal (smallTaskPath still routes it to wall) | loki-ts/src/engine10/sizing.ts (at most 3 core lines), loki-ts/tests/engine10/sizing.test.ts | MEDIUM | red: a 700-char task with LOKI_E10_SMALL_MAX_CHARS=800 sizes normal, and a no-runner task with LOKI_E10_SMALL_NO_RUNNER=1 sizes normal; green: both small, defaults byte-identical |
|
|
1108
|
+
| TEN-COST-26 | feature: sizing knobs LOKI_E10_SMALL_MAX_CHARS (default 600, unchanged) and opt-in LOKI_E10_SMALL_NO_RUNNER=1 so a missing test runner no longer forces normal (smallTaskPath still routes it to wall) | loki-ts/src/engine10/sizing.ts (at most 3 core lines), loki-ts/tests/engine10/sizing.test.ts | MEDIUM | red: a 700-char task with LOKI_E10_SMALL_MAX_CHARS=800 sizes normal, and a no-runner task with LOKI_E10_SMALL_NO_RUNNER=1 sizes normal; green: both small, defaults byte-identical | released v11.17.29 | goal: C-F1 run4 sized all 31 tasks normal (19 'no test runner detected', 12 also >600 chars at 683/724); core budget 4937/5000 |
|
|
1109
1109
|
| TEN-COST-27 | enhancement: loki start reads cost.profile from loki.yaml as the team default; --cost flag and LOKI_COST_PROFILE env still win | loki-ts/src/commands/start.ts (cost flag resolution near line 279 only; not the help block DEPLOY rows own), loki-ts/tests/commands/start.test.ts | LOW | red: loki.yaml cost.profile: lean with no flag resolves balanced; green: resolves lean, flag and env override it, invalid value warns and falls back | ready@2026-10-17T05:54Z | depends: TEN-COST-25. goal: teams opt in to the lean lever once; reuse util/yaml_key.ts yamlKey |
|
|
1110
|
-
| TEN-SPEED-01 | feature: cost_claim reports a paired elapsed_s ratio (raw/loki) with the same bootstrap CI as the cost ratio | benchmarks/bench/cost_claim.py, benchmarks/bench/tests/test_cost_claim.py | LOW | red: cost_claim JSON has no time_ratio key for paired rows carrying elapsed_s; green: time_ratio and time_ci present, rows missing elapsed_s are excluded and counted |
|
|
1110
|
+
| TEN-SPEED-01 | feature: cost_claim reports a paired elapsed_s ratio (raw/loki) with the same bootstrap CI as the cost ratio | benchmarks/bench/cost_claim.py, benchmarks/bench/tests/test_cost_claim.py | LOW | red: cost_claim JSON has no time_ratio key for paired rows carrying elapsed_s; green: time_ratio and time_ci present, rows missing elapsed_s are excluded and counted | released v11.17.29 | goal: speed has no measured number today (cost_claim has no time metric; attribution.py:351-355 already writes elapsed_s); first SPEED row |
|
|
1111
1111
|
| TEN-SPEED-02 | enhancement: bench-suite times loki verify --fast and records p95 to migration_bench.jsonl | loki-ts/scripts/bench-suite.ts, loki-ts/tests/scripts/bench_suite_argv.test.ts | LOW | red: benchRouteArgv has no verify --fast route and no p95 field; green: argv test asserts the route and the record carries p95_ms | ready@2026-10-17T05:54Z | goal: METRICS lists verify --fast p95 as not measured; fills it with a repeatable command |
|
|
1112
|
-
| TEN-UX-09 | feature: loki control stop stops the detached control plane via instance.json PID plus a /health identity check, never by name | loki-ts/src/commands/control.ts, loki-ts/tests/commands/control.test.ts, loki-ts/src/cli/registry.ts (control row sub list only), completions/_loki and completions/loki.bash (control subcommands only) | MEDIUM | red: loki control stop is an unknown subcommand (control.ts:175-181 handles serve/status/prune/backfill); green: stop ends the recorded PID, refuses a PID whose /health does not match, exits 0 when nothing runs |
|
|
1112
|
+
| TEN-UX-09 | feature: loki control stop stops the detached control plane via instance.json PID plus a /health identity check, never by name | loki-ts/src/commands/control.ts, loki-ts/tests/commands/control.test.ts, loki-ts/src/cli/registry.ts (control row sub list only), completions/_loki and completions/loki.bash (control subcommands only) | MEDIUM | red: loki control stop is an unknown subcommand (control.ts:175-181 handles serve/status/prune/backfill); green: stop ends the recorded PID, refuses a PID whose /health does not match, exits 0 when nothing runs | released v11.17.29 | goal: bare loki leaves a nohup control serve with no stop (observed PID 50904 + server.js on 47821); reuse restartStaleControlPlane control.ts:52-70 |
|
|
1113
1113
|
| TEN-UX-10 | fix: bare loki prints next steps (give work with loki start "<brief>"; stop the UI with loki control stop) after the URL instead of the URL alone | autonomy/loki (_loki_control_ui only), tests/test-control-plane.sh | LOW | red: bare loki stdout is exactly the URL line; green: the URL line is first and both next-step lines follow; --json or non-TTY output unchanged if it has a contract | ready@2026-10-17T05:54Z | depends: TEN-UX-09. goal: first-run UX; today a new user sees only http://127.0.0.1:47821 and a background daemon |
|
|
1114
1114
|
| TEN-UX-11 | enhancement: the run summary Time line marks the first sealed PR in a repo (time to first sealed PR) so METRICS row 14 can be read from run output | loki-ts/src/engine10/output.ts (Time line input field only), loki-ts/tests/engine10/output.test.ts | LOW | red: summary with firstSealed=true renders the same Time line as any run; green: Time line appends 'first sealed PR in this repo' only when set, golden for other runs unchanged | ready@2026-10-17T05:54Z | goal: time to first sealed PR is not measured (METRICS row 14); caller wiring is a follow-up row |
|
|
1115
1115
|
| TEN-UX-12 | fix: the preflight 'could not confirm the Claude login' warning never adds a stdout line in quiet mode (stderr only) | loki-ts/src/engine10/preflight.ts (warning emit at line ~28 only), loki-ts/tests/engine10/preflight.test.ts | LOW | red: quiet run with an unanswered auth status prints 9 stdout lines (e2e.test.ts:130 A-130 limit 8); green: warning on stderr, quiet stdout within 8 lines | ready@2026-10-17T05:54Z | depends: TEN-P9-FIX-F1 (owns preflight.test.ts). goal: quiet-mode contract and a likely cause of the red main Tests runs |
|
|
1116
1116
|
| TEN-ADOPTION-09 | feature: engine10 pr stage routes a gitlab.com pinned origin to engine10-push-gitlab.sh push-mr and accepts the MR URL | loki-ts/src/engine10/stages/pr.ts, loki-ts/tests/engine10/pr.test.ts | HIGH | red: gitlab origin calls DEFAULT_PUSH_SH engine10-push.sh (pr.ts:42) and an MR URL fails PR_URL_RE (pr.ts:72); green: push-mr is invoked and the MR URL is recorded; github path byte-identical | ready@2026-10-17T05:54Z | goal: GitLab teams; the script and tests/test-engine10-push-gitlab.sh exist but nothing in loki-ts/src references it |
|
|
1117
1117
|
| TEN-ADOPTION-10 | fix: reviewer instructions name the receipt-check action with a copyable uses: line pinned to the current version instead of 'adds the receipt-check action' | loki-ts/src/e10ext/reviewer_body.ts, loki-ts/tests/engine10/pr_body_golden.test.ts | LOW | red: reviewer body line 54 has no uses: reference; green: body contains uses: <repo>/.github/actions/receipt-check@v<VERSION> and the golden is updated | ready@2026-10-17T05:54Z | goal: .github/actions/receipt-check exists (tests/test-receipt-check-action.sh) but no user-facing text tells a reviewer how to add it |
|
|
1118
|
-
| TEN-GATES-11 | feature: build autonomy/lib/ablation.py to the tests/moat/p8-load-bearing-proof.sh contract and promote the 3 P8 ablation lines in tests/moat/pending.txt | autonomy/lib/ablation.py (new: the p8 header names this exact path and no module owns it), tests/test_ablation.py (new: the new module's own test), tests/moat/pending.txt (3 P8 lines only) | HIGH | red: bash tests/moat/p8-load-bearing-proof.sh fails, ablation.py missing; green: P8.ablation-runtime-change-load-bearing, P8.ablation-dead-code-not-sealed, P8.ablation-na-docs-only pass and leave pending.txt; restore is byte-for-byte |
|
|
1118
|
+
| TEN-GATES-11 | feature: build autonomy/lib/ablation.py to the tests/moat/p8-load-bearing-proof.sh contract and promote the 3 P8 ablation lines in tests/moat/pending.txt | autonomy/lib/ablation.py (new: the p8 header names this exact path and no module owns it), tests/test_ablation.py (new: the new module's own test), tests/moat/pending.txt (3 P8 lines only) | HIGH | red: bash tests/moat/p8-load-bearing-proof.sh fails, ablation.py missing; green: P8.ablation-runtime-change-load-bearing, P8.ablation-dead-code-not-sealed, P8.ablation-na-docs-only pass and leave pending.txt; restore is byte-for-byte | released v11.17.29 | goal: moat 2/9 proven; consumer already built (proof-generator.py:874 _collect_ablation, :2052 headline refusal), producer missing |
|
|
1119
1119
|
| TEN-GATES-12 | enhancement: loki start proof path runs ablation.py before proof-generator so quality/ablation.json exists on engine10 runs | loki-ts/src/runner/proof.ts (pre-proof hook only), loki-ts/tests/runner/proof.test.ts | HIGH | red: an engine10 run with a load-bearing change leaves no quality/ablation.json; green: file present with status load_bearing, ablation failure is recorded not_applicable and never blocks the proof | ready@2026-10-17T05:54Z | depends: TEN-GATES-07, TEN-GATES-11. goal: the P8 property reaches real runs, not just the moat script |
|
|
1120
1120
|
| TEN-GATES-13 | feature: seeded-defect corpus at benchmarks/seeded-defects/manifest.json (schema loki.seeded-defects/v2, >=100 defects, generated) and promote P4.seeded-defect-corpus-present | benchmarks/seeded-defects/ (new dir: the p4 contract names this path), tests/moat/pending.txt (1 P4 line only) | HIGH | red: P4.seeded-defect-corpus-present fails, directory absent; green: manifest validates, count >=100, generator rerun is byte-identical, pending line removed | ready@2026-10-17T05:54Z | depends: TEN-GATES-11 (shared pending.txt, one line each). goal: Seal accuracy cannot be measured without the corpus |
|
|
1121
1121
|
| TEN-DEPLOY-10 | feature: loki status --probe re-probes the last deploy health URL and exits non-zero on regression | loki-ts/src/commands/status.ts (renderLastDeploy region only), loki-ts/tests/commands/status.test.ts | MEDIUM | red: --probe is an unknown flag; green: healthy URL exits 0, a 500 or timeout exits 1 with the URL and status, no last deploy exits 0 with a note | ready@2026-10-17T05:54Z | goal: post-deploy watch; deploy health is recorded once and never rechecked |
|
package/docs/v10/CLI-MODERN.md
CHANGED
|
@@ -11,7 +11,7 @@ Classes: KEEP-MODERN (v10-native or engine-neutral), UPDATE (live but needs mode
|
|
|
11
11
|
- DROP-LEGACY: 31
|
|
12
12
|
- DELETE: 14
|
|
13
13
|
- Total rows: 141
|
|
14
|
-
- Registry paths (commands and nested subcommands):
|
|
14
|
+
- Registry paths (commands and nested subcommands): 202
|
|
15
15
|
|
|
16
16
|
## Commands
|
|
17
17
|
|
|
@@ -1019,3 +1019,10 @@ L0 review rule (ENGINE-LAWS.md L0): a fix that adds an `if` or a regex about the
|
|
|
1019
1019
|
- Siblings: doctor.ts provider probe at the json route (already tokenFreeEnv); modernize/oracle/search.ts `--version` probe (already tokenFreeEnv); engine10 session spawns (worker env, assertWorkerEnv).
|
|
1020
1020
|
- Mechanism: doctor.ts claudeAuthStatusLoggedIn, the one shared login probe, spawns with tokenFreeEnv plus GIT_SSH_COMMAND=false unless LOKI_ALLOW_AGENT_GITHUB_TOKEN=1.
|
|
1021
1021
|
- Fixture: tests/engine10/preflight.test.ts "the login probe spawns the provider CLI without the GitHub token or SSH agent"; tests/moat/p9-rule-of-two.sh against a dist rebuilt from the tree.
|
|
1022
|
+
|
|
1023
|
+
## FC-TEN-SEC-PREPUSH-MERGE-WARN Pre-push warns about .gitleaksignore lines a merge did not add (MERGE-SECOND-PARENT-DIFF)
|
|
1024
|
+
- User-visible vs raw: the v11.17.25 push printed 5 false ".gitleaksignore gained line(s)" warnings for merges c6d3894f1..0a16434e1 while the net .gitleaksignore diff of the push was empty; the raw cause was `diff-tree -m`, which also diffs a merge against its second parent (the older slice branch), so every line main gained since the fork looked added by the merge.
|
|
1025
|
+
- Law broken: a warning names what a commit changed relative to what it was merged onto; it never reports lines the base already had.
|
|
1026
|
+
- Siblings: the .gitleaks.toml name-only and patch diff-tree calls in .githooks/pre-push (same -m, fixed here; they could even FAIL the push on a toml main already had); the eval-fixture scan diff-tree -m in the same hook (kept: scanning both parents is conservative and fail-closed); scripts/security-audit-gitleaks.sh (comment only) and scripts/train-cycle.sh (plain non-merge name list).
|
|
1027
|
+
- Mechanism: the per-commit drift check in .githooks/pre-push passes `--diff-merges=first-parent` instead of `-m`; a merge that adds a line itself still differs from its first parent, and each branch commit is its own entry in the pushed list.
|
|
1028
|
+
- Fixture: tests/test-pre-push-gitleaks.sh case 19b (older-slice merge prints no warning; merge adding a line still warns); red with `-m` restored on all three calls, and red with the flag dropped from the ignore call.
|
package/docs/v10/PROGRESS.md
CHANGED
|
@@ -2461,3 +2461,9 @@ v11.17.21 published (Release success on 462fbc40a; npm latest 11.17.21, next 11.
|
|
|
2461
2461
|
|
|
2462
2462
|
## 2026-10-17T06:02Z (CoS) v11.17.27
|
|
2463
2463
|
- Ships TEN-COST-11 (HIGH r4 APPROVE 4014f37ee: r3 blockers N1 and N2 closed, reviewer reproduced red mutations for each; bun test wall, seal, machine, spawn_env_guard, budget, discard_proto 225 pass 0 fail) and the 20 Architect BOARD rows (871a2ef35). v11.17.25 Release run 39007241890 success, npm latest=next=11.17.25. Non-blocking follow-up: machine.ts distrust call has no direct test (pre-existing from WC-01b).
|
|
2464
|
+
|
|
2465
|
+
## 2026-10-17T06:08Z (CoS) v11.17.28
|
|
2466
|
+
- v11.17.26 Release run 39008106373 success, npm latest=next=11.17.26. v11.17.27 pushed e044e5395 (HEAD equals origin/main, gitleaks no leaks). Ships TEN-COST-04 (TL APPROVE b6562f67d: pytest 153 passed, mutation M1 3 failed) and TEN-SEC-PREPUSH-MERGE-WARN (TL APPROVE 78f48de3a: tests/test-pre-push-gitleaks.sh Passed 59 Failed 0, mutation case 19b red). TEN-COST-16-F1 built nothing (row uncommitted at dispatch); re-dispatched in wave Z with COST-26, SPEED-01, UX-09, GATES-11.
|
|
2467
|
+
|
|
2468
|
+
## 2026-10-17T06:16Z (CoS) v11.17.29
|
|
2469
|
+
- v11.17.28 pushed 5f504d333 without waiting for the v11.17.27 Release run (release.yml has no concurrency group; publish has a never-backwards guard). Wave Z all APPROVE: COST-16-F1 d9b5917fd (47 pass, 2 mutations red), COST-26 ddaf2389a (30 pass, 2 mutations red), SPEED-01 a643883d7 (pytest 24 passed, inverted ratio 6 failed), UX-09 b80656d3e (64 pass, 2 mutations red), GATES-11 c7cc6a31b HIGH (p8 moat rc=0 3 of 3 PASS, pytest 23 passed, 5 mutations red). COST-26 hand-merge: test-only conflict in sizing.test.ts resolved as union, bun test sizing and wall 94 pass 0 fail. Moat P8 moved from pending to proven.
|