loki-mode 9.17.0 → 9.18.2
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/issue-providers.sh +0 -24
- package/autonomy/lib/agent_readiness.py +1 -79
- package/autonomy/lib/outcome_ledger.py +0 -122
- package/autonomy/lib/proof-generator.py +4 -71
- package/autonomy/lib/verdict.py +4 -25
- package/autonomy/loki +444 -264
- package/autonomy/notify.sh +1 -70
- package/autonomy/queue-consumer.sh +18 -290
- package/autonomy/run.sh +280 -189
- package/autonomy/trigger-server.py +518 -17
- package/autonomy/verify.sh +100 -12
- package/bin/loki +7 -1
- package/completions/_loki +0 -3
- package/completions/loki.bash +2 -2
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +1 -168
- package/dashboard/static/index.html +222 -249
- package/docs/COMPETITIVE-SCORECARD.md +38 -0
- package/docs/COMPETITOR-DEPLOYMENT-MODELS.md +475 -0
- package/docs/DEPLOYMENT.md +542 -0
- package/docs/STALE-STATE-AUDIT.md +174 -0
- package/docs/VERIFICATION-COST.md +46 -166
- package/loki-ts/dist/loki.js +311 -305
- package/mcp/__init__.py +1 -1
- package/mcp/_sdk_loader.py +0 -25
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/autonomy/lib/gate_policy.py +0 -166
- package/docs/QUEUE-OPERATIONS.md +0 -107
package/autonomy/verify.sh
CHANGED
|
@@ -421,6 +421,30 @@ _verify_zero_tests_executed() {
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
# ---------------------------------------------------------------------------
|
|
424
|
+
# Reads scripts.test out of a package.json, or prints nothing.
|
|
425
|
+
#
|
|
426
|
+
# Parsed as JSON, never grepped: a substring search over the whole file is the
|
|
427
|
+
# exact bug this helper exists to remove, and re-introducing it one level down
|
|
428
|
+
# would be invisible. A malformed package.json prints nothing, which routes to
|
|
429
|
+
# runner=none -> INCONCLUSIVE, never to a guessed runner.
|
|
430
|
+
_verify_pkg_test_script() {
|
|
431
|
+
local tree="$1"
|
|
432
|
+
[ -f "$tree/package.json" ] || return 0
|
|
433
|
+
python3 -c '
|
|
434
|
+
import json,sys
|
|
435
|
+
try:
|
|
436
|
+
with open(sys.argv[1]) as fh:
|
|
437
|
+
d = json.load(fh)
|
|
438
|
+
except Exception:
|
|
439
|
+
sys.exit(0)
|
|
440
|
+
if not isinstance(d, dict):
|
|
441
|
+
sys.exit(0)
|
|
442
|
+
s = d.get("scripts")
|
|
443
|
+
if isinstance(s, dict) and isinstance(s.get("test"), str):
|
|
444
|
+
sys.stdout.write(s["test"])
|
|
445
|
+
' "$tree/package.json" 2>/dev/null || true
|
|
446
|
+
}
|
|
447
|
+
|
|
424
448
|
# Gate: tests (faithful port of enforce_test_coverage detection, run.sh:6624).
|
|
425
449
|
#
|
|
426
450
|
# Detection order mirrors the source: vitest -> jest -> mocha (package.json),
|
|
@@ -451,16 +475,52 @@ verify_gate_tests() {
|
|
|
451
475
|
local out=""
|
|
452
476
|
|
|
453
477
|
if [ -f "$tree/package.json" ]; then
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
478
|
+
# WHICH runner, decided by what `npm test` ACTUALLY INVOKES -- not by
|
|
479
|
+
# what happens to be installed.
|
|
480
|
+
#
|
|
481
|
+
# The old check was `grep '"jest"' package.json`, which matches a
|
|
482
|
+
# DEVDEPENDENCY entry. On this very repo that is exactly what happened:
|
|
483
|
+
# jest is a devDependency with NO jest config, while scripts.test runs
|
|
484
|
+
# bash -n plus `node --test`. So verify hijacked the runner, jest globbed
|
|
485
|
+
# 895 files that are not jest tests, every one reported "Your test suite
|
|
486
|
+
# must contain at least one test", and `loki verify` returned BLOCKED on
|
|
487
|
+
# a clean tree -- permanently, for a defect that does not exist.
|
|
488
|
+
#
|
|
489
|
+
# A false BLOCK on the flagship verification command is worse than a
|
|
490
|
+
# missed one: it trains users to ignore the verdict.
|
|
491
|
+
#
|
|
492
|
+
# scripts.test is the project's own declaration of its runner, so it is
|
|
493
|
+
# the signal with authority here. A declared script that names none of
|
|
494
|
+
# the three is still RUN, via `npm test` -- mirroring run.sh's npm-test
|
|
495
|
+
# fallback. Falling through instead would be its own defect: this repo
|
|
496
|
+
# declares `bash -n ... && node --test ...`, and without the fallback
|
|
497
|
+
# verify skipped it and ran PYTEST over a bash/node project.
|
|
498
|
+
local _test_script=""
|
|
499
|
+
_test_script="$(_verify_pkg_test_script "$tree")"
|
|
500
|
+
case "$_test_script" in
|
|
501
|
+
*vitest*)
|
|
502
|
+
runner="vitest"
|
|
503
|
+
out="$(cd "$tree" && $_vt npx vitest run 2>&1)" || rc=$? ;;
|
|
504
|
+
*jest*)
|
|
505
|
+
runner="jest"
|
|
506
|
+
out="$(cd "$tree" && $_vt npx jest --passWithNoTests --forceExit 2>&1)" || rc=$? ;;
|
|
507
|
+
*mocha*)
|
|
508
|
+
runner="mocha"
|
|
509
|
+
out="$(cd "$tree" && $_vt npx mocha 2>&1)" || rc=$? ;;
|
|
510
|
+
"")
|
|
511
|
+
: ;; # nothing declared: leave runner=none for the paths below
|
|
512
|
+
*"no test specified"*)
|
|
513
|
+
: ;; # npm's placeholder is not a test script
|
|
514
|
+
*)
|
|
515
|
+
# Labelled by what it invokes, so the evidence names the real
|
|
516
|
+
# runner rather than a generic "npm".
|
|
517
|
+
case "$_test_script" in
|
|
518
|
+
*"node --test"*|*"node:test"*) runner="node-test" ;;
|
|
519
|
+
*pytest*) runner="pytest" ;;
|
|
520
|
+
*) runner="npm-test" ;;
|
|
521
|
+
esac
|
|
522
|
+
out="$(cd "$tree" && $_vt npm test 2>&1)" || rc=$? ;;
|
|
523
|
+
esac
|
|
464
524
|
fi
|
|
465
525
|
|
|
466
526
|
if [ "$runner" = "none" ]; then
|
|
@@ -1060,10 +1120,38 @@ print("%d %d %d %d" % (crit, high, mod, low))
|
|
|
1060
1120
|
' 2>/dev/null || echo "")"
|
|
1061
1121
|
if [ -n "$sev" ]; then
|
|
1062
1122
|
read -r _c _h _m _l <<<"$sev"
|
|
1123
|
+
# SHIPPED vs dev. The audit above covers ALL dependencies,
|
|
1124
|
+
# which is right for a gate -- a compromised build tool is a
|
|
1125
|
+
# real risk -- but the FINDING must say which of the two it
|
|
1126
|
+
# is. This repo reports 4 high CVEs while `npm audit
|
|
1127
|
+
# --omit=dev` reports ZERO: nothing a user installs is
|
|
1128
|
+
# vulnerable. A receipt that says "4 high severity
|
|
1129
|
+
# vulnerabilities" with no such qualifier reads as "the
|
|
1130
|
+
# shipped product is vulnerable", which is a materially
|
|
1131
|
+
# different and false claim.
|
|
1132
|
+
#
|
|
1133
|
+
# Measured separately rather than subtracted: the two runs
|
|
1134
|
+
# count different dependency graphs, so arithmetic on the
|
|
1135
|
+
# totals would not be sound.
|
|
1136
|
+
local _prod_hc=""
|
|
1137
|
+
_prod_hc="$(cd "$tree" && npm audit --omit=dev --json 2>/dev/null | python3 -c '
|
|
1138
|
+
import sys, json
|
|
1139
|
+
try:
|
|
1140
|
+
v = json.load(sys.stdin).get("metadata", {}).get("vulnerabilities", {})
|
|
1141
|
+
except Exception:
|
|
1142
|
+
sys.exit(0)
|
|
1143
|
+
print(v.get("critical", 0) + v.get("high", 0))
|
|
1144
|
+
' 2>/dev/null || echo "")"
|
|
1145
|
+
local _scope_note=""
|
|
1146
|
+
if [ "$_prod_hc" = "0" ]; then
|
|
1147
|
+
_scope_note=" All are in devDependencies; \`npm audit --omit=dev\` reports 0 high/critical, so nothing in the shipped dependency tree is affected."
|
|
1148
|
+
elif [ -n "$_prod_hc" ]; then
|
|
1149
|
+
_scope_note=" $_prod_hc of these are in the SHIPPED (non-dev) dependency tree."
|
|
1150
|
+
fi
|
|
1063
1151
|
if [ "$_c" -gt 0 ] || [ "$_h" -gt 0 ]; then
|
|
1064
|
-
_verify_add_gate "dependency_audit" "fail" "npm-audit" "$_c critical, $_h high CVEs" "true"
|
|
1152
|
+
_verify_add_gate "dependency_audit" "fail" "npm-audit" "$_c critical, $_h high CVEs (shipped high/critical: ${_prod_hc:-unmeasured})" "true"
|
|
1065
1153
|
_verify_add_finding "High" "dependencies" "deterministic:npm-audit" "package-lock.json" "null" \
|
|
1066
|
-
"npm audit found $_c critical and $_h high severity vulnerabilities
|
|
1154
|
+
"npm audit found $_c critical and $_h high severity vulnerabilities.${_scope_note}"
|
|
1067
1155
|
elif [ "$_m" -gt 0 ]; then
|
|
1068
1156
|
_verify_add_gate "dependency_audit" "fail" "npm-audit" "$_m moderate CVEs" "true"
|
|
1069
1157
|
_verify_add_finding "Medium" "dependencies" "deterministic:npm-audit" "package-lock.json" "null" \
|
package/bin/loki
CHANGED
|
@@ -216,8 +216,14 @@ fi
|
|
|
216
216
|
# we never disclose for an egress that will not happen. This mirrors the
|
|
217
217
|
# cli_command sites below. No arguments are read, so nothing user-authored can
|
|
218
218
|
# reach the payload.
|
|
219
|
+
#
|
|
220
|
+
# `quickstart` and `demo` are here because they are the two commands `loki
|
|
221
|
+
# doctor` actually RECOMMENDS as a first build. Without them the funnel missed
|
|
222
|
+
# every user who followed the product's own advice, which is precisely the
|
|
223
|
+
# population whose drop-off this measures. Both are already in the
|
|
224
|
+
# _loki_known_command allowlist, so `entry=` stays a bounded enum.
|
|
219
225
|
case "${1:-}" in
|
|
220
|
-
start|run|quick)
|
|
226
|
+
start|run|quick|quickstart|demo)
|
|
221
227
|
if command -v curl &>/dev/null && [ -f "$REPO_ROOT/autonomy/telemetry.sh" ]; then
|
|
222
228
|
if ( SCRIPT_DIR="$REPO_ROOT/autonomy"; source "$SCRIPT_DIR/telemetry.sh" 2>/dev/null \
|
|
223
229
|
&& declare -f _loki_analytics_enabled >/dev/null 2>&1 && _loki_analytics_enabled \
|
package/completions/_loki
CHANGED
|
@@ -120,7 +120,6 @@ function _loki {
|
|
|
120
120
|
function _loki_commands {
|
|
121
121
|
local -a commands
|
|
122
122
|
commands=(
|
|
123
|
-
'gates:What blocks here vs only advises, and the promotion knob'
|
|
124
123
|
'start:Start Loki Mode'
|
|
125
124
|
'quick:Quick single-task mode'
|
|
126
125
|
'quickstart:Guided first build from your idea'
|
|
@@ -173,8 +172,6 @@ function _loki_commands {
|
|
|
173
172
|
'share:Share session report as GitHub Gist'
|
|
174
173
|
'proof:Inspect/share proof-of-run artifacts'
|
|
175
174
|
'outcomes:What happened to the work AFTER the receipt (reverted/reworked/survived)'
|
|
176
|
-
'verdict:The five measured trust signals in one readable block'
|
|
177
|
-
'readiness:Can an agent verify its own work in this repo? (measured, not LLM-scored)'
|
|
178
175
|
'preview:Preview the locally-running app'
|
|
179
176
|
'deploy:Deploy the built product (CI/CD-aware)'
|
|
180
177
|
'context:Context window management'
|
package/completions/loki.bash
CHANGED
|
@@ -5,8 +5,8 @@ _loki_completion() {
|
|
|
5
5
|
_init_completion || return
|
|
6
6
|
|
|
7
7
|
# Main subcommands (must match autonomy/loki main case statement)
|
|
8
|
-
local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes
|
|
9
|
-
local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue intent config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover
|
|
8
|
+
local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
|
|
9
|
+
local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue intent config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
|
|
10
10
|
|
|
11
11
|
# 1. If we are on the first argument (subcommand)
|
|
12
12
|
if [[ $cword -eq 1 ]]; then
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -8354,93 +8354,6 @@ async def get_trust_trajectory():
|
|
|
8354
8354
|
return traj
|
|
8355
8355
|
|
|
8356
8356
|
|
|
8357
|
-
# =============================================================================
|
|
8358
|
-
# Gate policy API: which gates block here, and which only advise?
|
|
8359
|
-
# =============================================================================
|
|
8360
|
-
|
|
8361
|
-
_GATE_POLICY_MODULE = None # cached import of autonomy/lib/gate_policy.py
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
def _load_gate_policy_module():
|
|
8365
|
-
"""Import the shared gate-policy reporter (single source of truth).
|
|
8366
|
-
|
|
8367
|
-
Same shape as _load_trust_module above: the derivation lives in
|
|
8368
|
-
autonomy/lib/gate_policy.py so this endpoint, the bash `loki gates`, and the
|
|
8369
|
-
test suite all agree, and it loads via importlib because autonomy/lib is not
|
|
8370
|
-
an importable package. Cached after first load, None when unavailable.
|
|
8371
|
-
"""
|
|
8372
|
-
global _GATE_POLICY_MODULE
|
|
8373
|
-
if _GATE_POLICY_MODULE is not None:
|
|
8374
|
-
return _GATE_POLICY_MODULE
|
|
8375
|
-
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
8376
|
-
mod_path = os.path.join(repo_root, "autonomy", "lib", "gate_policy.py")
|
|
8377
|
-
if not os.path.isfile(mod_path):
|
|
8378
|
-
return None
|
|
8379
|
-
try:
|
|
8380
|
-
import importlib.util as _ilu
|
|
8381
|
-
spec = _ilu.spec_from_file_location("gate_policy", mod_path)
|
|
8382
|
-
if spec is None or spec.loader is None:
|
|
8383
|
-
return None
|
|
8384
|
-
mod = _ilu.module_from_spec(spec)
|
|
8385
|
-
spec.loader.exec_module(mod)
|
|
8386
|
-
_GATE_POLICY_MODULE = mod
|
|
8387
|
-
return mod
|
|
8388
|
-
except Exception:
|
|
8389
|
-
return None
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
@app.get("/api/gate-policy", dependencies=[Depends(auth.require_scope("read"))])
|
|
8393
|
-
async def get_gate_policy():
|
|
8394
|
-
"""Which quality gates block on this repo, and which only advise.
|
|
8395
|
-
|
|
8396
|
-
Read-only by construction. There is deliberately no POST counterpart:
|
|
8397
|
-
promoting an advisory gate to blocking stays an explicit operator act via
|
|
8398
|
-
the environment variable named in each record's `promote_with`, because a
|
|
8399
|
-
surface that can silently start blocking is the thing operators most
|
|
8400
|
-
reasonably fear. This endpoint reports; it never promotes.
|
|
8401
|
-
|
|
8402
|
-
Honest-data rule, and the reason `audit_hits` is nullable: an unmeasured
|
|
8403
|
-
gate reports null, NEVER 0. Zero is the positive claim that the gate ran and
|
|
8404
|
-
never fired; an absent ledger is not evidence of that. See the shape
|
|
8405
|
-
contract in the gate_policy module docstring.
|
|
8406
|
-
|
|
8407
|
-
Fails open like /api/trust/trajectory: a missing module returns a renderable
|
|
8408
|
-
available=False payload rather than a 500. Unlike that sibling the assess()
|
|
8409
|
-
call is also wrapped, because it reads a ledger file that an unrelated
|
|
8410
|
-
writer can leave malformed, and a broken ledger must not take down a
|
|
8411
|
-
read-only report.
|
|
8412
|
-
|
|
8413
|
-
Response keys are the module shape contract plus `available` (bool). On the
|
|
8414
|
-
fail-open path `available` is False, `status` is "unavailable" (a value the
|
|
8415
|
-
module contract does not itself define, since assess() never returned), an
|
|
8416
|
-
`error` string explains why, and `gates` is empty. A UI should branch on
|
|
8417
|
-
`available`, not on a non-empty `gates`.
|
|
8418
|
-
"""
|
|
8419
|
-
mod = _load_gate_policy_module()
|
|
8420
|
-
if mod is None:
|
|
8421
|
-
return {
|
|
8422
|
-
"schema_version": 1,
|
|
8423
|
-
"available": False,
|
|
8424
|
-
"error": "gate_policy module not found",
|
|
8425
|
-
"status": "unavailable",
|
|
8426
|
-
"ledger": "absent",
|
|
8427
|
-
"gates": [],
|
|
8428
|
-
}
|
|
8429
|
-
try:
|
|
8430
|
-
res = mod.assess(str(_get_loki_dir()))
|
|
8431
|
-
except Exception as e:
|
|
8432
|
-
return {
|
|
8433
|
-
"schema_version": 1,
|
|
8434
|
-
"available": False,
|
|
8435
|
-
"error": f"gate policy assessment failed: {e}",
|
|
8436
|
-
"status": "unavailable",
|
|
8437
|
-
"ledger": "absent",
|
|
8438
|
-
"gates": [],
|
|
8439
|
-
}
|
|
8440
|
-
res["available"] = True
|
|
8441
|
-
return res
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
8357
|
# =============================================================================
|
|
8445
8358
|
# Pricing API
|
|
8446
8359
|
# =============================================================================
|
|
@@ -10270,71 +10183,6 @@ async def remove_checklist_waiver(item_id: str):
|
|
|
10270
10183
|
# Council Hard Gate Endpoint (Phase 4)
|
|
10271
10184
|
# =============================================================================
|
|
10272
10185
|
|
|
10273
|
-
# Receipts name gates in snake_case; the UI rows are display names. Mapping is
|
|
10274
|
-
# explicit rather than derived (a lower()/replace() heuristic would silently
|
|
10275
|
-
# mis-map "Test Suite" <-> "test_coverage" and "Test Mutation" <->
|
|
10276
|
-
# "mutation_integrity", which are different gates).
|
|
10277
|
-
_RECEIPT_GATE_NAMES = {
|
|
10278
|
-
"static_analysis": "Static Analysis",
|
|
10279
|
-
"test_coverage": "Test Suite",
|
|
10280
|
-
"code_review": "Blind Code Review",
|
|
10281
|
-
"anti_sycophancy": "Anti-Sycophancy",
|
|
10282
|
-
"mock_integrity": "Mock Integrity",
|
|
10283
|
-
"mutation_integrity": "Test Mutation",
|
|
10284
|
-
"doc_coverage": "Documentation Coverage",
|
|
10285
|
-
"magic_debate": "Magic Modules Debate",
|
|
10286
|
-
}
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
# Receipts say "passed"/"failed"; the UI switches on "pass"/"fail"
|
|
10290
|
-
# (dashboard-ui/components/loki-quality-gates.js:51). Passing the receipt's word
|
|
10291
|
-
# through unchanged matched nothing, so every gate fell to the "pending" default
|
|
10292
|
-
# and the page still read "0 Pass, 0 Fail, 8 Pending" while showing real
|
|
10293
|
-
# timestamps -- half-fixed, and only visible by loading the page.
|
|
10294
|
-
#
|
|
10295
|
-
# An UNRECOGNISED status maps to nothing and the gate stays pending. Guessing
|
|
10296
|
-
# (e.g. treating any unknown word as a pass) is how a future status string would
|
|
10297
|
-
# silently become a green badge.
|
|
10298
|
-
_RECEIPT_GATE_STATUS = {
|
|
10299
|
-
"passed": "pass", "pass": "pass", "ok": "pass",
|
|
10300
|
-
"failed": "fail", "fail": "fail", "blocked": "fail",
|
|
10301
|
-
}
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
def _receipt_backed_gates():
|
|
10305
|
-
"""_DEFAULT_QUALITY_GATES, with results filled in from the newest receipt.
|
|
10306
|
-
|
|
10307
|
-
Fail-open by design: any error returns the plain defaults, so a malformed
|
|
10308
|
-
receipt degrades to "pending" rather than breaking the Quality page.
|
|
10309
|
-
|
|
10310
|
-
Gates absent from the receipt keep status "pending" and get NO last_checked.
|
|
10311
|
-
An absent measurement is never rendered as a result.
|
|
10312
|
-
"""
|
|
10313
|
-
gates = [dict(g) for g in _DEFAULT_QUALITY_GATES]
|
|
10314
|
-
try:
|
|
10315
|
-
proofs = sorted((_get_loki_dir() / "proofs").glob("*/proof.json"))
|
|
10316
|
-
if not proofs:
|
|
10317
|
-
return gates
|
|
10318
|
-
receipt = json.loads(proofs[-1].read_text())
|
|
10319
|
-
checked_at = receipt.get("generated_at") or ""
|
|
10320
|
-
by_display = {}
|
|
10321
|
-
for entry in receipt.get("quality_gates", {}).get("gates", []) or []:
|
|
10322
|
-
display = _RECEIPT_GATE_NAMES.get(entry.get("name", ""))
|
|
10323
|
-
status = _RECEIPT_GATE_STATUS.get(str(entry.get("status", "")).lower())
|
|
10324
|
-
if display and status:
|
|
10325
|
-
by_display[display] = status
|
|
10326
|
-
for gate in gates:
|
|
10327
|
-
status = by_display.get(gate.get("name"))
|
|
10328
|
-
if status:
|
|
10329
|
-
gate["status"] = status
|
|
10330
|
-
if checked_at:
|
|
10331
|
-
gate["last_checked"] = checked_at
|
|
10332
|
-
gate["source"] = "receipt"
|
|
10333
|
-
except (OSError, ValueError, KeyError, TypeError, AttributeError):
|
|
10334
|
-
return [dict(g) for g in _DEFAULT_QUALITY_GATES]
|
|
10335
|
-
return gates
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
10186
|
_DEFAULT_QUALITY_GATES = [
|
|
10339
10187
|
{"name": "Static Analysis", "description": "CodeQL, ESLint/Pylint, type-checker findings on the diff", "status": "pending"},
|
|
10340
10188
|
{"name": "Test Suite", "description": "Project test runner pass/fail (red blocks)", "status": "pending"},
|
|
@@ -10373,22 +10221,7 @@ async def get_council_gate():
|
|
|
10373
10221
|
except (json.JSONDecodeError, IOError):
|
|
10374
10222
|
data = {"blocked": False, "gates": _DEFAULT_QUALITY_GATES, "error": "Failed to read gate file"}
|
|
10375
10223
|
else:
|
|
10376
|
-
|
|
10377
|
-
# records per-gate results -- rather than serving eight rows that all
|
|
10378
|
-
# say "Last checked: Never" while .loki/proofs/ holds the real answers.
|
|
10379
|
-
#
|
|
10380
|
-
# That "Never" was not a display bug: /api/council/gate served
|
|
10381
|
-
# _DEFAULT_QUALITY_GATES verbatim, and those entries carry no
|
|
10382
|
-
# last_checked, so the UI honestly rendered "Never" for every gate on a
|
|
10383
|
-
# repo with 9 receipts.
|
|
10384
|
-
#
|
|
10385
|
-
# ONLY gates the receipt actually names are filled in. Measured across
|
|
10386
|
-
# all 9 receipts here, that is 3 of 8 (static_analysis, code_review,
|
|
10387
|
-
# doc_coverage); the other five stay "pending" with no timestamp,
|
|
10388
|
-
# because a gate that never ran must not inherit a neighbour's result.
|
|
10389
|
-
# Filling all eight from a 3-gate receipt is the exact false-green this
|
|
10390
|
-
# codebase exists to prevent.
|
|
10391
|
-
data = {"blocked": False, "gates": _receipt_backed_gates()}
|
|
10224
|
+
data = {"blocked": False, "gates": _DEFAULT_QUALITY_GATES}
|
|
10392
10225
|
|
|
10393
10226
|
# Verified-completion evidence gate (additive).
|
|
10394
10227
|
if evidence_file.exists():
|