loki-mode 9.17.0 → 9.17.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 +5 -262
- package/autonomy/notify.sh +1 -70
- package/autonomy/queue-consumer.sh +18 -290
- package/autonomy/run.sh +65 -178
- package/autonomy/verify.sh +100 -12
- 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/VERIFICATION-COST.md +20 -170
- 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/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():
|