loki-mode 9.65.0 → 9.67.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/completion-council.sh +18 -1
- package/autonomy/lib/done-recognition.sh +12 -0
- package/autonomy/loki +101 -9
- package/autonomy/run.sh +38 -7
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/v10/ARCHITECT-CUTS.md +89 -0
- package/docs/v10/BOARD.md +52 -31
- package/docs/v10/METRICS.md +57 -0
- package/docs/v10/PROGRESS.md +9 -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/web-app/dist/assets/{AdminPage-Oyx-w2Ch.js → AdminPage-ChUvLzkF.js} +1 -1
- package/web-app/dist/assets/{Avatar-D60m65JB.js → Avatar-Dpy96tAc.js} +1 -1
- package/web-app/dist/assets/{Badge-CqfZNaem.js → Badge-BOHHYZTu.js} +1 -1
- package/web-app/dist/assets/{Button-BqjzE4fz.js → Button-B_XqEF7a.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-Czk7o_ou.js → CockpitPage-C0z5QwNJ.js} +1 -1
- package/web-app/dist/assets/{ComparePage-BEfTrTb1.js → ComparePage-KWL6acQk.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-BrLk5nkO.js → ErrorBoundary-BbyBJ4ZP.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-9vEpi5ir.js → EvidenceReceiptPanel-Btblkwfr.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-D6izZx0w.js → GitHubIssuesPanel-7Bi1t04Q.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-BTkG2FgA.js → GitHubPRsPanel-BxZtNprq.js} +1 -1
- package/web-app/dist/assets/{HomePage-nsr4kpN1.js → HomePage-BxDh3-CD.js} +1 -1
- package/web-app/dist/assets/{LoginPage-BVblQrr9.js → LoginPage-DpXq4rQe.js} +1 -1
- package/web-app/dist/assets/{MagicPage-CmnSyLq7.js → MagicPage-DP2k0ioE.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-BVfwU81j.js → MetricsPage-Dus04BUR.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-AYQ6uqqI.js → NotFoundPage--54yr9Cy.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-DMnbw9X9.js → ProjectPage-Cs6LOI7R.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-CNVTAF2N.js → ProjectsPage-5cpa0X1d.js} +1 -1
- package/web-app/dist/assets/SettingsPage-Duc5ApRj.js +10 -0
- package/web-app/dist/assets/{ShowcasePage-2dBCjLXn.js → ShowcasePage-C1wrPZgl.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-Djo6IE4u.js → SystemSettingsPage-CoD3LARj.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-CY8p-yxD.js → TeamsPage-GuQ4YDV5.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-B6iFgH6t.js → TemplatesPage-CvWecmzl.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-B3yzZe-h.js → TerminalOutput-DFZw34kD.js} +1 -1
- package/web-app/dist/assets/{activity-CNfAjtRv.js → activity-BvHkXBYj.js} +1 -1
- package/web-app/dist/assets/{bell-CMETsHPH.js → bell-Bw2j-HI7.js} +1 -1
- package/web-app/dist/assets/{bot-C9kXvk1i.js → bot-BRuEmshQ.js} +1 -1
- package/web-app/dist/assets/{check-BRK-zLB5.js → check-B5uToWdF.js} +1 -1
- package/web-app/dist/assets/{chevron-left-CxI5uD4g.js → chevron-left-D-tXeEPI.js} +1 -1
- package/web-app/dist/assets/{circle-alert-CVFRPWpv.js → circle-alert-p3-Bg3sx.js} +1 -1
- package/web-app/dist/assets/{clock-CLYV8a_x.js → clock-CJc_91vp.js} +1 -1
- package/web-app/dist/assets/{cloud-CiISiIOa.js → cloud-lhmojUN8.js} +1 -1
- package/web-app/dist/assets/{code-xml-DHsgqwV-.js → code-xml-DMvQ2jw1.js} +1 -1
- package/web-app/dist/assets/{database-DbvUqtz3.js → database-EdwkplCN.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-Ct0lRPHw.js → dollar-sign-BQiPo9Co.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-D1sjFEh7.js → file-code-corner-CXUzs2z1.js} +1 -1
- package/web-app/dist/assets/{file-plus-DnmX18Fi.js → file-plus-hS0xkTL5.js} +1 -1
- package/web-app/dist/assets/{globe-bSpDNgoA.js → globe-B6Xk69Fj.js} +1 -1
- package/web-app/dist/assets/{hammer-Bj41Kvlq.js → hammer-B_evoVEr.js} +1 -1
- package/web-app/dist/assets/{index-CxajNJVu.js → index-XZrgDphJ.js} +2 -2
- package/web-app/dist/assets/{layers-BNtBwYER.js → layers-Bb295b4u.js} +1 -1
- package/web-app/dist/assets/{loader-circle-YY_VZofG.js → loader-circle-C8cKZhjP.js} +1 -1
- package/web-app/dist/assets/{lock-rpjEPVoC.js → lock-DcC9B8sE.js} +1 -1
- package/web-app/dist/assets/{package-desXU6XA.js → package-BFz4FYcR.js} +1 -1
- package/web-app/dist/assets/{plus-7AoOU-I9.js → plus-aS4YpFPG.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-oNj-Xn4B.js → refresh-cw-B9_ksndc.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-DeiFjfVO.js → rotate-ccw-CBogZEoT.js} +1 -1
- package/web-app/dist/assets/{scroll-text-Q9AzWiNV.js → scroll-text-DMGgO6uq.js} +1 -1
- package/web-app/dist/assets/{server-DKgVwfek.js → server-CsEqUM8R.js} +1 -1
- package/web-app/dist/assets/{shield-alert-G6x-bS-I.js → shield-alert-Btnj6tmQ.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-Qhk0yvw0.js → thumbs-up-C_NLSPqa.js} +1 -1
- package/web-app/dist/assets/{trash-2-D23gvznl.js → trash-2-zqZc9cSK.js} +1 -1
- package/web-app/dist/assets/{trending-down-CSQClPrr.js → trending-down-BGQh2rVE.js} +1 -1
- package/web-app/dist/assets/{trending-up-sjMSd1wc.js → trending-up-C31TrzUt.js} +1 -1
- package/web-app/dist/assets/{upload-Dchovhld.js → upload-CLwWKjCG.js} +1 -1
- package/web-app/dist/assets/{usePolling-DwLceXwF.js → usePolling-8wwOP53Y.js} +1 -1
- package/web-app/dist/assets/{user-BqZM8CmD.js → user-DUkbxibF.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/web-app/dist/assets/SettingsPage-D1_qasFD.js +0 -10
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 v9.
|
|
6
|
+
# Loki Mode v9.67.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
470
470
|
|
|
471
471
|
---
|
|
472
472
|
|
|
473
|
-
**v9.
|
|
473
|
+
**v9.67.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.
|
|
1
|
+
9.67.0
|
|
@@ -1856,6 +1856,12 @@ _loki_test_provenance() {
|
|
|
1856
1856
|
# legitimate completion is never falsely stopped. Default-on; opt out with
|
|
1857
1857
|
# LOKI_EVIDENCE_GATE=0 (byte-identical to prior behavior, no read/write).
|
|
1858
1858
|
council_evidence_gate() {
|
|
1859
|
+
# P2 (S-116): when the caller sets _LOKI_EVIDENCE_REQUIRE_TESTS=1, inconclusive
|
|
1860
|
+
# test evidence blocks instead of passing through. council_evaluate sets it as
|
|
1861
|
+
# a function-local (bash dynamic scope, gone on return), so the vote alone can
|
|
1862
|
+
# never approve while every other caller keeps the old pass-through rc.
|
|
1863
|
+
local _require_tests="false"
|
|
1864
|
+
[ "${_LOKI_EVIDENCE_REQUIRE_TESTS:-0}" = "1" ] && _require_tests="true"
|
|
1859
1865
|
# Knob first: opt-out is exact-as-today, before any file read or write.
|
|
1860
1866
|
[ "${LOKI_EVIDENCE_GATE:-1}" = "0" ] && return 0
|
|
1861
1867
|
|
|
@@ -2702,6 +2708,15 @@ PYEOF
|
|
|
2702
2708
|
# silently. The durable detail is in evidence-gate-details.json; this is
|
|
2703
2709
|
# the human-visible honesty at the pass site.
|
|
2704
2710
|
if [ "$test_inconclusive" = "true" ]; then
|
|
2711
|
+
# Tautological provenance is excluded: tests DID run green (e.g.
|
|
2712
|
+
# characterization tests pass on the base by design), and the
|
|
2713
|
+
# provenance block above promises that downgrade never blocks.
|
|
2714
|
+
if [ "$_require_tests" = "true" ] && [ "$test_inconclusive_reason" != "test_provenance_unconfirmed" ]; then
|
|
2715
|
+
# Council route: a vote over inconclusive tests is the vote alone.
|
|
2716
|
+
log_warn "[Council] Evidence gate: completion not backed by test evidence (${test_inconclusive_reason}); the council vote alone cannot approve. Record a real test run, or set LOKI_EVIDENCE_NO_TESTS_AFFIRMATIVE=1 to treat no-tests as affirmative."
|
|
2717
|
+
_write_evidence_details "block"
|
|
2718
|
+
return 1
|
|
2719
|
+
fi
|
|
2705
2720
|
log_warn "[Council] Evidence gate: completion not backed by test evidence (${test_inconclusive_reason}). Pass-through; set LOKI_EVIDENCE_NO_TESTS_AFFIRMATIVE=1 to treat no-tests as affirmative."
|
|
2706
2721
|
fi
|
|
2707
2722
|
# Same honesty for the runtime-boot axis: a pass that could not confirm the
|
|
@@ -4176,7 +4191,9 @@ council_evaluate() {
|
|
|
4176
4191
|
fi
|
|
4177
4192
|
|
|
4178
4193
|
# Phase 2.5 (v7.19.1): evidence hard gate - block completion unless there is
|
|
4179
|
-
# real evidence that files changed AND tests are green.
|
|
4194
|
+
# real evidence that files changed AND tests are green. The local below makes
|
|
4195
|
+
# an inconclusive test signal block here, so the vote never approves alone.
|
|
4196
|
+
local _LOKI_EVIDENCE_REQUIRE_TESTS=1
|
|
4180
4197
|
if ! council_evidence_gate; then
|
|
4181
4198
|
log_info "[Council] Completion blocked by evidence hard gate"
|
|
4182
4199
|
return 1 # CONTINUE - cannot complete without real evidence
|
|
@@ -461,8 +461,20 @@ def tests_axis(path):
|
|
|
461
461
|
if ec != 0:
|
|
462
462
|
return "red"
|
|
463
463
|
# exit 0 alone is green only if nothing else contradicts it (checked below).
|
|
464
|
+
def _nonzero_int(v):
|
|
465
|
+
return isinstance(v, int) and not isinstance(v, bool) and v != 0
|
|
466
|
+
# Zero-test record: nothing actually ran, regardless of self-reported
|
|
467
|
+
# pass/fail. Checked before the pass:true/false branches below so a
|
|
468
|
+
# plausible {"pass": true, "total": 0} shape cannot slip through the
|
|
469
|
+
# pass:true branch as a false green.
|
|
470
|
+
if isinstance(d.get("total"), int) and d.get("total") == 0:
|
|
471
|
+
return "unknown"
|
|
464
472
|
p = d.get("pass")
|
|
465
473
|
if p is True:
|
|
474
|
+
# A self-reported pass contradicted by a nonzero failure count (under
|
|
475
|
+
# either key production/legacy shapes use) is not authoritative green.
|
|
476
|
+
if _nonzero_int(d.get("failed")) or _nonzero_int(d.get("failed_count")):
|
|
477
|
+
return "red"
|
|
466
478
|
# A clean pass:true with no contradicting red signal is authoritative green.
|
|
467
479
|
if d.get("failed") in (None, 0) and (ec in (None, 0)):
|
|
468
480
|
return "green"
|
package/autonomy/loki
CHANGED
|
@@ -856,8 +856,48 @@ PY
|
|
|
856
856
|
# absent measurement is not evidence, and inventing a PASS here would be worse
|
|
857
857
|
# than inventing a FAIL.
|
|
858
858
|
loki_remote_attestation_status() {
|
|
859
|
-
local pj="$1" url="$2"
|
|
859
|
+
local pj="$1" url="$2" _jh
|
|
860
860
|
[ -n "$url" ] || { printf ''; return 0; }
|
|
861
|
+
# A JWKS key set travels over the same channel as the receipt it certifies.
|
|
862
|
+
# Plain http lets anyone on the network path swap in their own key set, and
|
|
863
|
+
# the attestation would then "verify" against a forged key -- a MITM the
|
|
864
|
+
# TLS the API already uses is meant to prevent. Only https is trusted;
|
|
865
|
+
# loopback http and file:// are the exceptions, because neither one leaves
|
|
866
|
+
# this machine for anyone to sit on. Checked here, before python is even
|
|
867
|
+
# invoked, so a rejected URL is never fetched -- not "fetched and then
|
|
868
|
+
# distrusted". Same check in loki_proof_attestation_check below -- change
|
|
869
|
+
# both.
|
|
870
|
+
#
|
|
871
|
+
# The host is bounded exactly (up to the first "/", then stripped of any
|
|
872
|
+
# port) rather than matched with a trailing glob, and a userinfo "@" is
|
|
873
|
+
# rejected outright -- "http://127.0.0.1:80@evil.com" is host evil.com to
|
|
874
|
+
# any real URL parser (userinfo before the @, host after), and a loose
|
|
875
|
+
# `http://127.0.0.1:*` glob would wrongly wave that straight through.
|
|
876
|
+
case "$url" in
|
|
877
|
+
https://*|file://*) ;;
|
|
878
|
+
http://*)
|
|
879
|
+
_jh="${url#http://}"; _jh="${_jh%%/*}"
|
|
880
|
+
case "$_jh" in
|
|
881
|
+
*@*) printf ''; return 0 ;;
|
|
882
|
+
esac
|
|
883
|
+
# A bracketed IPv6 literal must be the WHOLE host: "[::1]" or
|
|
884
|
+
# "[::1]:port", never a prefix, or "[::1].evil.com" (not itself
|
|
885
|
+
# resolvable as loopback, but not rejected either) would parse as
|
|
886
|
+
# loopback "::1" with everything after the first "]" silently
|
|
887
|
+
# dropped instead of validated.
|
|
888
|
+
case "$_jh" in
|
|
889
|
+
\[*\]) _jh="${_jh#\[}"; _jh="${_jh%\]}" ;;
|
|
890
|
+
\[*\]:*) _jh="${_jh#\[}"; _jh="${_jh%%\]:*}" ;;
|
|
891
|
+
\[*) printf ''; return 0 ;;
|
|
892
|
+
*) _jh="${_jh%%:*}" ;;
|
|
893
|
+
esac
|
|
894
|
+
case "$_jh" in
|
|
895
|
+
127.0.0.1|localhost|::1) ;;
|
|
896
|
+
*) printf ''; return 0 ;;
|
|
897
|
+
esac
|
|
898
|
+
;;
|
|
899
|
+
*) printf ''; return 0 ;;
|
|
900
|
+
esac
|
|
861
901
|
command -v python3 >/dev/null 2>&1 || { printf ''; return 0; }
|
|
862
902
|
local tok
|
|
863
903
|
# jq's `//` treats JSON false the same as null/missing, which is exactly
|
|
@@ -954,8 +994,53 @@ PY
|
|
|
954
994
|
# testing found exactly this: a mutation applied to the first copy left every
|
|
955
995
|
# assertion of the second suite green.
|
|
956
996
|
loki_proof_attestation_check() {
|
|
957
|
-
local pj="$1" src="$2"
|
|
997
|
+
local pj="$1" src="$2" _jh
|
|
958
998
|
[ -n "$src" ] || { printf ''; return 0; }
|
|
999
|
+
# Same MITM guard as loki_remote_attestation_status above -- change both.
|
|
1000
|
+
# Only applies to a URL source; a local file (the third-party offline case)
|
|
1001
|
+
# never travels a network and needs no scheme at all. The host is bounded
|
|
1002
|
+
# exactly and a userinfo "@" is rejected outright -- see the sibling
|
|
1003
|
+
# function's comment for why a trailing glob is not safe here.
|
|
1004
|
+
#
|
|
1005
|
+
# A recognized scheme prefix is checked FIRST, before any "-f" test: the
|
|
1006
|
+
# process cwd is the checkout being verified (attacker-controlled), and a
|
|
1007
|
+
# repo can ship a file whose literal name IS a URL string (for example
|
|
1008
|
+
# "https:/evil.example.com/.well-known/jwks.json" as a real path on disk).
|
|
1009
|
+
# Testing "-f" first would treat that shadow file as the local-file form
|
|
1010
|
+
# and hand its content straight to the caller with no network involved at
|
|
1011
|
+
# all -- silently defeating the "only https is trusted" guarantee this
|
|
1012
|
+
# function exists to provide. Scheme detection must win over the
|
|
1013
|
+
# filesystem every time, not just when no file happens to collide.
|
|
1014
|
+
case "$src" in
|
|
1015
|
+
https://*|http://*|file://*)
|
|
1016
|
+
case "$src" in
|
|
1017
|
+
https://*|file://*) ;;
|
|
1018
|
+
http://*)
|
|
1019
|
+
_jh="${src#http://}"; _jh="${_jh%%/*}"
|
|
1020
|
+
case "$_jh" in
|
|
1021
|
+
*@*) printf ''; return 0 ;;
|
|
1022
|
+
esac
|
|
1023
|
+
# See loki_remote_attestation_status above for why the
|
|
1024
|
+
# bracketed-IPv6 match must bound the closing bracket.
|
|
1025
|
+
case "$_jh" in
|
|
1026
|
+
\[*\]) _jh="${_jh#\[}"; _jh="${_jh%\]}" ;;
|
|
1027
|
+
\[*\]:*) _jh="${_jh#\[}"; _jh="${_jh%%\]:*}" ;;
|
|
1028
|
+
\[*) printf ''; return 0 ;;
|
|
1029
|
+
*) _jh="${_jh%%:*}" ;;
|
|
1030
|
+
esac
|
|
1031
|
+
case "$_jh" in
|
|
1032
|
+
127.0.0.1|localhost|::1) ;;
|
|
1033
|
+
*) printf ''; return 0 ;;
|
|
1034
|
+
esac
|
|
1035
|
+
;;
|
|
1036
|
+
esac
|
|
1037
|
+
;;
|
|
1038
|
+
*)
|
|
1039
|
+
# A plain path is decided in python below, which reports an
|
|
1040
|
+
# unsigned receipt as "none" BEFORE it looks at the key file, so a
|
|
1041
|
+
# missing key file on an unsigned receipt still fails (exit 1).
|
|
1042
|
+
;;
|
|
1043
|
+
esac
|
|
959
1044
|
command -v python3 >/dev/null 2>&1 || { printf ''; return 0; }
|
|
960
1045
|
python3 -E - "$pj" "$src" "${_LOKI_SCRIPT_DIR}" <<'PY' 2>/dev/null || printf ''
|
|
961
1046
|
# The cwd is the checkout being verified: drop it from sys.path before any
|
|
@@ -984,15 +1069,20 @@ if tok is None or tok == "" or tok is False:
|
|
|
984
1069
|
print("none"); sys.exit(0) # a FACT about the receipt, not an error
|
|
985
1070
|
src = sys.argv[2]
|
|
986
1071
|
try:
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1072
|
+
# Scheme check first, same as the bash gate above -- a "-f" test run
|
|
1073
|
+
# first would let a local file whose literal name IS an http(s) URL
|
|
1074
|
+
# string (the process cwd is the checkout being verified) silently stand
|
|
1075
|
+
# in for a real fetch. A recognized scheme always means "URL", even if a
|
|
1076
|
+
# same-named file happens to exist on disk.
|
|
1077
|
+
if src.startswith(("http://", "https://")):
|
|
992
1078
|
url = src if src.rstrip("/").endswith("jwks.json") else \
|
|
993
1079
|
src.rstrip("/") + "/.well-known/jwks.json"
|
|
994
1080
|
with urllib.request.urlopen(url, timeout=10) as r:
|
|
995
1081
|
jwks = json.loads(r.read())
|
|
1082
|
+
elif os.path.isfile(src):
|
|
1083
|
+
jwks = json.load(open(src))
|
|
1084
|
+
else:
|
|
1085
|
+
sys.exit(1)
|
|
996
1086
|
except Exception:
|
|
997
1087
|
sys.exit(1) # "" : unreachable/unreadable, no verdict
|
|
998
1088
|
# A key set that is not {"keys": [{...}, ...]} -- INCLUDING an empty list -- is
|
|
@@ -1177,7 +1267,8 @@ loki_remote_verify_receipt() {
|
|
|
1177
1267
|
# gpg-unavailable branch above: NOT CHECKED, return 0, provenance unproven.
|
|
1178
1268
|
if [ -z "$_attest" ] && jq -e '.verification.attestation // empty' "$pj" >/dev/null 2>&1; then
|
|
1179
1269
|
echo -e " ${YELLOW}NOT CHECKED${NC}: the receipt carries an attestation, but it could not be checked"
|
|
1180
|
-
echo " against ${_attest_url:-<no url>} (JWKS unreachable,
|
|
1270
|
+
echo " against ${_attest_url:-<no url>} (JWKS unreachable, python cryptography missing, or"
|
|
1271
|
+
echo " the JWKS URL is not https -- only loopback is allowed over plain http)."
|
|
1181
1272
|
echo " Its integrity hash matches. Provenance is unproven here, not disproven."
|
|
1182
1273
|
echo " Install python3 cryptography, or re-run where the build server is reachable."
|
|
1183
1274
|
return 0
|
|
@@ -37014,7 +37105,8 @@ print(json.dumps(json.load(open(os.environ['LOKI_PROOF_JSON'])), indent=2))
|
|
|
37014
37105
|
# read as either a pass or an accusation, so it exits 2
|
|
37015
37106
|
# (could not check). A tamper/drift 1 from the base
|
|
37016
37107
|
# verifier is a finding and is never softened to 2.
|
|
37017
|
-
echo -e "${YELLOW}attestation: NOT CHECKED -- could not check against ${_pv_jwks} (key set unreadable
|
|
37108
|
+
echo -e "${YELLOW}attestation: NOT CHECKED -- could not check against ${_pv_jwks} (key set unreadable,${NC}" >&2
|
|
37109
|
+
echo -e "${YELLOW} verifier dependency missing, or the JWKS URL is not https -- only loopback is allowed over plain http).${NC}" >&2
|
|
37018
37110
|
echo " This is not a failure of the receipt. Nothing was proven either way (exit 2)." >&2
|
|
37019
37111
|
if [ "$_pv_rc" -eq 0 ]; then _pv_rc=2; fi
|
|
37020
37112
|
_pv_attestation_field="NOT CHECKED"
|
package/autonomy/run.sh
CHANGED
|
@@ -5911,6 +5911,27 @@ _loki_trusted_push() {
|
|
|
5911
5911
|
log_warn "Not pushing branch '$_branch': origin changed during the run (it was $(_loki_github_repo_from_url "$_LOKI_PINNED_ORIGIN" 2>/dev/null || echo "not a GitHub repository") when the run started). Loki does not push to a destination the agent could have chosen."
|
|
5912
5912
|
return 2
|
|
5913
5913
|
fi
|
|
5914
|
+
# S-100: never push a default branch, for every caller (on_run_complete,
|
|
5915
|
+
# create_session_pr): the branch name can come from agent-writable state.
|
|
5916
|
+
# The default branch is resolved for the origin just validated (the pinned
|
|
5917
|
+
# one when pinned), gh run from / with an explicit OWNER/REPO. An
|
|
5918
|
+
# unresolvable default branch refuses: fail closed.
|
|
5919
|
+
local _repo _def=""
|
|
5920
|
+
case "$_branch" in
|
|
5921
|
+
main|master|HEAD)
|
|
5922
|
+
log_warn "Not pushing branch '$_branch': Loki never pushes directly to a default branch."
|
|
5923
|
+
return 2 ;;
|
|
5924
|
+
esac
|
|
5925
|
+
_repo="$(_loki_github_repo_from_url "$_url")"
|
|
5926
|
+
_def="$(_loki_run_neutral "$_repo" "$_runner" gh repo view "$_repo" --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null)" || _def=""
|
|
5927
|
+
if [ -z "$_def" ]; then
|
|
5928
|
+
log_warn "Not pushing branch '$_branch': could not resolve the default branch of $_repo, and Loki never risks a direct push to it."
|
|
5929
|
+
return 2
|
|
5930
|
+
fi
|
|
5931
|
+
if [ "$_branch" = "$_def" ]; then
|
|
5932
|
+
log_warn "Not pushing branch '$_branch': it is the default branch of $_repo, and Loki never pushes directly to the default branch."
|
|
5933
|
+
return 2
|
|
5934
|
+
fi
|
|
5914
5935
|
_tmp="$(mktemp -d "${TMPDIR:-/tmp}/loki-push.XXXXXX")" || return 1
|
|
5915
5936
|
# --update-shallow: a shallow agent repo's history ends in shallow roots,
|
|
5916
5937
|
# which a plain fetch rejects while still exiting 0. The rev-parse makes a
|
|
@@ -6070,6 +6091,8 @@ on_run_complete() {
|
|
|
6070
6091
|
case "$branch" in
|
|
6071
6092
|
""|main|master|HEAD) return 0 ;;
|
|
6072
6093
|
esac
|
|
6094
|
+
# S-100: the repository's ACTUAL default branch (develop, trunk, ...) is
|
|
6095
|
+
# refused inside _loki_trusted_push, for every caller.
|
|
6073
6096
|
log_info "LOKI_DELEGATE_PR=1: opening a local pull request for branch '$branch'..."
|
|
6074
6097
|
# Push, then create. Non-interactive (no tty in --bg). Best-effort, each
|
|
6075
6098
|
# network call bounded by the timeout guard above.
|
|
@@ -21516,9 +21539,14 @@ except (json.JSONDecodeError, KeyError, TypeError, OSError):
|
|
|
21516
21539
|
mv "$state_file" "${state_file}.corrupt.$(date +%s)" 2>/dev/null || true
|
|
21517
21540
|
# Starts at iteration 0: drop the previous session's test
|
|
21518
21541
|
# evidence, as the iteration-0 block at the end does.
|
|
21542
|
+
# S-124 follow-up: static-analysis.pass has no freshness
|
|
21543
|
+
# marker of its own (same reasoning as the block below), so
|
|
21544
|
+
# this corrupted-state restart must drop it too, or a stale
|
|
21545
|
+
# copy survives this sibling iteration-0 trigger.
|
|
21519
21546
|
rm -f "${TARGET_DIR:-.}/.loki/quality/.test-results.iter" \
|
|
21520
21547
|
"${TARGET_DIR:-.}/.loki/quality/unit-tests.pass" \
|
|
21521
|
-
"${TARGET_DIR:-.}/.loki/quality/test-results.json"
|
|
21548
|
+
"${TARGET_DIR:-.}/.loki/quality/test-results.json" \
|
|
21549
|
+
"${TARGET_DIR:-.}/.loki/quality/static-analysis.pass" 2>/dev/null || true
|
|
21522
21550
|
return
|
|
21523
21551
|
fi
|
|
21524
21552
|
|
|
@@ -21589,16 +21617,19 @@ except (json.JSONDecodeError, KeyError, TypeError, OSError):
|
|
|
21589
21617
|
RETRY_COUNT=0
|
|
21590
21618
|
fi
|
|
21591
21619
|
# A session that starts at iteration 0 has run nothing yet, so a leftover
|
|
21592
|
-
# freshness marker
|
|
21593
|
-
# Iterations restart at 0, so keeping them would let
|
|
21594
|
-
# as this iteration's result.
|
|
21620
|
+
# freshness marker, unit-tests.pass, or static-analysis.pass is a previous
|
|
21621
|
+
# session's evidence. Iterations restart at 0, so keeping them would let
|
|
21622
|
+
# an old pass:true read as this iteration's result.
|
|
21595
21623
|
if [ "${ITERATION_COUNT:-0}" = "0" ]; then
|
|
21596
|
-
# Same path the writer (enforce_test_coverage)
|
|
21597
|
-
# readers use.
|
|
21624
|
+
# Same path the writer (enforce_test_coverage, enforce_static_analysis)
|
|
21625
|
+
# and the freshness readers use.
|
|
21598
21626
|
local _q="${TARGET_DIR:-.}/.loki/quality"
|
|
21599
21627
|
# test-results.json too: with the marker gone the receipt's quality
|
|
21600
21628
|
# gates fall back to its status, reporting a previous session's run.
|
|
21601
|
-
|
|
21629
|
+
# static-analysis.pass has no freshness marker of its own, so a stale
|
|
21630
|
+
# copy from a previous session reads as a pass of this session's
|
|
21631
|
+
# exogenous static_analysis gate unless it is dropped here too.
|
|
21632
|
+
rm -f "$_q/.test-results.iter" "$_q/unit-tests.pass" "$_q/test-results.json" "$_q/static-analysis.pass" 2>/dev/null || true
|
|
21602
21633
|
fi
|
|
21603
21634
|
}
|
|
21604
21635
|
|
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:** v9.
|
|
5
|
+
**Version:** v9.67.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Architect slice cuts (2026-09-27)
|
|
2
|
+
|
|
3
|
+
Verbatim final reports from the two Architect agents, kept so BOARD rows S-112..S-151 can cite a durable source.
|
|
4
|
+
|
|
5
|
+
## 17:10Z cut: S-112..S-131
|
|
6
|
+
|
|
7
|
+
Here are 20 new slices, S-112 to S-131. I edited no files. Each red condition below comes from reading or grepping the current source. I did not run any failing command to confirm a red.
|
|
8
|
+
|
|
9
|
+
Two rules apply to every moat row. First, the Wall check is the single moat script's `CASE ... PASS` line, not `tests/moat/run.sh`. `pending.txt` is a Captain-owned hot file, and `run.sh` fails the suite when a case that is still listed there passes. So `run.sh` is expected to go red on exactly the named case ID, and the Captain deletes that pending line at merge. Second, any new test file is registered in `tests/run-all-tests.sh` by the Captain, because that file is hot too.
|
|
10
|
+
|
|
11
|
+
| S-112 | P8 pending (P8.ablation-runtime-change-load-bearing, P8.ablation-na-docs-only): build the ablation producer to the interface in the p8 script header, writing `.loki/quality/ablation.json` with status load_bearing, not_load_bearing or not_applicable | autonomy/lib/ablation.py (new), tests/test-ablation.sh (new) | HIGH | red: `python3 autonomy/lib/ablation.py --help` fails today (file absent); green: on a fixture repo whose check exercises the changed function, ablation.json has status load_bearing; a docs-only change gives not_applicable with a non-empty reason; the working tree is byte-identical after the run, and after a SIGTERM sent mid-ablation (git status clean, sha256 of every file unchanged) | ready@2026-09-27T17:10Z | It rewrites the user's working tree in place (data-risk class), so restore on signal is required. Contract: tests/moat/p8-load-bearing-proof.sh lines 22-50. The P8 cases go green only once both S-112 and S-113 are merged. Neither half flips a case alone. Do not promote until a wiring slice lands (BACKLOG 34). |
|
|
12
|
+
| S-113 | P8 pending (P8.ablation-dead-code-not-sealed): proof-generator copies ablation.json verbatim into facts.ablation, and _compute_headline never returns VERIFIED when status is not_load_bearing | autonomy/lib/proof-generator.py (the facts.ablation collector and _compute_headline only), tests/test-proof-ablation-headline.sh (new) | HIGH | red: on a fixture .loki with a hand-written ablation.json of status not_load_bearing and otherwise green evidence, the generated proof.json headline reads VERIFIED; green: facts.ablation equals the file's keys and the headline is not VERIFIED; an absent ablation.json leaves today's headline unchanged | ready@2026-09-27T17:10Z | Seal work. Independent of S-112 because it reads the file only. Other slices that touched proof-generator.py (S-52, S-59, S-60) are all released. Same BACKLOG 34 caveat: not wired into run.sh. |
|
|
13
|
+
| S-114 | P3 pending (P3.check-author-context-excludes-implementation): build `check_author.py context --spec --repo` to interface A, printing spec_sha256, inputs and prompt, with the spec as the only repo input | autonomy/lib/check_author.py (new), tests/test-check-author-context.sh (new) | HIGH | red: `bash tests/moat/p3-the-wall.sh` emits `CASE P3.check-author-context-excludes-implementation FAIL` today; green: the same script emits PASS, and a fixture repo with src/app.py never appears in inputs or prompt | ready@2026-09-27T17:10Z | Contract: tests/moat/p3-the-wall.sh interface A (~lines 70-85). The script is present but not invoked by the pipeline, so do not promote until a wiring slice exists (BACKLOG 34). |
|
|
14
|
+
| S-115 | P3 pending (P3.checks-frozen-before-eval): add `checklist-verify.py --freeze`, which records a digest of check definitions only; a later verify on edited definitions exits non-zero with "hash mismatch", and the verifier's own write-back never trips the digest | autonomy/checklist-verify.py, tests/test-checklist-freeze.sh (new) | HIGH | red: `python3 autonomy/checklist-verify.py --checklist x --freeze` is an unknown flag today, and p3-the-wall.sh emits `CASE P3.checks-frozen-before-eval FAIL`; green: that case emits PASS; freezing then verifying twice in a row exits 0 both times; editing one verification spec gives a non-zero exit with "hash mismatch" | ready@2026-09-27T17:10Z | Contract: interface B in the p3 header. Nothing in the pipeline calls --freeze yet (BACKLOG 34 caveat). |
|
|
15
|
+
| S-116 | P2 pending (P2.council-inconclusive-cannot-exit-zero, BACKLOG 4): council_evidence_gate must not let inconclusive evidence through, and council_evaluate must not approve on the vote alone | autonomy/completion-council.sh (council_evidence_gate ~1858 and council_evaluate ~4154 only), tests/test-council-inconclusive-no-approve.sh (new) | HIGH | red: `bash tests/moat/p2-honest-verdict.sh` emits `CASE P2.council-inconclusive-cannot-exit-zero FAIL`; green: that case emits PASS; a genuine green-evidence fixture still approves (positive control); every other P2 case is unchanged | ready@2026-09-27T17:10Z | Honest-verdict moat. The case looks bash-only (case_council_inconclusive has no loki_route call). If the builder finds a Bun leg, return BLOCKED and do not touch council.ts. The S-49 and S-17 edits to this file are released. |
|
|
16
|
+
| S-117 | P4 pending (P4.seeded-defect-corpus-present, BACKLOG 6 and 9), part 1 of 5: 20 logic-bug seeded defects to the manifest contract at p4-model-freedom.sh lines 65-84 | benchmarks/seeded-defects/cases/SD-0001 to SD-0020 (new), benchmarks/seeded-defects/fragments/logic-bug.json (new) | MEDIUM | red: benchmarks/seeded-defects does not exist; green: the fragment lists 20 entries of category logic-bug with expected_verdict NOT_SEALED; each deterministic check exits non-zero on its fixture (the defect is real); no two fixtures are byte-identical, and a renamed copy of one defect does not count as a new one | ready@2026-09-27T17:10Z | ID ranges are pre-assigned so the five corpus builders cannot collide. The moat script's corpus.py checker (line ~361) is the acceptance oracle once S-122 assembles the manifest. |
|
|
17
|
+
| S-118 | P4 corpus part 2 of 5: 20 spec-miss seeded defects | benchmarks/seeded-defects/cases/SD-0021 to SD-0040 (new), benchmarks/seeded-defects/fragments/spec-miss.json (new) | MEDIUM | red: fragment absent; green: 20 spec-miss entries, each fixture with a spec plus code that misses it and a check that fails; unique and non-duplicate as in S-117 | ready@2026-09-27T17:10Z | Same contract as S-117. |
|
|
18
|
+
| S-119 | P4 corpus part 3 of 5: 20 test-fitting seeded defects (tests pass by being fitted to wrong code) | benchmarks/seeded-defects/cases/SD-0041 to SD-0060 (new), benchmarks/seeded-defects/fragments/test-fitting.json (new) | MEDIUM | red: fragment absent; green: 20 test-fitting entries; each fixture's own tests pass while a held-out check named in "check" fails; verdict_path set honestly (model where no deterministic check exists) | ready@2026-09-27T17:10Z | Same contract. This is the hardest category to make deterministic. |
|
|
19
|
+
| S-120 | P4 corpus part 4 of 5: 20 mock-abuse seeded defects | benchmarks/seeded-defects/cases/SD-0061 to SD-0080 (new), benchmarks/seeded-defects/fragments/mock-abuse.json (new) | MEDIUM | red: fragment absent; green: 20 mock-abuse entries, each with a mock that hides the real defect and a check that exposes it | ready@2026-09-27T17:10Z | Same contract. |
|
|
20
|
+
| S-121 | P4 corpus part 5 of 5: 20 security seeded defects (hermetic, no network) | benchmarks/seeded-defects/cases/SD-0081 to SD-0100 (new), benchmarks/seeded-defects/fragments/security.json (new) | MEDIUM | red: fragment absent; green: 20 security entries (injection, path traversal, secret in code and similar), each with a check that fails on the fixture, no live exploit and no network | ready@2026-09-27T17:10Z | Same contract. |
|
|
21
|
+
| S-122 | P4 corpus assembler: build_manifest.py merges fragments/*.json into manifest.json with schema loki.seeded-defects/v2 and fails on a duplicate id, a missing category or fewer than 100 defects | benchmarks/seeded-defects/build_manifest.py (new), tests/test-seeded-defects-manifest.sh (new) | LOW | red: script absent; green: on synthetic fragments it writes a valid manifest; a duplicate id, a missing category or 99 defects each exits non-zero with the reason | ready@2026-09-27T17:10Z | Buildable now against fixtures. The Captain runs it once S-117 to S-121 merge, and only then does `CASE P4.seeded-defect-corpus-present PASS` appear. P4.floor-does-not-raise-wrong-pass still needs a real measurement run that no slice covers. |
|
|
22
|
+
| S-123 | BACKLOG 20: loki_proof_attestation_check accepts plain http:// JWKS URLs (a MITM controls the key set). Require https, allowing only loopback http | autonomy/loki (loki_proof_attestation_check ~956 and loki_remote_attestation_status, same guard), tests/test-jwks-https-only.sh (new) | MEDIUM | red: `loki proof verify <receipt> --jwks http://example.invalid` attempts the fetch today (source line ~990 accepts http://); green: a non-loopback http:// JWKS reads NOT CHECKED with a reason naming https, without fetching; http://127.0.0.1 still works, so tests/test-remote-attestation-verdict.sh stays green | ready@2026-09-27T17:10Z | Security. The Bun route forwards --jwks to bash (loki-ts/src/commands/proof.ts:646), so there is no TS change. This is the only slice on autonomy/loki. |
|
|
23
|
+
| S-124 | BACKLOG 79: a previous session's static-analysis.pass survives iteration 0 and reads as a pass of the exogenous static_analysis gate | autonomy/run.sh (the iteration-0 evidence drop at ~21595 only), tests/test-iteration0-drops-static-analysis.sh (new) | LOW | red: with .loki/quality/static-analysis.pass present and ITERATION_COUNT=0, the file survives the drop block today (it removes only .test-results.iter, unit-tests.pass and test-results.json); green: it is removed, and a mid-session (iteration above 0) static-analysis.pass is kept | ready@2026-09-27T17:10Z | Honest verdict. It does not touch S-100's trusted-push area. This is the only slice on run.sh. |
|
|
24
|
+
| S-125 | BACKLOG 76 and 89: done-recognition tests_axis reads a zero-test record ({"passed":0,"failed":0,"total":0}) as green via the failed==0 branch, and ignores failed_count on a pass:true record | autonomy/lib/done-recognition.sh (tests_axis classifier ~434-485 only), tests/test-done-recognition-tests-axis.sh (new) | MEDIUM | red: the zero-test record and {"pass":true,"failed_count":2} each classify green today; green: they classify unknown and red respectively; a real {"pass":true,"exit_code":0,"failed_count":0} stays green | ready@2026-09-27T17:10Z | Honest verdict. Current source read confirms the fall-through at the "failed == 0" line. |
|
|
25
|
+
| S-126 | BACKLOG 94 part a: test_all_data_gets_scoped walks server.app.routes, which on FastAPI 0.141 and later never lists the v2 or operator routes, so its v2 coverage is vacuous on CI | tests/dashboard/test_all_data_gets_scoped.py | MEDIUM | red: under FastAPI 0.141 or later, the audited route set contains zero /api/v2 paths while app.openapi() lists them; green: the route set comes from the app router in the way the P7 matcher does it, asserts at least one /api/v2 route was audited, and passes on 0.128 and 0.141 | ready@2026-09-27T17:10Z | Local FastAPI is 0.128.0, so the red must be run on a 0.141 venv (requirements-test.txt resolves it). Avoid vacuity: assert the count is non-zero. |
|
|
26
|
+
| S-127 | BACKLOG 94 part b: the same vacuous app.routes walk in test_fleet_observability (~171), test_fleet_retry (~414) and test_memory_read_scope_auth (~94) | tests/dashboard/test_fleet_observability.py, tests/dashboard/test_fleet_retry.py, tests/dashboard/test_memory_read_scope_auth.py | MEDIUM | red: under FastAPI 0.141 or later, each walk sees zero v2 or operator routes; green: each asserts a non-zero count of the route class it audits and passes on both versions | ready@2026-09-27T17:10Z | Reuse the approach from S-126, but do not edit that file. Running in parallel is fine. |
|
|
27
|
+
| S-128 | BACKLOG 148: moat baseline detection (read_baselines, unreached_release) cannot see a symlinked pending.txt, because git grep -l and -L both return rc 1 on a mode-120000 blob | tests/moat/run.sh (read_baselines and unreached_release only), tests/test-moat-runner.sh | MEDIUM | red: a fixture repo with a release tag whose tests/moat/pending.txt is a symlink is treated as carrying no baseline (bootstrap), so it is unratcheted; green: that tag is either read as a baseline or refused outright with a named reason; every existing test-moat-runner.sh case stays green | ready@2026-09-27T17:10Z | Moat integrity. It does not touch pending.txt or cases.txt. |
|
|
28
|
+
| S-129 | BACKLOG 93: the "zero-exit leader" case in test-hard-deadline-confinement.sh waits only 0.2s for the child PID file, so under CI load the PID is empty and the case fails on correct product behaviour | tests/test-hard-deadline-confinement.sh | LOW | red: the source has `deadline = time.monotonic() + 0.2` (~line 199), and the case fails when run under stress (for example 4 parallel copies with a CPU hog); green: a bounded wait of 10s or more for the PID file, 20/20 green under the same stress, and the rc 125 plus no-mutation assertions unchanged | ready@2026-09-27T17:10Z | Load-flake fix only. Do not weaken the fail-closed assertion. |
|
|
29
|
+
| S-130 | S-51 follow-up (BACKLOG 117 remainder): SettingsPage lists Gemini as an active provider (deprecated and removed from the runtime in v7.5.18), and its build date is hardcoded to 2026-03-24 | web-app/src/pages/SettingsPage.tsx | LOW | red: `grep -n "gemini" web-app/src/pages/SettingsPage.tsx` shows a provider entry (~108) and the default providerPriority (~220), and the file contains the literal 2026-03-24 (~1003); green: Gemini is removed from PROVIDERS and the default priority (or marked deprecated and not selectable), the build date comes from build metadata or is dropped, and the web-app typecheck passes | ready@2026-09-27T17:10Z | User-visible. The Captain rebuilds web-app/dist. opencode, missing from the list, can be added the same way if trivial. |
|
|
30
|
+
| S-131 | BACKLOG 106 (Bun half): the budget breaker treats unmeasured spend as $0 and never warns; warn (never pause) when a cap is set and spend is unmeasured | loki-ts/src/runner/budget.ts, loki-ts/tests/runner/budget.test.ts | MEDIUM | red: a unit test with a cap of $10 and zero measured iterations gets budget_used 0 and no warning; green: the state marks spend as unmeasured and a warning is emitted, while exceeded stays false and nothing pauses; measured runs are unchanged | ready@2026-09-27T17:10Z | P7-class honesty. The bash half (autonomy/loki cost and check_budget_limit) is a later slice, because autonomy/loki is taken by S-123 and run.sh by S-124. The Captain rebuilds loki-ts/dist. |
|
|
31
|
+
|
|
32
|
+
Notes:
|
|
33
|
+
1. The task premise is stale: tests/moat/pending.txt holds 13 entries, not 38. Two of them I did not slice, because they need a CEO decision (both are breaking changes deferred to v10.0.0): P2.fast-verify-inconclusive-not-zero, which is also dispatched through autonomy/loki and so collides with S-123, and P2.verify-exit-contract. P1.verification-metadata-signed is Seal work bigger than 30 minutes.
|
|
34
|
+
2. Risky: S-112 rewrites the user's working tree in place. S-112 to S-115 would promote P3 and P8 as present but not wired (BACKLOG 34 says do not promote until a stub-provider run.sh session records facts.ablation and invokes check-author), so a wiring slice on run.sh has to follow S-124.
|
|
35
|
+
3. CEO decisions: whether to spend 6 slices on the corpus (S-117 to S-122, which prove only P4.seeded-defect-corpus-present, not the floor-vs-top measurement), and S-131's warn-never-pause default. GUARDS 11 and 13 were not cut because they collide with S-99 (v10-guard.sh) and the pulse script. GUARDS 12 (gitconfig-write lint) is the next alternate.
|
|
36
|
+
|
|
37
|
+
## 18:05Z cut: S-132..S-151
|
|
38
|
+
|
|
39
|
+
I've cut 20 slices, S-132 to S-151, and checked each one against the current tree. Every row below names something still open in the code or in CI, and no two rows edit the same file. None of them edits a file or block the in-flight reworks own (S-30/S-50, S-96, S-99, S-100, S-123, S-124, S-125, S-126).
|
|
40
|
+
|
|
41
|
+
Candidates I dropped because they are already fixed or would not help:
|
|
42
|
+
- **BACKLOG 13:** no Docker pin is left.
|
|
43
|
+
- **BACKLOG 35:** `tests/test-moat-runner.sh:653` already has the 0-byte case.
|
|
44
|
+
- **BACKLOG 37 and 60:** already fixed in `quality_gates.ts`.
|
|
45
|
+
- **BACKLOG 53 and 54:** those files already use `-E` everywhere.
|
|
46
|
+
- **Pinning pytest-xdist:** every entry in `requirements-test.txt` is unpinned, so pinning one would break the file's convention.
|
|
47
|
+
|
|
48
|
+
| S-132 | Velocity: skip the full Tests workflow on a version-bump-only commit whose parent's Tests run succeeded; run it in full otherwise (source 4) | .github/workflows/test.yml (new first gate job, `actions: read`, `needs`/`if` on the heavy jobs), scripts/ci/version-bump-only.sh (new), tests/test-version-bump-only.sh (new) | HIGH | red: on a bump-only fixture commit, test.yml runs every job; a fixture whose `loki-ts/dist` differs by more than the version string is judged eligible by nothing yet. green: bump-only plus parent Tests success skips the heavy jobs; parent pending or red runs the full suite (never waits); any non-allowlisted file runs the full suite; a parity test pulls release.yml's STEP 1 out by its content markers and gets the same eligibility verdict as the new script on at least 6 fixture diffs; mutating the script's allowlist turns the parity test red | ready@2026-09-27T18:05Z | Why HIGH: a Tests run whose jobs are all skipped concludes success, and required-ci rule (a) accepts success at the release SHA without looking at the parent. So the gate job itself must confirm the parent's Tests run succeeded, and its eligibility must be no looser than release.yml STEP 1. Never skip the version-bump consistency step (test.yml:81). Do not edit release.yml; deduplicating STEP 1 is a later slice. What it buys: runner capacity, plus no flaky failure at the release SHA (rule (b)) can block a release whose parent is green. The Captain registers the new test. |
|
|
49
|
+
| S-133 | Velocity: make the Release workflow's required-ci poll faster (source 4), and fix the stale publish-npm comment S-85's review flagged as non-blocking (source 1) | .github/workflows/release.yml (the `sleep 30` in the STEP 2 poll loop, and that one comment in publish-npm) | LOW | red: `grep -n 'sleep 30' release.yml` matches inside the required-ci loop, and the publish-npm comment names publish-docker for the dist assert. green: poll interval is 10s or less, the 2400s deadline is unchanged, the comment names publish-docker-build, actionlint is clean | ready@2026-09-27T18:05Z | Token budget: 2 API calls per iteration at 10s is 12 a minute, at most 480 across the 40-minute deadline, against GITHUB_TOKEN's shared 1000 an hour per repo. Keep it at 10s or more. Edit STEP 2 only; S-132's parity test reads STEP 1 by content markers. Ships alone, since it touches release.yml (D22). |
|
|
50
|
+
| S-134 | Velocity: shorten the slowest Tests shard by re-measuring shard-durations.tsv (it is from run 36319020918, now stale), plus the duration-table drift detector S-81's review listed as a follow-up (sources 4 and 1) | tests/shard-durations.tsv, tests/test-shard-durations-drift.sh (new) | LOW | red: Tests run 36338540948 shows shard 2/8 at 325s against shard 7/8 at 124s; the drift test fails today on any registered suite missing from the table, or any table row naming no registered suite. green: the table is rebuilt from the `END: <name> (Ns)` lines of all 8 shard logs of a recent green run, the drift test passes, and the next real Tests run's max shard is under 250s | ready@2026-09-27T18:05Z | Measure the whole workflow's wall clock, not just the max shard: once rebalanced, the 195s Moat suite likely becomes the floor. The floor for any single shard is ShellCheck at 140s. The Captain registers the drift test. |
|
|
51
|
+
| S-135 | Velocity: run tests/test-trust-core-tests-detect.sh's 90 probe_case mutations in parallel (130s in shard 1, the second-slowest suite) (source 4) | tests/test-trust-core-tests-detect.sh | MEDIUM | red: 130s on CI and one probe at a time. green: bounded parallel probes under 50s locally; the case count stays exactly 90 and every case still executes; a deliberately removed guard still turns its case red; 5 runs under 4-way CPU load with 0 flakes | ready@2026-09-27T18:05Z | First confirm probe_case mutates a private copy. If it edits the shared tree in place, parallel runs corrupt each other and the slice changes shape. Its anchors point into completion-council.sh (line 196 loops council_checklist_gate/council_heldout_gate) and probably into the run.sh failure-count awk, so rebase after S-141 and S-140 if they land first. |
|
|
52
|
+
| S-136 | Velocity: tests/test-select-tests.sh hit the 60s D27 cap (rc=124) on S-49's and S-91's train 4 checks (source 1) | tests/test-select-tests.sh | MEDIUM | red: `timeout 60 bash tests/test-select-tests.sh` returns rc=124. green: it finishes in under 30s with rc=0, still 38/38 cases, and a mutation of scripts/select-tests.sh (drop one rule) still turns it red | ready@2026-09-27T18:05Z | Coverage must not drop: assert the case count, not just the time. Do not touch scripts/select-tests.sh or tier-a.yml (S-96). |
|
|
53
|
+
| S-137 | S-81 review follow-up: shard-coverage check T4 recomputes a static count instead of running the real runner (source 1) | tests/test-shard-coverage.sh | LOW | red: a mutation that makes run-all-tests.sh's LPT assignment drop one suite from shard 0 leaves T4 green. green: T4 enumerates every shard of the real runner at n=4 and n=8 through a dry-run/list path, the union equals the registration list, and the same mutation turns it red | ready@2026-09-27T18:05Z | If run-all-tests.sh has no list mode, stop and report. Adding one would touch a Captain-owned hot file. |
|
|
54
|
+
| S-138 | GUARDS 12 (no guard yet): a lint so test fixtures never write the operator's real ~/.gitconfig (source 3) | tests/test-no-ambient-gitconfig-writes.sh (new) | LOW | red: a planted fixture with `git config --global url.x.insteadOf y` and no `HOME=`/`GIT_CONFIG_GLOBAL=` isolation before it is flagged, rc=1. green: the lint passes on the current 5 files that do global writes (test-trusted-push-agent-config, -acceptance-resume-idempotence, -enterprise-resilience, -branch-lifecycle, plus the moat p6/p9 scripts) only if each isolates first; any real offender is named, not allowlisted in silence | ready@2026-09-27T18:05Z | If a current file really does write the ambient config, report it; do not fix it inside this slice. The Captain registers the lint and updates GUARDS 12. |
|
|
55
|
+
| S-139 | GUARDS 13 (pending) plus BACKLOG 136: pulse flags a `released@` row stamped after npm's newest publish time (RELEASED_AHEAD_OF_NPM), and reports UNKNOWN when the local release tag disagrees with npm's latest version (sources 3 and 2) | scripts/v10-pulse.sh, tests/test-v10-pulse.sh | MEDIUM | red: a fixture BOARD with a row stamped `released@15:54Z` while the PULSE_NPM_CMD fixture's latest publish is 15:00Z raises no violation; a local tag v9.54.2 against npm latest 9.55.0 prints a confident "N commits since". green: both fixtures give the new violation and an UNKNOWN respectively, existing pulse tests stay green, runtime stays under 5s | ready@2026-09-27T18:05Z | Reuse the existing `npm view loki-mode time --json` result and its cache. No new network call, to stay inside S-104's 5s budget. Chosen over a board-row-status precondition because the pulse runs on every prompt, while nothing shows the Captain writes `released` through v10-ops.sh. |
|
|
56
|
+
| S-140 | BACKLOG 103: jest "Test Suites: 1 failed" alongside "Tests: 2 passed", and vitest "Test Files 1 failed", still record as passed (source 2) | autonomy/run.sh (the BACKLOG 73/111 failure-count awk near line 14240 only), plus the existing S-45 summary-parser test | MEDIUM | red: fixture output with "Test Suites: 1 failed, 1 total" and "Tests: 2 passed" records failed_count 0 and pass true; same for vitest "Test Files 1 failed \| 2 passed". green: both count at least 1 failed and the gate fails; a pure-green jest/vitest summary is still a measured 0; the trust-core suite still passes | ready@2026-09-27T18:05Z | Far from S-100's region (on_run_complete ~6012, trusted push) and S-124's (iteration 0, load_state ~21470). Run the trust-core suite in the wall and fix any anchor this breaks. |
|
|
57
|
+
| S-141 | S-49 follow-up: council verdict readers outside the 3 fixed functions still run bare `python3 -E` (~1425, ~1564 gate results; ~4232 aggregate_result) (source 1) | autonomy/completion-council.sh (those 3 call sites only), tests/test-council-gate-readers-pth.sh (new) | HIGH | red: a user-site `.pth` planted under a scratch HOME flips each of the 3 readers' verdicts (as S-49 reproduced). green: all 3 go through the resolved `-I -S` interpreter helper, fail closed when no interpreter is found, the `.pth` has no effect, and a mutation reverting one site turns the test red; the trust-core suite passes | ready@2026-09-27T18:05Z | A standalone suite, not a moat case, because tests/moat/cases.txt is a hot file. The Captain registers the suite. Line numbers drift, so find the sites by grep. |
|
|
58
|
+
| S-142 | S-48 follow-up: convergence-floor NEGATIVE-B passes even without the `runner!='none'` guard (source 1) | tests/test-council-convergence-floor.sh | LOW | red: removing the runner guard from the fast-path check (a scratch-copy mutation) leaves every case green. green: a new NEGATIVE-C writes the legacy shape `runner:"none", pass:true`, returns 1, and turns red under that mutation | ready@2026-09-27T18:05Z | Test-only. Confirm the guard still exists before writing; if it was removed on purpose, report that instead. |
|
|
59
|
+
| S-143 | BACKLOG 117 remainder / S-51 follow-ups: Gemini, Railway, stale gate counts and "Q2 2026 Current" copy (source 2) | web-app/src/components/Roadmap.tsx, DocsSidebar.tsx, ContextualHelp.tsx, ProductTour.tsx, CostEstimator.tsx, web-app/src/pages/SystemSettingsPage.tsx | LOW | red: `grep -il -E 'gemini\|railway\|9 (automated )?quality gates\|10 quality gates\|Q2 2026'` matches all 6 files. green: 0 matches in them; gate count is 8; deploy targets are Vercel, Netlify and GitHub Pages only | ready@2026-09-27T18:05Z | Copy only. If CostEstimator's Gemini entry is pricing logic, remove the option and note it. ProjectWorkspace.tsx is out of scope. The Captain rebuilds web-app/dist. |
|
|
60
|
+
| S-144 | S-51 follow-up: Footer.tsx shows v9.55.0 against VERSION 9.66.0 and is not in the bump list (source 1) | scripts/release.sh, tests/test-release-sh.sh, web-app/src/components/Footer.tsx | LOW | red: Footer.tsx holds v9.55.0 while VERSION is 9.66.0, and a `--bump-only` fixture run leaves Footer unchanged. green: `--bump-only` rewrites the Footer version slot, and the test asserts it for that file by name | ready@2026-09-27T18:05Z | WhatsNew.tsx is deliberately excluded. Its modal opens when `lastSeen !== CURRENT_VERSION`, so bumping it automatically would pop the same stale modal for every user at every release (3-5 an hour). Keying it to its own content version is a separate question (see the end list). |
|
|
61
|
+
| S-145 | BACKLOG 118: GET /api/checklist/waivers answers 200 `{waivers: []}` when waivers.json is corrupt, so a read error looks like "no waivers" (source 2) | dashboard/server.py (get_checklist_waivers only), tests/dashboard/test_checklist_waivers_read_error.py (new) | LOW | red: a corrupt waivers.json returns 200 with an empty list. green: it returns 500 with an error body, matching the POST sibling at ~10431; a missing file still returns 200 with an empty list | ready@2026-09-27T18:05Z | Survey the consumers first (dashboard-ui checklist viewer, standalone): each must show "Could not load" on 500, not an empty state. Note which consumers were checked in the wall. |
|
|
62
|
+
| S-146 | BACKLOG 118: the dashboard shell has two elements with `id="budget-banner"` (source 2) | dashboard-ui/scripts/build-standalone.js | LOW | red: `grep -c 'id="budget-banner"'` returns 2. green: returns 1, and the banner script still finds and updates the remaining element (a node or DOM check in the builder's evidence) | ready@2026-09-27T18:05Z | While in the file, check the other two 118 items (the "Newest first" reversal at ~2510); fix only if trivial, otherwise report. The Captain rebuilds dashboard/static/index.html. |
|
|
63
|
+
| S-147 | BACKLOG 118: `loki metrics --json` sends `tokens.total` 0 and `total_iterations` 0 when nothing was recorded, and invents `time_saved_hours = iterations*15/60` (source 2) | autonomy/loki (cmd_metrics only), tests/test-metrics-json-unmeasured.sh (new) | MEDIUM | red: on an empty .loki, `loki metrics --json` prints total 0, iterations 0 and a numeric time_saved_hours. green: null (unknown) for unrecorded values, time_saved_hours removed or null, real recorded values unchanged | ready@2026-09-27T18:05Z | cmd_metrics (~28481) is far from S-123's attestation region (~956); rebase if S-123 lands first. Survey consumers of the JSON (dashboard, docs) before changing 0 to null. The Captain registers the test. |
|
|
64
|
+
| S-148 | BACKLOG 118: trust.html says "Stable: no significant change" when every axis lacks the 2+ runs of history needed (source 2) | dashboard/static/trust.html | LOW | red: an API fixture where every axis has `insufficient: true` renders the "Stable" headline. green: it renders "Not enough history yet"; a fixture with real flat axes still says "Stable" (pull the headline into a named function and check it with a small node assertion) | ready@2026-09-27T18:05Z | Hand-written page, not a generated bundle. The `delta: 0.0` on an insufficient axis in trust_trajectory.py is out of scope; note it for later. |
|
|
65
|
+
| S-149 | BACKLOG 118: prompt_optimizer.get_current_version returns never-ran zeros (version 0, based_on_sessions 0), and a corrupt latest file is folded into the same default (source 2) | dashboard/prompt_optimizer.py, tests/dashboard/test_prompt_optimizer_sentinels.py (new) | LOW | red: a corrupt latest file and a missing one return the identical zero dict. green: missing returns an explicit never-ran marker (nulls); corrupt returns an error marker distinct from never-ran | ready@2026-09-27T18:05Z | Survey the endpoint and panel consumers first so the new fields render as unknown, not crash. |
|
|
66
|
+
| S-150 | BACKLOG 118: migration_engine.get_progress reports 0/0 features and steps when the features or plan file is corrupt or missing (source 2) | dashboard/migration_engine.py (get_progress only), tests/dashboard/test_migration_progress_unknown.py (new) | LOW | red: a corrupt plan.json gives `steps_total 0, steps_completed 0`, identical to a real empty plan. green: corrupt or missing gives null counts plus a reason; a real empty plan still gives 0 | ready@2026-09-27T18:05Z | Survey consumers (loki-migration-dashboard.js, server endpoint) to make sure null renders as unknown. |
|
|
67
|
+
| S-151 | BACKLOG 118: loki-analytics heatmap counts days before the returned activity window as 0 activities (source 2) | dashboard-ui/components/loki-analytics.js (_computeHeatmap and the cell render only) | LOW | red: a fixture with activity starting 10 days ago renders the previous ~355 days as "0 activities". green: days before the earliest returned entry (or the API's window start) render as "no data", while days inside the window with no activity stay 0 | ready@2026-09-27T18:05Z | The Captain rebuilds dashboard/static/index.html. Different file from S-146. |
|
|
68
|
+
|
|
69
|
+
**Risks**
|
|
70
|
+
- **Registration is on the Captain.** 8 rows add a new test file (S-132, S-134, S-138, S-141, S-145, S-147, S-149, S-150). Each needs the Captain to register it in `tests/run-all-tests.sh`, or it never runs in CI.
|
|
71
|
+
- **Rebuilds are on the Captain.** S-143 needs web-app/dist rebuilt; S-146 and S-151 need dashboard/static rebuilt.
|
|
72
|
+
- **Test anchors, not files, couple S-135, S-140 and S-141.** Trust-core probes anchor into completion-council.sh and probably into the run.sh failure-count awk. Merge S-140 and S-141 first, and each of them must run the trust-core suite in its wall. S-135's first step is confirming probes use a private copy.
|
|
73
|
+
- **Two rows touch the release gate.** S-132 is the highest-risk slice: an all-skipped Tests run reads as success at the release SHA. It needs a HIGH review focused on the parent-green check and on eligibility never being looser than release.yml STEP 1. S-133 also touches release.yml, so under D22 it ships in its own train.
|
|
74
|
+
- **Same-file neighbours.** S-140 (run.sh) and S-147 (autonomy/loki) share a file with in-flight S-100/S-124 and S-123, in regions far from theirs. Expect rebases, not conflicts.
|
|
75
|
+
- **S-134's gain only shows on a real CI run.** The first Tests run after merge is the evidence; the Moat suite (195s) may become the new floor.
|
|
76
|
+
- **Changing zeros to null or 200 to 500 changes response shapes.** S-145, S-147, S-149 and S-150 must name the consumers they checked, or a panel can crash where it used to show a false zero.
|
|
77
|
+
|
|
78
|
+
**Held for a CEO decision or waiting on another slice (not in the 20)**
|
|
79
|
+
- S-112 and S-117 to S-122 (P4 corpus and ablation producer): already held.
|
|
80
|
+
- BACKLOG 4: breaking exit-code renumber for `loki verify` (v10.0.0).
|
|
81
|
+
- BACKLOG 18: Caveman bootstrap and its global `~/.claude` hook default-on; changing the default is a product decision.
|
|
82
|
+
- BACKLOG 121: browser-only settings (plain-text API keys in localStorage, unwired "Budget limit" and "Auto-deploy"): remove or wire is a product decision.
|
|
83
|
+
- BACKLOG 77: whether Bun should write its own test-results freshness marker.
|
|
84
|
+
- BACKLOG 123: `audit.py verify` reporting valid on nothing checked; the cross-chain verifier reads it, so it needs its own review.
|
|
85
|
+
- WhatsNew.tsx version: stays stale until someone decides what should trigger the modal.
|
|
86
|
+
- GUARDS 11 (scratchpad glob `rm`): a helper nobody is forced to call blocks nothing. The real rule belongs in `scripts/v10-guard.sh`, so cut it after S-99 lands.
|
|
87
|
+
- Waiting on a slice in this set: BACKLOG 127 remainder (`run.sh` auto_capture_episode, `cd "$PROJECT_DIR"` with no unset guard, ~21361) after S-140; BACKLOG 38 after S-141; BACKLOG 133 after S-139.
|
|
88
|
+
|
|
89
|
+
I edited no files. The only temp file was a log saved under `$TMPDIR` and deleted in the same command. I started no background processes.
|