loki-mode 7.128.2 → 7.129.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/council-v2.sh +41 -1
- package/autonomy/lib/cr-rematerialize.py +120 -0
- package/autonomy/lib/done-recognition.sh +48 -0
- package/autonomy/lib/voter-agents.sh +13 -3
- package/autonomy/loki +3 -3
- package/autonomy/run.sh +51 -2
- package/autonomy/verify.sh +38 -8
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +2 -2
- package/docs/RARV-C-100X-PLAN.md +278 -0
- package/docs/RARV-C-CHANGE-MAP.md +5 -0
- package/docs/RARV-C-LOOP-EFFICIENCY-PLAN.md +5 -0
- package/loki-ts/data/code-review-schema.json +39 -0
- package/loki-ts/data/council-v2-schema.json +45 -0
- package/loki-ts/data/done-recognition-schema.json +51 -0
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.
|
|
6
|
+
# Loki Mode v7.129.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
408
408
|
|
|
409
409
|
---
|
|
410
410
|
|
|
411
|
-
**v7.
|
|
411
|
+
**v7.129.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.129.0
|
package/autonomy/council-v2.sh
CHANGED
|
@@ -296,7 +296,47 @@ Respond ONLY with a valid JSON object. No markdown fencing."
|
|
|
296
296
|
# inline prefix here is belt-and-suspenders so the carve-out is
|
|
297
297
|
# self-documenting and robust to sourcing order. No-op when caveman
|
|
298
298
|
# is absent.
|
|
299
|
-
|
|
299
|
+
#
|
|
300
|
+
# STRUCTURED VERDICT (v8.x): when the CLI supports --json-schema,
|
|
301
|
+
# force valid JSON so the sed-carve below never has to rescue
|
|
302
|
+
# prose-wrapped output. --json-schema takes INLINE content, not a
|
|
303
|
+
# path (CLI 2.1.207 rejects a path). On any failure fall through to
|
|
304
|
+
# the plain -p call + sed-carve (unchanged, fail-closed to REJECT).
|
|
305
|
+
# Opt out LOKI_REVIEW_JSON_SCHEMA=off. claude branch only.
|
|
306
|
+
local _c2_schema_dir _c2_schema
|
|
307
|
+
_c2_schema_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
308
|
+
_c2_schema="${_c2_schema_dir}/loki-ts/data/council-v2-schema.json"
|
|
309
|
+
result=""
|
|
310
|
+
if [ "${LOKI_REVIEW_JSON_SCHEMA:-on}" != "off" ] && [ -f "$_c2_schema" ] \
|
|
311
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
312
|
+
&& loki_claude_flag_supported "--json-schema"; then
|
|
313
|
+
local _c2_schema_content _c2_json
|
|
314
|
+
_c2_schema_content="$(cat "$_c2_schema" 2>/dev/null)" || _c2_schema_content=""
|
|
315
|
+
if [ -n "$_c2_schema_content" ]; then
|
|
316
|
+
_c2_json="$(echo "$full_prompt" | CAVEMAN_DEFAULT_MODE=off claude "${_c2_argv[@]}" -p \
|
|
317
|
+
--json-schema "$_c2_schema_content" --output-format json 2>/dev/null)" || _c2_json=""
|
|
318
|
+
if [ -n "$_c2_json" ]; then
|
|
319
|
+
# Pull the schema-validated object out of the envelope.
|
|
320
|
+
result="$(printf '%s' "$_c2_json" | python3 -c 'import sys,json
|
|
321
|
+
try:
|
|
322
|
+
e=json.load(sys.stdin)
|
|
323
|
+
p=e.get("structured_output")
|
|
324
|
+
if not isinstance(p,dict):
|
|
325
|
+
r=e.get("result")
|
|
326
|
+
p=json.loads(r) if isinstance(r,str) else None
|
|
327
|
+
if isinstance(p,dict) and str(p.get("verdict","")).upper() in ("APPROVE","REJECT"):
|
|
328
|
+
sys.stdout.write(json.dumps(p))
|
|
329
|
+
else:
|
|
330
|
+
sys.exit(1)
|
|
331
|
+
except Exception:
|
|
332
|
+
sys.exit(1)' 2>/dev/null)" || result=""
|
|
333
|
+
fi
|
|
334
|
+
fi
|
|
335
|
+
fi
|
|
336
|
+
# Fall through to the plain text call (+ sed-carve) on any miss.
|
|
337
|
+
if [ -z "$result" ]; then
|
|
338
|
+
result=$(echo "$full_prompt" | CAVEMAN_DEFAULT_MODE=off claude "${_c2_argv[@]}" -p 2>/dev/null || echo '{"verdict":"REJECT","reasoning":"review execution failed","issues":[]}')
|
|
339
|
+
fi
|
|
300
340
|
else
|
|
301
341
|
result='{"verdict":"REJECT","reasoning":"reviewer CLI unavailable","issues":[]}'
|
|
302
342
|
fi
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Re-materialize a `claude --json-schema --output-format json` code-review
|
|
3
|
+
envelope into the LEGACY 'VERDICT: X\\nFINDINGS:\\n- [severity] description'
|
|
4
|
+
text, so the downstream text parsers (_classify_verdict, _severity_is_blocking,
|
|
5
|
+
_count_nonblocking_findings in autonomy/run.sh, and the TS mirror) stay
|
|
6
|
+
byte-identical. This is the adapter that lets the code-review verdict path adopt
|
|
7
|
+
structured output WITHOUT rewriting any consumer.
|
|
8
|
+
|
|
9
|
+
Contract:
|
|
10
|
+
- Reads the raw CLI envelope JSON from env _LOKI_CR_JSON (or stdin if unset).
|
|
11
|
+
- Writes legacy text to the path in env _LOKI_CR_OUT (or stdout if unset).
|
|
12
|
+
- FAIL-CLOSED: exits non-zero on ANY structural miss so the bash caller falls
|
|
13
|
+
through to the text path. NEVER emits 'VERDICT: PASS' on a miss.
|
|
14
|
+
- T1 SAFETY (cross-field enforcement JSON Schema cannot express): if ANY finding
|
|
15
|
+
is Critical or High, the verdict is FORCED to FAIL regardless of the model's
|
|
16
|
+
emitted verdict token. A self-contradictory PASS+Critical can never be waved
|
|
17
|
+
through as non-blocking.
|
|
18
|
+
|
|
19
|
+
Live-verified envelope shape (claude 2.1.207): top-level 'structured_output'
|
|
20
|
+
object is the payload; 'result' is the same JSON as a string (fallback);
|
|
21
|
+
'stop_reason' is 'tool_use' for a valid structured turn.
|
|
22
|
+
|
|
23
|
+
Exit codes (all non-zero == fail-closed, caller falls through to text):
|
|
24
|
+
0 success 2 no input/output target 3 envelope not JSON / not a dict
|
|
25
|
+
4 wrong stop_reason 5 no usable payload 6 missing/invalid verdict
|
|
26
|
+
7 write error
|
|
27
|
+
"""
|
|
28
|
+
import json
|
|
29
|
+
import os
|
|
30
|
+
import sys
|
|
31
|
+
|
|
32
|
+
_BLOCKING = {"CRITICAL", "HIGH"}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def rematerialize(raw):
|
|
36
|
+
"""Return legacy VERDICT/FINDINGS text, or raise ValueError(exit_code)."""
|
|
37
|
+
try:
|
|
38
|
+
env = json.loads(raw)
|
|
39
|
+
except Exception:
|
|
40
|
+
raise ValueError(3)
|
|
41
|
+
if not isinstance(env, dict):
|
|
42
|
+
raise ValueError(3)
|
|
43
|
+
|
|
44
|
+
# A valid structured turn ends with tool_use. A refusal / max_tokens / other
|
|
45
|
+
# stop_reason means the schema was not honored -> fail-closed.
|
|
46
|
+
if env.get("stop_reason") not in (None, "tool_use"):
|
|
47
|
+
raise ValueError(4)
|
|
48
|
+
|
|
49
|
+
payload = env.get("structured_output")
|
|
50
|
+
if not isinstance(payload, dict):
|
|
51
|
+
res = env.get("result")
|
|
52
|
+
if isinstance(res, str):
|
|
53
|
+
try:
|
|
54
|
+
payload = json.loads(res)
|
|
55
|
+
except Exception:
|
|
56
|
+
payload = None
|
|
57
|
+
if not isinstance(payload, dict):
|
|
58
|
+
raise ValueError(5)
|
|
59
|
+
|
|
60
|
+
verdict = str(payload.get("verdict", "")).strip().upper()
|
|
61
|
+
if verdict not in ("PASS", "FAIL"):
|
|
62
|
+
raise ValueError(6)
|
|
63
|
+
|
|
64
|
+
findings = payload.get("findings")
|
|
65
|
+
if not isinstance(findings, list):
|
|
66
|
+
findings = []
|
|
67
|
+
|
|
68
|
+
body = []
|
|
69
|
+
has_blocking = False
|
|
70
|
+
for f in findings:
|
|
71
|
+
if not isinstance(f, dict):
|
|
72
|
+
continue
|
|
73
|
+
sev = str(f.get("severity", "")).strip()
|
|
74
|
+
desc = str(f.get("description", "")).strip()
|
|
75
|
+
if not sev or not desc:
|
|
76
|
+
continue
|
|
77
|
+
if sev.upper() in _BLOCKING:
|
|
78
|
+
has_blocking = True
|
|
79
|
+
# Bracketed severity form matches _severity_is_blocking /
|
|
80
|
+
# _count_nonblocking_findings regexes exactly.
|
|
81
|
+
body.append("- [" + sev + "] " + desc)
|
|
82
|
+
|
|
83
|
+
# T1: a Critical/High finding forces FAIL even if the model said PASS.
|
|
84
|
+
if has_blocking:
|
|
85
|
+
verdict = "FAIL"
|
|
86
|
+
|
|
87
|
+
lines = ["VERDICT: " + verdict, "FINDINGS:"]
|
|
88
|
+
if body:
|
|
89
|
+
lines.extend(body)
|
|
90
|
+
else:
|
|
91
|
+
lines.append("- None")
|
|
92
|
+
return "\n".join(lines) + "\n"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main():
|
|
96
|
+
raw = os.environ.get("_LOKI_CR_JSON")
|
|
97
|
+
if raw is None:
|
|
98
|
+
raw = sys.stdin.read()
|
|
99
|
+
out_path = os.environ.get("_LOKI_CR_OUT", "")
|
|
100
|
+
if not raw:
|
|
101
|
+
return 2
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
text = rematerialize(raw)
|
|
105
|
+
except ValueError as e:
|
|
106
|
+
return int(e.args[0])
|
|
107
|
+
|
|
108
|
+
if out_path:
|
|
109
|
+
try:
|
|
110
|
+
with open(out_path, "w") as fp:
|
|
111
|
+
fp.write(text)
|
|
112
|
+
except OSError:
|
|
113
|
+
return 7
|
|
114
|
+
else:
|
|
115
|
+
sys.stdout.write(text)
|
|
116
|
+
return 0
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
sys.exit(main())
|
|
@@ -51,6 +51,54 @@
|
|
|
51
51
|
_loki_done_recog_invoke() {
|
|
52
52
|
local prompt="$1"
|
|
53
53
|
command -v claude >/dev/null 2>&1 || return 1
|
|
54
|
+
|
|
55
|
+
# STRUCTURED VERDICT (v8.x): when the CLI supports --json-schema, force valid
|
|
56
|
+
# JSON so parse_object never has to brace-slice prose. --json-schema takes
|
|
57
|
+
# INLINE content, not a path (CLI 2.1.207 rejects a path). On any miss (flag
|
|
58
|
+
# unsupported, empty, no structured_output) fall through to the plain -p call
|
|
59
|
+
# below; parse_object then handles it and defaults to inconclusive (safe).
|
|
60
|
+
# Opt out LOKI_REVIEW_JSON_SCHEMA=off.
|
|
61
|
+
local _dr_schema_dir _dr_schema
|
|
62
|
+
_dr_schema_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
63
|
+
_dr_schema="${_dr_schema_dir}/loki-ts/data/done-recognition-schema.json"
|
|
64
|
+
if [ "${LOKI_REVIEW_JSON_SCHEMA:-on}" != "off" ] && [ -f "$_dr_schema" ] \
|
|
65
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
66
|
+
&& loki_claude_flag_supported "--json-schema"; then
|
|
67
|
+
local _dr_schema_content _dr_json _dr_obj
|
|
68
|
+
_dr_schema_content="$(cat "$_dr_schema" 2>/dev/null)" || _dr_schema_content=""
|
|
69
|
+
if [ -n "$_dr_schema_content" ]; then
|
|
70
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
71
|
+
_dr_json=$(CAVEMAN_DEFAULT_MODE=off timeout "${LOKI_DONE_RECOG_TIMEOUT}" \
|
|
72
|
+
claude --dangerously-skip-permissions -p "$prompt" \
|
|
73
|
+
--json-schema "$_dr_schema_content" --output-format json 2>/dev/null) || _dr_json=""
|
|
74
|
+
else
|
|
75
|
+
_dr_json=$(CAVEMAN_DEFAULT_MODE=off \
|
|
76
|
+
claude --dangerously-skip-permissions -p "$prompt" \
|
|
77
|
+
--json-schema "$_dr_schema_content" --output-format json 2>/dev/null) || _dr_json=""
|
|
78
|
+
fi
|
|
79
|
+
if [ -n "$_dr_json" ]; then
|
|
80
|
+
_dr_obj=$(printf '%s' "$_dr_json" | python3 -c 'import sys,json
|
|
81
|
+
try:
|
|
82
|
+
e=json.load(sys.stdin)
|
|
83
|
+
p=e.get("structured_output")
|
|
84
|
+
if not isinstance(p,dict):
|
|
85
|
+
r=e.get("result")
|
|
86
|
+
p=json.loads(r) if isinstance(r,str) else None
|
|
87
|
+
if isinstance(p,dict) and "requirements" in p:
|
|
88
|
+
sys.stdout.write(json.dumps(p))
|
|
89
|
+
else:
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
except Exception:
|
|
92
|
+
sys.exit(1)' 2>/dev/null) || _dr_obj=""
|
|
93
|
+
if [ -n "$_dr_obj" ]; then
|
|
94
|
+
printf '%s' "$_dr_obj"
|
|
95
|
+
return 0
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
fi
|
|
99
|
+
# fall through to the plain text call (parse_object handles it, inconclusive-safe)
|
|
100
|
+
fi
|
|
101
|
+
|
|
54
102
|
local rc=0
|
|
55
103
|
local out=""
|
|
56
104
|
if command -v timeout >/dev/null 2>&1; then
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# autonomy/lib/voter-agents.sh -- Phase C (v7.5.20) bash side.
|
|
3
3
|
#
|
|
4
4
|
# Builds the JSON declaration of the council voter agents and orchestrates a
|
|
5
|
-
# single `claude --agents <json> --json-schema <
|
|
5
|
+
# single `claude --agents <json> --json-schema <inline-content>` dispatch per iteration.
|
|
6
6
|
# Replaces the ad-hoc per-voter heuristic loop in council_aggregate_votes when
|
|
7
7
|
# the locally installed Claude CLI supports both flags.
|
|
8
8
|
#
|
|
@@ -176,7 +176,7 @@ loki_finding_schema_path() {
|
|
|
176
176
|
# --json-schema in `claude --help`. Either missing -> return 1 (fallback).
|
|
177
177
|
# 2. Build the agents JSON via loki_voter_agents_json.
|
|
178
178
|
# 3. Invoke `claude --dangerously-skip-permissions -p <prompt>
|
|
179
|
-
# --agents <json> --json-schema <
|
|
179
|
+
# --agents <json> --json-schema <inline-content>` and capture stdout.
|
|
180
180
|
# Any non-zero exit or empty stdout -> return 1 (fallback).
|
|
181
181
|
# 4. Parse the response via python3 (no jq dependency): extract
|
|
182
182
|
# findings[].{role, vote, reason, confidence}. On parse failure -> 1.
|
|
@@ -236,6 +236,16 @@ loki_council_dispatch_agents() {
|
|
|
236
236
|
local schema_path
|
|
237
237
|
schema_path=$(loki_finding_schema_path) || return 1
|
|
238
238
|
|
|
239
|
+
# v8.x FIX: `claude --json-schema` takes the schema JSON as INLINE CONTENT,
|
|
240
|
+
# not a file path. Live-verified on CLI 2.1.207: passing a path errors with
|
|
241
|
+
# "--json-schema is not valid JSON: Unrecognized token '/'" -> rc!=0 ->
|
|
242
|
+
# fail-closed to the heuristic path, so this structured dispatch was SILENTLY
|
|
243
|
+
# DEAD (never actually schema-constrained the council). Read the file and pass
|
|
244
|
+
# its content. Fail-closed if unreadable/empty (return 1 -> heuristic path).
|
|
245
|
+
local schema_content
|
|
246
|
+
schema_content=$(cat "$schema_path" 2>/dev/null) || return 1
|
|
247
|
+
[ -n "$schema_content" ] || return 1
|
|
248
|
+
|
|
239
249
|
# 3. Invoke claude. Guard against absent binary or non-zero exit.
|
|
240
250
|
command -v claude >/dev/null 2>&1 || return 1
|
|
241
251
|
|
|
@@ -274,7 +284,7 @@ loki_council_dispatch_agents() {
|
|
|
274
284
|
env CAVEMAN_DEFAULT_MODE=off claude --dangerously-skip-permissions \
|
|
275
285
|
-p "$prompt" \
|
|
276
286
|
--agents "$agents_json" \
|
|
277
|
-
--json-schema "$
|
|
287
|
+
--json-schema "$schema_content" 2>"$stderr_log") || rc=$?
|
|
278
288
|
if [ "$rc" -ne 0 ] || [ -z "$response" ]; then
|
|
279
289
|
return 1
|
|
280
290
|
fi
|
package/autonomy/loki
CHANGED
|
@@ -826,7 +826,7 @@ show_help() {
|
|
|
826
826
|
echo "New here? Try these in order:"
|
|
827
827
|
echo " loki doctor Check your setup is ready (a few seconds)"
|
|
828
828
|
echo " loki quickstart Guided first build from your idea (no PRD needed)"
|
|
829
|
-
echo " loki start ./prd.md Build from a spec (PRD file, GitHub issue, or no arg)"
|
|
829
|
+
echo " loki start ./prd.md Build from a spec (PRD file, GitHub/GitLab/Jira/Azure issue, or no arg)"
|
|
830
830
|
echo " quickstart = guided first build; quick = one small task (3 iters max)."
|
|
831
831
|
echo " Docs: https://github.com/asklokesh/loki-mode | Report a problem: loki crash"
|
|
832
832
|
echo ""
|
|
@@ -846,7 +846,7 @@ show_help() {
|
|
|
846
846
|
echo "Commands:"
|
|
847
847
|
echo ""
|
|
848
848
|
echo "Build:"
|
|
849
|
-
echo " start [SPEC] Start a build (PRD file, GitHub issue, or no arg)"
|
|
849
|
+
echo " start [SPEC] Start a build (PRD file, GitHub/GitLab/Jira/Azure issue, or no arg)"
|
|
850
850
|
echo " plan <PRD-file> Dry-run analysis: complexity, cost, execution plan"
|
|
851
851
|
echo " quickstart [idea] Guided first build: setup, idea, template, plan, go"
|
|
852
852
|
echo ""
|
|
@@ -1043,7 +1043,7 @@ show_landing() {
|
|
|
1043
1043
|
echo -e " ${CYAN}loki tour${NC} See a real result now (no provider, no key, no spend)"
|
|
1044
1044
|
echo -e " ${CYAN}loki doctor${NC} Check your setup is ready (a few seconds)"
|
|
1045
1045
|
echo -e " ${CYAN}loki quickstart${NC} Guided first build from your idea (no PRD needed)"
|
|
1046
|
-
echo -e " ${CYAN}loki start ./prd.md${NC} Build from a spec (PRD file, GitHub issue, or no arg)"
|
|
1046
|
+
echo -e " ${CYAN}loki start ./prd.md${NC} Build from a spec (PRD file, GitHub/GitLab/Jira/Azure issue, or no arg)"
|
|
1047
1047
|
echo " quickstart = guided first build; quick = one small task (3 iters max)."
|
|
1048
1048
|
echo ""
|
|
1049
1049
|
echo -e "Need help? ${CYAN}loki help${NC} lists every command."
|
package/autonomy/run.sh
CHANGED
|
@@ -5911,8 +5911,15 @@ compute_codebase_signature() {
|
|
|
5911
5911
|
# truncating the output and misaligning the path<->hash pairing. Drop
|
|
5912
5912
|
# them; their removal from the list is itself the detected change.
|
|
5913
5913
|
gitc_deleted=$(git ls-files --deleted -z 2>/dev/null | tr '\0' '\n')
|
|
5914
|
+
# Exclude .loki/ AND Loki's own session-end artifacts (HANDOFF.md,
|
|
5915
|
+
# USAGE.md) written+committed to the repo ROOT by the completion path
|
|
5916
|
+
# (LOKI_HANDOFF / commit_session_changes, both default-on). Those are
|
|
5917
|
+
# runtime OUTPUT of the session, not user codebase; counting them would
|
|
5918
|
+
# make the next PRD-reuse check see a spurious "codebase changed" and
|
|
5919
|
+
# flip reuse->update (the signature is persisted per-iteration BEFORE
|
|
5920
|
+
# the after-loop HANDOFF write, so it can never account for them).
|
|
5914
5921
|
gitc_paths=$( { git ls-files -z 2>/dev/null; git ls-files --others --exclude-standard -z 2>/dev/null; } \
|
|
5915
|
-
| tr '\0' '\n' | grep -vE '(^|/)\.loki(/|$)' | LC_ALL=C sort -u )
|
|
5922
|
+
| tr '\0' '\n' | grep -vE '(^|/)\.loki(/|$)|^HANDOFF\.md$|^USAGE\.md$' | LC_ALL=C sort -u )
|
|
5916
5923
|
if [ -n "$gitc_deleted" ]; then
|
|
5917
5924
|
gitc_paths=$(printf '%s\n' "$gitc_paths" | grep -vxF -f <(printf '%s\n' "$gitc_deleted") || true)
|
|
5918
5925
|
fi
|
|
@@ -5933,6 +5940,7 @@ compute_codebase_signature() {
|
|
|
5933
5940
|
-o -name build -o -name .next -o -name target -o -name vendor \
|
|
5934
5941
|
-o -name __pycache__ -o -name .venv -o -name venv \) -prune -o \
|
|
5935
5942
|
-type f -print 2>/dev/null \
|
|
5943
|
+
| grep -vE '^\./(HANDOFF|USAGE)\.md$' \
|
|
5936
5944
|
| while IFS= read -r f; do
|
|
5937
5945
|
local sz
|
|
5938
5946
|
sz=$(stat -f%z "$f" 2>/dev/null || stat -c%s "$f" 2>/dev/null || echo 0)
|
|
@@ -9658,8 +9666,16 @@ sys.stdout.write(t.strip())
|
|
|
9658
9666
|
local output
|
|
9659
9667
|
# Pass matched files explicitly (node --test globbing is
|
|
9660
9668
|
# Node-version-sensitive); quote each so paths with spaces survive.
|
|
9669
|
+
# Force --test-reporter=tap: Node 20+ made the human "spec" reporter
|
|
9670
|
+
# the default for a TTY and Node 26 emits it even under capture
|
|
9671
|
+
# ("i pass N" / check+cross marks) instead of the TAP "# pass N" /
|
|
9672
|
+
# "ok N - " lines that BOTH the count parser below AND
|
|
9673
|
+
# _loki_zero_tests_executed rely on. TAP has been stable since Node 18,
|
|
9674
|
+
# so forcing it restores a deterministic, version-independent format
|
|
9675
|
+
# without touching any parser. Older node ignores nothing here (tap is
|
|
9676
|
+
# a valid built-in reporter on every supported version).
|
|
9661
9677
|
output=$(cd "${TARGET_DIR:-.}" && timeout "${LOKI_GATE_TIMEOUT:-300}" \
|
|
9662
|
-
node --test "${_nt_files[@]}" 2>&1) || test_passed=false
|
|
9678
|
+
node --test --test-reporter=tap "${_nt_files[@]}" 2>&1) || test_passed=false
|
|
9663
9679
|
# tail -14 so node's TAP summary block (# tests / # pass N / # fail N,
|
|
9664
9680
|
# ~10 lines) survives truncation for the best-effort count parse below.
|
|
9665
9681
|
details="node --test: $(echo "$output" | tail -14 | tr '\n' ' ')"
|
|
@@ -10994,6 +11010,39 @@ _dispatch_reviewer() {
|
|
|
10994
11010
|
# then deletes its flag and emits nothing). Set inline, not via
|
|
10995
11011
|
# the helper, so the carve-out holds even when the helper is
|
|
10996
11012
|
# out of scope. No-op when caveman is absent.
|
|
11013
|
+
# STRUCTURED VERDICT (v8.x): when the CLI supports --json-schema,
|
|
11014
|
+
# force valid JSON and re-materialize the LEGACY VERDICT/FINDINGS text
|
|
11015
|
+
# so every downstream consumer (_classify_verdict,
|
|
11016
|
+
# _severity_is_blocking, _count_nonblocking_findings, mergeability,
|
|
11017
|
+
# DA arm, aggregate.json) stays byte-identical. Fail-closed: any
|
|
11018
|
+
# failure falls through to the text path below (which itself may leave
|
|
11019
|
+
# an empty file -> NO_VERDICT -> inconclusive block). NEVER a PASS on a
|
|
11020
|
+
# miss. --json-schema takes INLINE content, not a path (CLI 2.1.207
|
|
11021
|
+
# rejects a path). Opt out with LOKI_REVIEW_JSON_SCHEMA=off.
|
|
11022
|
+
local _cr_root _cr_here _cr_schema _cr_remat
|
|
11023
|
+
_cr_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
11024
|
+
_cr_here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11025
|
+
_cr_schema="${_cr_root}/loki-ts/data/code-review-schema.json"
|
|
11026
|
+
_cr_remat="${_cr_here}/lib/cr-rematerialize.py"
|
|
11027
|
+
if [ "${LOKI_REVIEW_JSON_SCHEMA:-on}" != "off" ] \
|
|
11028
|
+
&& [ -f "$_cr_schema" ] && [ -f "$_cr_remat" ] \
|
|
11029
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
11030
|
+
&& loki_claude_flag_supported "--json-schema"; then
|
|
11031
|
+
local _cr_schema_content _cr_json _cr_rc=0
|
|
11032
|
+
_cr_schema_content="$(cat "$_cr_schema" 2>/dev/null)" || _cr_schema_content=""
|
|
11033
|
+
if [ -n "$_cr_schema_content" ]; then
|
|
11034
|
+
_cr_json="$(CAVEMAN_DEFAULT_MODE=off \
|
|
11035
|
+
claude "${_rv_argv[@]}" -p "$prompt_text" \
|
|
11036
|
+
--json-schema "$_cr_schema_content" \
|
|
11037
|
+
--output-format json 2>/dev/null)" || _cr_rc=$?
|
|
11038
|
+
if [ "$_cr_rc" -eq 0 ] && [ -n "$_cr_json" ] \
|
|
11039
|
+
&& _LOKI_CR_JSON="$_cr_json" _LOKI_CR_OUT="$review_output" \
|
|
11040
|
+
python3 "$_cr_remat"; then
|
|
11041
|
+
return 0
|
|
11042
|
+
fi
|
|
11043
|
+
fi
|
|
11044
|
+
# fall through to the text path below (fail-closed)
|
|
11045
|
+
fi
|
|
10997
11046
|
CAVEMAN_DEFAULT_MODE=off \
|
|
10998
11047
|
claude "${_rv_argv[@]}" -p "$prompt_text" \
|
|
10999
11048
|
--output-format text > "$review_output" 2>/dev/null
|
package/autonomy/verify.sh
CHANGED
|
@@ -427,6 +427,14 @@ _verify_zero_tests_executed() {
|
|
|
427
427
|
verify_gate_tests() {
|
|
428
428
|
local tree="$1"
|
|
429
429
|
local timeout_s="${LOKI_GATE_TIMEOUT:-300}"
|
|
430
|
+
# Bounded-exec prefix: "timeout <secs>" / "gtimeout <secs>", or EMPTY when
|
|
431
|
+
# neither is on PATH (bare macOS, or a shadowed PATH). A bare `timeout ...`
|
|
432
|
+
# would exit 127 (command not found) and be misread as a test FAILURE ->
|
|
433
|
+
# spurious BLOCKED verdict. Degrade to running without a wall-clock cap
|
|
434
|
+
# instead. Mirrors run.sh's with_timeout discipline.
|
|
435
|
+
local _vt_bin _vt
|
|
436
|
+
_vt_bin="$(_verify_runtime_timeout_bin)"
|
|
437
|
+
if [ -n "$_vt_bin" ]; then _vt="$_vt_bin $timeout_s"; else _vt=""; fi
|
|
430
438
|
local runner="none"
|
|
431
439
|
local rc=0
|
|
432
440
|
local out=""
|
|
@@ -434,13 +442,13 @@ verify_gate_tests() {
|
|
|
434
442
|
if [ -f "$tree/package.json" ]; then
|
|
435
443
|
if grep -q '"vitest"' "$tree/package.json" 2>/dev/null; then
|
|
436
444
|
runner="vitest"
|
|
437
|
-
out="$(cd "$tree" &&
|
|
445
|
+
out="$(cd "$tree" && $_vt npx vitest run 2>&1)" || rc=$?
|
|
438
446
|
elif grep -q '"jest"' "$tree/package.json" 2>/dev/null; then
|
|
439
447
|
runner="jest"
|
|
440
|
-
out="$(cd "$tree" &&
|
|
448
|
+
out="$(cd "$tree" && $_vt npx jest --passWithNoTests --forceExit 2>&1)" || rc=$?
|
|
441
449
|
elif grep -q '"mocha"' "$tree/package.json" 2>/dev/null; then
|
|
442
450
|
runner="mocha"
|
|
443
|
-
out="$(cd "$tree" &&
|
|
451
|
+
out="$(cd "$tree" && $_vt npx mocha 2>&1)" || rc=$?
|
|
444
452
|
fi
|
|
445
453
|
fi
|
|
446
454
|
|
|
@@ -480,7 +488,7 @@ verify_gate_tests() {
|
|
|
480
488
|
if [ "$has_python" = "true" ]; then
|
|
481
489
|
if command -v pytest >/dev/null 2>&1; then
|
|
482
490
|
runner="pytest"
|
|
483
|
-
out="$(cd "$tree" &&
|
|
491
|
+
out="$(cd "$tree" && $_vt pytest --tb=short 2>&1)" || rc=$?
|
|
484
492
|
elif command -v python3 >/dev/null 2>&1; then
|
|
485
493
|
# #139 parity: pytest ABSENT but a Python suite exists -> run it with
|
|
486
494
|
# stdlib unittest (zero third-party deps) instead of going blindly
|
|
@@ -490,7 +498,7 @@ verify_gate_tests() {
|
|
|
490
498
|
# a "Ran 0 tests" line detected by the zero-test guard on the run.sh
|
|
491
499
|
# mirror) so this never green-washes an empty suite.
|
|
492
500
|
runner="unittest"
|
|
493
|
-
out="$(cd "$tree" &&
|
|
501
|
+
out="$(cd "$tree" && $_vt python3 -m unittest discover -p 'test_*.py' 2>&1)" || rc=$?
|
|
494
502
|
else
|
|
495
503
|
# Applicable but cannot run -> inconclusive (Entanglement 2).
|
|
496
504
|
_verify_add_gate "tests" "inconclusive" "pytest" "python project detected but no pytest/python3 on PATH" "true"
|
|
@@ -502,7 +510,7 @@ verify_gate_tests() {
|
|
|
502
510
|
if [ "$runner" = "none" ] && [ -f "$tree/go.mod" ]; then
|
|
503
511
|
if command -v go >/dev/null 2>&1; then
|
|
504
512
|
runner="go-test"
|
|
505
|
-
out="$(cd "$tree" &&
|
|
513
|
+
out="$(cd "$tree" && $_vt go test ./... 2>&1)" || rc=$?
|
|
506
514
|
else
|
|
507
515
|
_verify_add_gate "tests" "inconclusive" "go-test" "go project detected but go not on PATH" "true"
|
|
508
516
|
return 0
|
|
@@ -512,7 +520,7 @@ verify_gate_tests() {
|
|
|
512
520
|
if [ "$runner" = "none" ] && [ -f "$tree/Cargo.toml" ]; then
|
|
513
521
|
if command -v cargo >/dev/null 2>&1; then
|
|
514
522
|
runner="cargo-test"
|
|
515
|
-
out="$(cd "$tree" &&
|
|
523
|
+
out="$(cd "$tree" && $_vt cargo test 2>&1)" || rc=$?
|
|
516
524
|
else
|
|
517
525
|
_verify_add_gate "tests" "inconclusive" "cargo-test" "rust project detected but cargo not on PATH" "true"
|
|
518
526
|
return 0
|
|
@@ -550,7 +558,13 @@ verify_gate_tests() {
|
|
|
550
558
|
runner="node-test"
|
|
551
559
|
# Pass matched files explicitly (node --test globbing is
|
|
552
560
|
# Node-version-sensitive); quote each so paths with spaces survive.
|
|
553
|
-
|
|
561
|
+
# Force --test-reporter=tap: Node 20+ defaults to the human "spec"
|
|
562
|
+
# reporter (Node 26 emits it even under capture), whose "i pass N" /
|
|
563
|
+
# check-mark lines the count parser + zero-tests detector below do NOT
|
|
564
|
+
# match; they expect TAP "# pass N" / "ok N - ". TAP is stable since
|
|
565
|
+
# Node 18, so forcing it restores a version-independent format with no
|
|
566
|
+
# parser change. Parity with autonomy/run.sh:9662.
|
|
567
|
+
out="$(cd "$tree" && $_vt node --test --test-reporter=tap "${_nt_files[@]}" 2>&1)" || rc=$?
|
|
554
568
|
fi
|
|
555
569
|
fi
|
|
556
570
|
|
|
@@ -564,6 +578,22 @@ verify_gate_tests() {
|
|
|
564
578
|
return 0
|
|
565
579
|
fi
|
|
566
580
|
|
|
581
|
+
# #82 (Python 3.12+): `python3 -m unittest` exits 5 (NOT 0) when it discovers
|
|
582
|
+
# ZERO tests -- e.g. a suite of pytest-style bare `def test_*` functions that
|
|
583
|
+
# unittest (TestCase-only) cannot collect. That non-zero exit would otherwise
|
|
584
|
+
# fall into the fail branch below and record tests=fail -> BLOCKED, when the
|
|
585
|
+
# honest classification is "applicable but ran no real tests" = inconclusive
|
|
586
|
+
# -> CONCERNS. Route the unittest zero-discovery case through the same guard
|
|
587
|
+
# the rc==0 path uses (its unittest arm matches "Ran 0 tests"/"NO TESTS RAN"
|
|
588
|
+
# but was unreachable behind rc==0). Scoped to unittest so pytest/go/cargo/node
|
|
589
|
+
# non-zero exits still correctly record fail.
|
|
590
|
+
if [ "$rc" -eq 5 ] && [ "$runner" = "unittest" ] \
|
|
591
|
+
&& _verify_zero_tests_executed "$runner" "$out" "${_nt_files[@]:-}"; then
|
|
592
|
+
_verify_add_gate "tests" "inconclusive" "$runner" \
|
|
593
|
+
"runner ran but discovered zero tests (source_without_runnable_tests)" "true"
|
|
594
|
+
return 0
|
|
595
|
+
fi
|
|
596
|
+
|
|
567
597
|
if [ "$rc" -eq 0 ]; then
|
|
568
598
|
# #82: a green run that executed ZERO real tests is a mini fake-green.
|
|
569
599
|
# Record it as HONEST inconclusive (forces at-least-CONCERNS), NOT pass
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
|
|
4
4
|
|
|
5
|
-
**Version:** v7.
|
|
5
|
+
**Version:** v7.129.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -396,7 +396,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
|
|
|
396
396
|
# Run Loki Mode in Docker (Claude provider, API-key auth)
|
|
397
397
|
docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
|
|
398
398
|
-v $(pwd):/workspace -w /workspace \
|
|
399
|
-
asklokesh/loki-mode:7.
|
|
399
|
+
asklokesh/loki-mode:7.129.0 start ./my-spec.md
|
|
400
400
|
```
|
|
401
401
|
|
|
402
402
|
##### docker compose + .env (no host install)
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# RARV-C 100x Native-Primitives Upgrade - Plan (v8.0.0 arc)
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
Founder mandate (memory `project-rarvc-cookbook-upgrade`): we can't out-build
|
|
6
|
+
Replit/Emergent/Cognition on capability. Loki's moat is loop RIGOR (never-fake-green
|
|
7
|
+
RARV-C, 3-reviewer council, evidence gate), which is ON-THESIS with Anthropic's own
|
|
8
|
+
long-running-agent guidance. The gap isn't rigor; it's efficiency: the loop does its
|
|
9
|
+
mechanics via home-grown bash (parsing model output as TEXT, polling for budget,
|
|
10
|
+
inferring cost from token math) where the installed `claude` CLI now ships NATIVE
|
|
11
|
+
primitives that do the same job cheaper, faster, and without fragile parsing.
|
|
12
|
+
|
|
13
|
+
This plan adopts those primitives where they MEASURABLY beat the bash equivalents, one
|
|
14
|
+
per commit, bench-proven, quality never regresses. MAJOR arc (v8.0.0) on branch
|
|
15
|
+
`feature/rarv-c-100x-primitives` (not yet created).
|
|
16
|
+
|
|
17
|
+
**The honest headline: much of this is already shipped.** Two prior plan docs exist
|
|
18
|
+
(`docs/RARV-C-LOOP-EFFICIENCY-PLAN.md`, `docs/RARV-C-CHANGE-MAP.md`, grounded to v7.121.5),
|
|
19
|
+
AND the engine has since adopted several native flags. So this plan does NOT regenerate
|
|
20
|
+
them. It: (1) reconciles drift to v7.128.2, (2) records what's ALREADY adopted so we don't
|
|
21
|
+
rebuild it, (3) scopes the genuinely net-new work (extend structured-outputs to 4 remaining
|
|
22
|
+
text-parse sites), (4) adds the missing PARITY and RELEASE sections, and consolidates into
|
|
23
|
+
`docs/RARV-C-100X-PLAN.md`.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Primary-source verification (done live this session, CLI v2.1.207)
|
|
28
|
+
|
|
29
|
+
Per `reference-claude-session-flags` ("live-probe, don't trust stale training or --help
|
|
30
|
+
alone"), every flag was verified by RUNNING it:
|
|
31
|
+
|
|
32
|
+
| Primitive | Flag | Verified behavior (2026-07-13) |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Structured output | `--json-schema <schema>` | Forces valid JSON. Envelope carries top-level `structured_output` key + `result` valid-JSON + `stop_reason:"tool_use"`. Model CANNOT emit malformed output. |
|
|
35
|
+
| Cost breaker | `--max-budget-usd <amt>` | On exceed: `subtype:"error_max_budget_usd"`, `is_error:true`, exit 0 (graceful, parseable). |
|
|
36
|
+
| Effort dial | `--effort low\|medium\|high\|xhigh\|max` | 5 levels; maps onto `get_rarv_tier()`. |
|
|
37
|
+
| Rate-limit resilience | `--fallback-model <model>` | Present. |
|
|
38
|
+
| Caching visibility | JSON envelope | `usage.cache_creation.ephemeral_1h_input_tokens` vs `ephemeral_5m` per call -> caching measurable. |
|
|
39
|
+
| Cost/iter readout | JSON envelope | `total_cost_usd`, per-model `costUSD`, `cache_read_input_tokens`, `iterations[]`. |
|
|
40
|
+
| Continuity | `--resume`/`--session-id`/`--fork-session` | iter0 `--session-id U0`, iterN `--resume U0`; `--session-id` reuse ERRORS. |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## What is ALREADY adopted (do NOT rebuild - ponytail rung 2)
|
|
45
|
+
|
|
46
|
+
Verified in current source. The prior CHANGE-MAP predates these:
|
|
47
|
+
|
|
48
|
+
- **`--effort` + `--max-budget-usd` are ALREADY plumbed** in `_loki_build_claude_auto_flags`
|
|
49
|
+
(`providers/claude.sh:143-158`), gated on `loki_claude_flag_supported` (degrades on old CLI),
|
|
50
|
+
mirrored in `loki-ts/src/providers/claude_flags.ts` (`effortForTier()`:23, `buildAutoFlags()`:150).
|
|
51
|
+
So "adopt native budget/effort breakers" is LARGELY DONE. `check_budget_limit()` (`run.sh:13035`)
|
|
52
|
+
and `is_rate_limited()` (`run.sh:12835`) still exist as the belt to the native suspenders.
|
|
53
|
+
- **`--json-schema` is ALREADY adopted for the council-vote path**: `voter-agents.sh:277`
|
|
54
|
+
(`claude --agents <json> --json-schema <schema>`), wired as PREFERRED council dispatch at
|
|
55
|
+
`completion-council.sh:3137-3144` with graceful text-parse fallback (3146-3154). Schema:
|
|
56
|
+
`loki-ts/data/finding-schema.json`. TS mirror: `loki-ts/src/council/voter_agents.ts:299-327`.
|
|
57
|
+
This pair is the TEMPLATE to extend, not a greenfield build.
|
|
58
|
+
- **Completion detection is already structured** via `.loki/signals/*` + `loki_complete_task`
|
|
59
|
+
MCP tool (`run.sh:13351-13369`); text-match is legacy behind `LOKI_LEGACY_COMPLETION_MATCH=true`.
|
|
60
|
+
|
|
61
|
+
**Net-new structured-output work = extend the voter-agents+schema template to the 4 remaining
|
|
62
|
+
text-parse sites. Everything else is tuning + bench + pointing side-calls at flags that exist.**
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Reconciled anchors (v7.128.2; lines drift, re-grep before editing)
|
|
67
|
+
|
|
68
|
+
**completion-council.sh:** `_council_effective_min_iter()`:118 | `COUNCIL_CHECK_INTERVAL`:84 |
|
|
69
|
+
`COUNCIL_STAGNATION_LIMIT`:132 | `council_evidence_gate()`:1704 | explicit-claim fast path:3640
|
|
70
|
+
(block 3625-3648) | modulo gate now in helper `_council_should_check_now()`:3521-3560 (gate:3548),
|
|
71
|
+
called from `council_should_stop`:3644 | `council_should_stop()`:3562
|
|
72
|
+
|
|
73
|
+
**run.sh:** `detect_complexity()`:2491 | `get_rarv_tier()`:2616 | `run_code_review()`:11095 |
|
|
74
|
+
`is_rate_limited()`:12835 | `check_budget_limit()`:13035 | `store_episode_trace()`:13729 |
|
|
75
|
+
`save_state()`:14395 | `build_prompt()`:14633 | `run_autonomous()`:16299 | `LOKI_SESSION_MODEL`
|
|
76
|
+
default:742, session-pin read:16959, case block:16963-16967 | `[CACHE_BREAKPOINT]` INERT (doc anchor
|
|
77
|
+
only, no cache wiring):15143/15231/15278 | APP_CRASHED injection:14969 (python3 -c inline)
|
|
78
|
+
|
|
79
|
+
**Main-loop invocation:** `run.sh:17316-17320` -> `claude "${_loki_claude_argv[@]}" -p "$prompt"
|
|
80
|
+
--output-format stream-json --verbose` (argv ~17226+). Uses stream-json, not --json-schema.
|
|
81
|
+
Model catalog: opus->claude-opus-4-8, sonnet->claude-sonnet-5, haiku->claude-haiku-4-5; defaults
|
|
82
|
+
planning/development/fast ALL=sonnet (v7.104.0).
|
|
83
|
+
|
|
84
|
+
### The 4 remaining text-parse sites (the genuine structured-output targets)
|
|
85
|
+
1. **Code-review verdict** `run.sh:11044-11063` `_classify_verdict` (regex `grep -iE "VERDICT:"`
|
|
86
|
+
-> case FAIL/PASS/AMBIGUOUS, no retry) + severity greps :11075/:11090; reviewer call :10998
|
|
87
|
+
uses `--output-format text`. Parity-locked to `loki-ts/src/runner/quality_gates.ts`.
|
|
88
|
+
2. **Council VOTE fallback** `completion-council.sh:645-659` (`grep -oE VOTE:`) + `:1005` (Python
|
|
89
|
+
`re.search`) - the fallback under the already-structured path; mangled VOTE silently->REJECT.
|
|
90
|
+
3. **Council-v2 JSON** `council-v2.sh:337-346` (`sed -n '/^{/,/^}/p'` carves JSON from prose, else
|
|
91
|
+
hardcodes REJECT); invocation :299 (no schema).
|
|
92
|
+
4. **Done-recognition** `done-recognition.sh:282-303` (`json.loads` + brace-substring fallback,
|
|
93
|
+
else `inconclusive`); call :57/:171 (no schema).
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Deliverable 1: Bench harness (BUILD/LOCK THIS FIRST - nothing ships before it)
|
|
98
|
+
|
|
99
|
+
Reuse `benchmarks/bench/` (~95% built per existing plan S2a). The work is lock + reconcile +
|
|
100
|
+
baseline, not a build:
|
|
101
|
+
- **Lock a discriminator-derived corpus** (existing plan 2e-HARDENING #3): simple + HARD (tier-guard)
|
|
102
|
+
+ MULTI-FAILURE (parallel-fix) + TOKEN-HEAVY (distillation), each with machine-checkable held-out
|
|
103
|
+
acceptance. Frozen once baselined.
|
|
104
|
+
- **Reconcile iterations source** to `events.jsonl` `iteration_complete` count (canonical) with
|
|
105
|
+
session.json fallback, so `adapters/loki.py` and `speed-benchmark.sh` agree. PREREQUISITE, blocking.
|
|
106
|
+
- **NEW (from live probe):** read cost/cache/iterations straight from the `--json-schema`/`--output-format
|
|
107
|
+
json` envelope (`total_cost_usd`, `cache_read_input_tokens`, `iterations[]`) instead of token-math x
|
|
108
|
+
price-table where available - more accurate, self-consistent with the CLI.
|
|
109
|
+
- **Noise floor** (blocking): baseline reports SPREAD (std/min-max across trials); a change ships only
|
|
110
|
+
if its metric moves BEYOND the noise band. Effect < trial variance -> "no measurable effect", don't ship.
|
|
111
|
+
- **2-spec smoke check** (the one runnable guard): one MUST-pass, one underspecified MUST-fail; assert
|
|
112
|
+
`verified_pass == [True, False]`. If the grader can't tell them apart it measures nothing.
|
|
113
|
+
- **Commit the v7.128.2 baseline** (`benchmarks/results/<baseline>.json`, N real OFF-WORKTREE builds,
|
|
114
|
+
isolated source copy, cwd + LOKI_TARGET_DIR pinned to scratch) BEFORE any change.
|
|
115
|
+
- **Grader stays deterministic** `success = exit==0` on held-out acceptance. NEVER council/completion-claim.
|
|
116
|
+
`validate_adapter_output()` already rejects self-judged keys - keep that invariant.
|
|
117
|
+
|
|
118
|
+
**Gate rule (binding, every change):** ships iff it improves >=1 of {iterations, $/build, wall-clock}
|
|
119
|
+
beyond the noise band AND does not regress verified-pass-rate on the frozen corpus vs baseline.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Deliverable 2: Primitive-by-primitive adoption table (ranked, one per commit)
|
|
124
|
+
|
|
125
|
+
Bucket key: **(a)** orchestration/flags, changeable now | **(b)** founder-gated (build_prompt
|
|
126
|
+
byte-lock) | **(c)** needs off-worktree real-build validation.
|
|
127
|
+
|
|
128
|
+
| # | Primitive / change | Current impl (file:line) | Native replacement | Expected win | Bucket | Parity | Rollback |
|
|
129
|
+
|---|---|---|---|---|---|---|---|
|
|
130
|
+
| 1 | **Structured code-review verdict** | `run.sh:11044` regex `_classify_verdict` + severity greps :11075/:11090; reviewer :10998 `--output-format text` | reviewer emits `--json-schema` (extend `voter-agents.sh`+`finding-schema.json` template); read `structured_output.verdict/severity` | Kills fragile parse + AMBIGUOUS-token bugs; zero retry. Quality-safety, not speed | (a) | safe (invocation flag, NOT build_prompt); MUST mirror in `quality_gates.ts` | keep text fallback path (like council does 3146-3154) |
|
|
131
|
+
| 2 | **Structured council VOTE** (retire text fallback) | `completion-council.sh:645/1005` regex/re.search fallback | make the already-wired `--json-schema` path (3137) the only path; delete text fallback once proven | Removes silent VOTE->REJECT corruption (:2337) | (a) | safe; TS mirror `voter_agents.ts` | revert to fallback |
|
|
132
|
+
| 3 | **Structured council-v2** | `council-v2.sh:337-346` sed-carved JSON | add `--json-schema` + `--output-format json` to :299 | Removes "prose around JSON"->hardcoded REJECT | (a) | safe | hardcoded-REJECT fallback stays |
|
|
133
|
+
| 4 | **Structured done-recognition** | `done-recognition.sh:282-303` json.loads+brace-slice | add `--json-schema` to :57/:171 | Removes unparsable->inconclusive | (a) | safe | inconclusive fallback stays |
|
|
134
|
+
| 5 | **Council interval sweep** (existing CHANGE-MAP #1) | `completion-council.sh:84` interval=5 | tune 2-3 for simple tier | fewer idle iters -> $/wall-clock | (a) | n/a | env default |
|
|
135
|
+
| 6 | **Complexity->tier routing** (existing #3) | `run.sh:742` flat sonnet; case :16963 | `auto` arm: simple->haiku-enable, complex->opus-pin | $/build on simple majority | (c) | 3 readers + hard-guard | revert to `sonnet` |
|
|
136
|
+
| 7 | **Native budget/effort tighten** | ALREADY plumbed `claude.sh:143-158`; side-calls (:10998, council-v2:299, done-rec:57) still bare | point side-calls at `--max-budget-usd`/`--effort` too | consistent native breaker everywhere | (a) | mirror bash<->TS flags | drop flag |
|
|
137
|
+
| 8 | **Prompt caching** (existing #7, CONTINGENT) | `[CACHE_BREAKPOINT]` INERT `run.sh:15143` | wire real `cache_control` on static prefix IF CLI folds it across per-iter processes | $/build on multi-iter (cache reads visible in envelope) | (b)-adjacent | verify CLI first; may be no-op | remove marker wiring |
|
|
138
|
+
| 9 | **Self-heal from logs** (existing #4) | APP_CRASHED count only `run.sh:14969`; LAST_ERROR write-only | route `tail+grep` error signature into prompt | pass-rate on crashing specs | (c) | build_prompt-adjacent -> check byte-lock | honest-degrade to today |
|
|
139
|
+
|
|
140
|
+
Ranked order to execute: **1 -> 2 -> 3 -> 4** (structured-outputs cluster, all bucket-(a),
|
|
141
|
+
parity-safe, mostly quality/robustness - do first, low risk) **-> 5** (interval, zero code)
|
|
142
|
+
**-> 7** (side-call flag tighten) **-> 6** (tier routing, (c)) **-> 9** (self-heal, (c)) **-> 8**
|
|
143
|
+
(caching, contingent, verify CLI first). Each: change, re-run corpus, apply gate rule. No bench
|
|
144
|
+
move the right way = don't ship (ponytail: no no-ops).
|
|
145
|
+
|
|
146
|
+
**Do NOT** touch `build_prompt()` output text (items that would: 8, 9 partially) without founder
|
|
147
|
+
auth + 60-fixture regen. Structured-outputs items 1-4 do NOT touch build_prompt - confirmed
|
|
148
|
+
parity-safe below.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Deliverable 3: Parity plan
|
|
153
|
+
|
|
154
|
+
**The load-bearing fact:** the 60-fixture SHA-256 lock (`loki-ts/tests/parity/build_prompt.test.ts`,
|
|
155
|
+
KNOWN_FAILING empty = hard 60/60) hashes ONLY `buildPrompt()` return TEXT. CLI flags come from a
|
|
156
|
+
separate surface (`claude_flags.ts` / `voter_agents.ts` / `providers.ts:244`). **So adding
|
|
157
|
+
`--json-schema`/`--effort`/`--max-budget-usd` to a claude call is parity-SAFE w.r.t. the lock**, and
|
|
158
|
+
also doesn't touch the 10-command CLI matrix (which never invokes a model).
|
|
159
|
+
|
|
160
|
+
**The parity TRAP this arc must own** (agent-verified): a native flag added to ONE route only
|
|
161
|
+
(bash `autonomy/lib/claude-flags.sh` vs TS `loki-ts/src/providers/claude_flags.ts`) would **pass all
|
|
162
|
+
four parity gates** (bun-parity.yml, parity-drift.yml, local-ci matrix, build_prompt lock) while
|
|
163
|
+
diverging at RUNTIME - because the matrix doesn't spawn the model. Therefore:
|
|
164
|
+
- **Every native-flag change lands in BOTH `claude-flags.sh` AND `claude_flags.ts`, byte-mirrored**,
|
|
165
|
+
gated on `claudeFlagSupported()`/`loki_claude_flag_supported` for old-CLI degrade.
|
|
166
|
+
- **Proven by the flag-parity unit tests** (`loki-ts/tests/runner/providers.test.ts`,
|
|
167
|
+
`tests/test-bash-bun-parity.sh` run at `local-ci.sh:390`), NOT the CLI matrix. Add a case per new flag.
|
|
168
|
+
- Structured-output items 1-4: mirror the bash change (voter-agents/council-v2/done-rec/quality-gates)
|
|
169
|
+
in the corresponding TS (`council/voter_agents.ts`, `runner/quality_gates.ts`).
|
|
170
|
+
- **build_prompt-touching items (8, 9)**: founder-gated. Regenerate all 60 fixtures via `run-bash.sh`
|
|
171
|
+
(`for i in $(seq 1 60); do bash run-bash.sh fixture-$i; done`), regenerate `index.json`, keep bash<->TS
|
|
172
|
+
60/60 in the SAME PR. Watch the fixtures 27/45 alphabetical-readdir determinism guard.
|
|
173
|
+
- **Doctor normalizers are TRIPLICATED** (`bun-parity.yml:127-184`, `parity-drift.yml:83-135`,
|
|
174
|
+
`local-ci.sh:812-860`) with subtle diffs (JSON floor-vs-delete, counts kept-vs-blanked). If any
|
|
175
|
+
primitive changes doctor output, edit ALL THREE in sync.
|
|
176
|
+
|
|
177
|
+
Every commit proven on BOTH routes (`bin/loki` + `LOKI_LEGACY_BASH=1 bin/loki`).
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Deliverable 4: Gate plan
|
|
182
|
+
|
|
183
|
+
- **Trust-core = 3-reviewer council** (2 Opus + 1 Sonnet, unanimous APPROVE, reviewers RUN tests).
|
|
184
|
+
Any CONCERN/REJECT -> read source, fix, RE-RUN whole council. Loop to 3/3. Never "2-of-3".
|
|
185
|
+
- **`bash scripts/local-ci.sh` green before EVERY push** (Step 0). It mirrors every workflow:
|
|
186
|
+
bash -n + shellcheck, pytest, `bun typecheck` + `bun test` (the 60-fixture lock runs here),
|
|
187
|
+
CLI dual-route (`test-cli-commands.sh` Bun + LEGACY_BASH), `test-bash-bun-parity.sh`, the local
|
|
188
|
+
bun-parity matrix, npm-pack contents (>=6 load-bearing files), SBOM, no-emoji, no-`git add -A`.
|
|
189
|
+
- **Extraction-test harness for bash fn changes** (`feedback-function-extraction-test-harness`):
|
|
190
|
+
extract the changed fn AND every helper it calls (grep `sed -n '/^<fn>() {/'` tests); `set +u`
|
|
191
|
+
inside the eval subshell; prove runtime-vs-test before "fixing" a red test (fix the TEST if runtime
|
|
192
|
+
is right, never patch correct code).
|
|
193
|
+
- **RARV-C changes proven with hermetic Bun integration tests** (stub judge, `LOKI_OVERRIDE_REAL_JUDGE=0`,
|
|
194
|
+
mkdtemp scratch, no token cost) - pattern in `loki-ts/tests/integration/override_on_block.test.ts`.
|
|
195
|
+
- **Never run an engine build from the worktree** (`feedback-never-run-engine-build`): all real builds
|
|
196
|
+
from isolated source copy, cwd + LOKI_TARGET_DIR pinned to scratch.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Deliverable 5: Release plan (v8.0.0, MAJOR)
|
|
201
|
+
|
|
202
|
+
- **14 version locations** (all verified at 7.128.2, zero drift): VERSION:1, package.json:4,
|
|
203
|
+
SKILL.md:6+411, Dockerfile:9/14, Dockerfile.sandbox:102/103, plugins/.../plugin.json:5, CLAUDE.md:306,
|
|
204
|
+
dashboard/__init__.py:10, mcp/__init__.py:60, CHANGELOG.md:8, docs/INSTALLATION.md:5(+399 docker tag),
|
|
205
|
+
wiki/Home.md:106, wiki/_Sidebar.md:46, wiki/API-Reference.md:69.
|
|
206
|
+
- **MAJOR bump also touches docker-tag group** (README.md ~81/~380, docs/INSTALLATION.md 7+ tags,
|
|
207
|
+
docker-compose.yml:1).
|
|
208
|
+
- **Do NOT touch** (independently versioned, would break if "fixed"): `sdk/python/pyproject.toml`
|
|
209
|
+
(auto-synced by release.yml:352 at publish), `sdk/typescript/package.json` (5.55.0),
|
|
210
|
+
`loki-ts/package.json` (0.1.0-alpha.1), `vscode-extension/package.json` (deprecated).
|
|
211
|
+
- **FIX in v8 - release-notes bug** (`release.yml:179/231`): awk expects bracketed `## [VERSION]` but
|
|
212
|
+
CHANGELOG uses unbracketed `## vVERSION`, so release notes have silently been generic. Fix the awk or
|
|
213
|
+
the header format (one PR, verify the extracted body is non-empty).
|
|
214
|
+
- **Dashboard frontend rebuilt** (`cd dashboard-ui && npm ci && npm run build:all`) - writes both
|
|
215
|
+
`dashboard-ui/dist/` and `dashboard/static/`.
|
|
216
|
+
- **Pre-publish validation** (CLAUDE.md 3a): `npm pack --dry-run` contains web-app/dist + dashboard/static;
|
|
217
|
+
fresh global install serves web app + API.
|
|
218
|
+
- **Per-job release verification**: `gh run view <id> --json jobs` per channel (npm/Docker/Homebrew/Release),
|
|
219
|
+
not just "workflow green".
|
|
220
|
+
- **Post-release smoke from SHIPPED artifacts on BOTH routes**: `npm pack loki-mode@8.0.0` + `bun run bin/loki
|
|
221
|
+
version` and `LOKI_LEGACY_BASH=1 ... version`; `docker run --rm asklokesh/loki-mode:8.0.0 doctor --json`;
|
|
222
|
+
WebFetch brew formula version+sha.
|
|
223
|
+
- **Cleanup after every local-ci/validation**: `lsof -ti:57374 | xargs kill -9; rm -rf /tmp/loki-* /tmp/test-* /tmp/package /tmp/*.tgz`.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Do-NOT list (binding)
|
|
228
|
+
|
|
229
|
+
1. Do NOT replace the 3-reviewer council with a single-LLM outcome-grader. Structured-outputs changes
|
|
230
|
+
the OUTPUT FORMAT (text->JSON), NOT the 3-judge architecture. That's the moat.
|
|
231
|
+
2. Do NOT add an LLM judge to the bench grader (deterministic exit==0 only; `validate_adapter_output`
|
|
232
|
+
rejects self-judged keys).
|
|
233
|
+
3. Do NOT trust council APPROVE / completion-claim as the bench pass signal (fake-green failure mode).
|
|
234
|
+
4. Do NOT touch `build_prompt()` output text without founder auth + 60-fixture regen + 60/60 bash<->TS.
|
|
235
|
+
5. Do NOT change corpus/grader/price-table across a before/after pair.
|
|
236
|
+
6. Do NOT run a real engine build from the worktree.
|
|
237
|
+
7. Do NOT add a native flag to one route only (silent runtime divergence past all parity gates).
|
|
238
|
+
8. Do NOT ship a change that doesn't move the bench the right way.
|
|
239
|
+
9. Do NOT rebuild what's already adopted (--effort/--max-budget-usd plumbing, council --json-schema path).
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## FOUNDER SCOPE DECISIONS (2026-07-13, locked)
|
|
244
|
+
|
|
245
|
+
- **SCOPE = FULL ARC** including founder-gated items 8 (caching) and 9 (self-heal-from-logs).
|
|
246
|
+
This constitutes explicit authorization to unlock `build_prompt()`'s 60-fixture byte-lock WHEN
|
|
247
|
+
those items come up - executed per Deliverable 3 (regenerate all 60 fixtures via `run-bash.sh`,
|
|
248
|
+
keep bash<->TS 60/60 in the same PR, full council + local-ci). Founder-gated items still run LAST,
|
|
249
|
+
after all bucket-(a) structured-output wins are banked and bench-measured. Caching (8) still
|
|
250
|
+
requires the CLI-mechanics verification (does the CLI fold cache_control across per-iteration
|
|
251
|
+
processes?) BEFORE committing - if it's a no-op, report that and drop it, don't ship a no-op.
|
|
252
|
+
- **BASELINE = 3-SPEC SMOKE FIRST.** Prove the full pipeline (baseline -> one change -> measure ->
|
|
253
|
+
gate rule) on a tiny 3-spec corpus to validate the harness machinery cheaply, THEN build the real
|
|
254
|
+
discriminator-derived corpus (simple+hard+multi-failure+token-heavy) and run the full v7.128.2
|
|
255
|
+
baseline. Catches harness bugs before the big real-dollar run.
|
|
256
|
+
- **Honesty pre-commit stands:** most iteration wins are already shipped; expect single-digit-% to
|
|
257
|
+
maybe-2x from tuning, NOT 100x (the real 100x is the already-scoped k8s INFRA work, not this arc).
|
|
258
|
+
Report the true number even if unimpressive.
|
|
259
|
+
|
|
260
|
+
## Consolidation + sequencing
|
|
261
|
+
|
|
262
|
+
1. Create branch `feature/rarv-c-100x-primitives`. Build harness, run 3-SPEC SMOKE to validate the
|
|
263
|
+
pipeline, then commit the full v7.128.2 baseline (Deliverable 1) before any change.
|
|
264
|
+
2. Write `docs/RARV-C-100X-PLAN.md` = this plan (supersedes/links the two prior v7.121.5 docs; keep
|
|
265
|
+
them as history, note they're superseded).
|
|
266
|
+
3. Execute the adoption table in ranked order, one primitive per commit, bench + full gate after each.
|
|
267
|
+
4. Founder-gated items (8 caching, 9 self-heal build_prompt half) LAST, only after (a)-items banked and
|
|
268
|
+
explicit unlock.
|
|
269
|
+
5. Release v8.0.0 per Deliverable 5 once the bench shows the aggregate win (or honestly report the real
|
|
270
|
+
delta if single-digit - the plan pre-commits to reporting the true number, not dressing it up).
|
|
271
|
+
|
|
272
|
+
## Verification (how to test end-to-end)
|
|
273
|
+
- Bench: `bash benchmarks/bench/run.sh run <spec> --trials 3` from isolated scratch; read
|
|
274
|
+
iterations/cost/duration from result JSON; grader `exit==0`. 2-spec smoke asserts [pass, fail].
|
|
275
|
+
- Structured-outputs: hermetic Bun integration test with stub judge asserts JSON verdict parsed,
|
|
276
|
+
no text-regex path hit; + extraction test for the bash side.
|
|
277
|
+
- Parity: `bash scripts/local-ci.sh` green (runs 60-fixture lock + dual-route CLI + flag-parity tests).
|
|
278
|
+
- Per commit: full 3-reviewer council + local-ci, both routes.
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# RARV-C Efficiency Change Map (apply-and-measure, from lever investigation)
|
|
2
2
|
|
|
3
|
+
> SUPERSEDED by `docs/RARV-C-100X-PLAN.md` (v8.0.0 arc, 2026-07-13). Kept as history.
|
|
4
|
+
> Anchors here are grounded to v7.121.5 and have DRIFTED; use the 100X plan for current
|
|
5
|
+
> file:line. Note: budget/effort (#7) and council --json-schema were adopted AFTER this doc.
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
Both checks resolve cleanly:
|
|
4
9
|
|
|
5
10
|
**Check 1 (env propagation): CONFIRMED SAFE.** `_base.py:113` does `full_env = dict(os.environ)` then passes it as `env=full_env` to subprocess. The loki adapter (`loki.py:120`) uses `run_env`. So env-prefixing the bench invocation propagates the knob into the loki subprocess, where completion-council.sh:84 reads it at source time. Every `KNOB=x bench run` command in the plan is a real measurement, not a no-op.
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# RARV-C Loop-Efficiency Upgrade Plan (ultracode phase)
|
|
2
2
|
|
|
3
|
+
> SUPERSEDED by `docs/RARV-C-100X-PLAN.md` (v8.0.0 arc, 2026-07-13). Kept as history.
|
|
4
|
+
> Anchors here are grounded to v7.121.5 and have DRIFTED; use the 100X plan for current
|
|
5
|
+
> file:line and for the native-primitives-already-adopted findings.
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
Architect: synthesis of the six cookbook pattern sets against the current
|
|
4
9
|
Loki RARV-C loop. This is a PLAN, not code. Every proposed change carries a
|
|
5
10
|
constraint bucket and a bench-gate. No benchmark numbers are asserted here --
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://loki-mode.dev/schemas/code-review.json",
|
|
4
|
+
"title": "Loki Code-Review Verdict",
|
|
5
|
+
"description": "Structured output contract for the 8-gate code-review reviewer (main + Devils-Advocate). Claude Code emits this when invoked with --json-schema (INLINE content, not a path) --output-format json. A python3/TS adapter re-materializes it into the legacy 'VERDICT: X\\nFINDINGS:\\n- [severity] description' text so _classify_verdict, _severity_is_blocking, _count_nonblocking_findings, the mergeability score, the DA arm, and aggregate.json stay byte-identical. verdict is the sole gating token; findings[].severity feeds blocking (Critical/High) and mergeability (Medium/Low) weighting. NOTE: JSON Schema cannot express the cross-field rule 'a Critical/High finding requires verdict FAIL'; the re-materializer enforces it (force-FAIL on any Critical/High finding) so a self-contradictory PASS+Critical can never be waved through as non-blocking.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["verdict", "findings"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"verdict": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"enum": ["PASS", "FAIL"],
|
|
13
|
+
"description": "PASS if no blocking issue; FAIL if a real issue was found. A FAIL with a Critical or High finding blocks the merge; a FAIL with only Medium/Low is a non-blocking dissent. If any finding is Critical or High you MUST set verdict to FAIL."
|
|
14
|
+
},
|
|
15
|
+
"findings": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"minItems": 0,
|
|
18
|
+
"description": "Zero or more issues. Empty array means clean (equivalent to the legacy '- None' bullet).",
|
|
19
|
+
"items": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"additionalProperties": false,
|
|
22
|
+
"required": ["severity", "description"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"severity": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["Critical", "High", "Medium", "Low"],
|
|
27
|
+
"description": "Critical/High are blocking; Medium/Low are advisory and feed the mergeability score."
|
|
28
|
+
},
|
|
29
|
+
"description": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"minLength": 1,
|
|
32
|
+
"maxLength": 2000,
|
|
33
|
+
"description": "One-line issue description, ideally ending with (file:line)."
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://loki-mode.dev/schemas/council-v2.json",
|
|
4
|
+
"title": "Loki Council-v2 Reviewer Verdict",
|
|
5
|
+
"description": "Structured output contract for a council-v2 reviewer (requirements/test/quality/devils-advocate/general). Claude Code emits this when invoked with --json-schema (INLINE content, not a path) --output-format json. The council-v2 dispatch reads structured_output directly; a sed-carve of prose JSON remains the fail-closed fallback. verdict is the gating token; any parse miss defaults to REJECT (the safe direction).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["verdict", "reasoning", "issues"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"verdict": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"enum": ["APPROVE", "REJECT"],
|
|
13
|
+
"description": "APPROVE if the evidence satisfies the requirements for this reviewer's lens; REJECT otherwise."
|
|
14
|
+
},
|
|
15
|
+
"reasoning": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 1,
|
|
18
|
+
"maxLength": 4000,
|
|
19
|
+
"description": "Short justification for the verdict."
|
|
20
|
+
},
|
|
21
|
+
"issues": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"minItems": 0,
|
|
24
|
+
"description": "Zero or more issues found. Empty array is allowed on APPROVE.",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"required": ["severity", "description"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"severity": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": ["CRITICAL", "HIGH", "MEDIUM", "LOW"],
|
|
33
|
+
"description": "Issue severity."
|
|
34
|
+
},
|
|
35
|
+
"description": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"minLength": 1,
|
|
38
|
+
"maxLength": 2000,
|
|
39
|
+
"description": "One-line issue description."
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://loki-mode.dev/schemas/done-recognition.json",
|
|
4
|
+
"title": "Loki Done-Recognition Verdict",
|
|
5
|
+
"description": "Structured output contract for the done-recognition gate (is a reused PRD already satisfied by the current codebase?). Claude Code emits this when invoked with --json-schema (INLINE content, not a path) --output-format json. The gate DEFENSIVELY re-derives the verdict from requirements[].status, never trusting the top-line verdict, so a valid requirements array is the load-bearing part. Any parse miss falls back to the existing json.loads/brace-slice path, which defaults to inconclusive (the safe direction).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["verdict", "summary", "requirements"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"verdict": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"enum": ["done", "incomplete", "inconclusive"],
|
|
13
|
+
"description": "Top-line claim. done ONLY when ALL requirements are met AND fresh tests are green; incomplete when any requirement is unmet; inconclusive when ground truth cannot be established. The gate re-derives this from requirements[].status regardless."
|
|
14
|
+
},
|
|
15
|
+
"summary": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"maxLength": 2000,
|
|
18
|
+
"description": "One plain sentence for the user."
|
|
19
|
+
},
|
|
20
|
+
"tests": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": false,
|
|
23
|
+
"required": ["passed", "total", "green"],
|
|
24
|
+
"properties": {
|
|
25
|
+
"passed": { "type": "integer", "minimum": 0 },
|
|
26
|
+
"total": { "type": "integer", "minimum": 0 },
|
|
27
|
+
"green": { "type": "boolean" }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"requirements": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"minItems": 0,
|
|
33
|
+
"description": "One entry per PRD requirement, with its satisfaction status. The gate re-derives the verdict from these statuses.",
|
|
34
|
+
"items": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"required": ["title", "status"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"id": { "type": "string", "maxLength": 256 },
|
|
40
|
+
"title": { "type": "string", "minLength": 1, "maxLength": 512 },
|
|
41
|
+
"status": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"enum": ["met", "unmet", "uncertain"],
|
|
44
|
+
"description": "met = proven satisfied; unmet = not implemented; uncertain = cannot establish."
|
|
45
|
+
},
|
|
46
|
+
"evidence": { "type": "string", "maxLength": 1024 }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.
|
|
2
|
+
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.129.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function R($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([c1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}Error: jq is required but not installed.${J}
|
|
3
3
|
`),process.stdout.write(`Install with:
|
|
4
4
|
`),process.stdout.write(` brew install jq (macOS)
|
|
5
5
|
`),process.stdout.write(` apt install jq (Debian/Ubuntu)
|
|
@@ -814,4 +814,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
814
814
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
815
815
|
`),process.stderr.write(K8),2}}e1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var SZ=await NZ(Bun.argv.slice(2));process.exit(SZ);
|
|
816
816
|
|
|
817
|
-
//# debugId=
|
|
817
|
+
//# debugId=4C57B2F2D01C30E264756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
3
|
"mcpName": "io.github.asklokesh/loki-mode",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.129.0",
|
|
5
5
|
"description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "loki-mode",
|
|
4
4
|
"displayName": "Loki Mode",
|
|
5
|
-
"version": "7.
|
|
5
|
+
"version": "7.129.0",
|
|
6
6
|
"description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Autonomi",
|