loki-mode 7.85.0 → 7.87.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/README.md CHANGED
@@ -45,6 +45,82 @@ _The free, source-available autonomous coding agent by [Autonomi](https://www.au
45
45
 
46
46
  ---
47
47
 
48
+ ## Loki does not lie about "done"
49
+
50
+ Most coding agents declare a task done by telling you so in a transcript. The
51
+ transcript is the agent's own narration; there is nothing to check. Loki Mode
52
+ takes a different stance: it does not call work done until the work is verified,
53
+ and every build produces an **Evidence Receipt** you can re-verify yourself.
54
+
55
+ The receipt separates two things most tools blur together:
56
+
57
+ - **Facts** -- deterministic, non-LLM, and re-derivable by anyone: the git diff
58
+ (base/head SHAs, file/insertion/deletion counts, a `diff_sha256`), the test
59
+ command that ran with its exit code, the build command with its exit code, and
60
+ each quality-gate verdict. A skeptic can recompute every one of these from the
61
+ same repo state.
62
+ - **Assessments** -- AI judgments such as the review council's verdict. These are
63
+ labeled explicitly as judgment, not proof, and never make the headline green on
64
+ their own.
65
+
66
+ The receipt's headline is computed only from the facts:
67
+
68
+ - **VERIFIED** -- tests recorded a real command, ran, and exited 0; the diff is
69
+ non-empty; nothing was skipped.
70
+ - **VERIFIED WITH GAPS** -- some facts checked out, but something was not run or
71
+ was inconclusive. Every gap is listed by name, so silence never reads as a pass.
72
+ - **NOT VERIFIED** -- a test, build, or gate ran and failed (or there was nothing
73
+ to verify).
74
+
75
+ This is honesty-of-done, not a claim of perfection. The receipt proves the
76
+ completion claim is backed by deterministic evidence and is independently
77
+ re-checkable; it does not claim the generated code is bug-free.
78
+
79
+ ### Verify it yourself
80
+
81
+ Receipts are written to `.loki/proofs/<run_id>/` automatically at run completion
82
+ (opt out with `LOKI_PROOF=0`). Inspect and re-check them with `loki proof`
83
+ (aliased as `loki receipt`):
84
+
85
+ ```bash
86
+ loki proof list # every receipt: run id, time, council verdict, cost, files
87
+ loki proof show <id> # the full proof.json (facts, assessments, honesty)
88
+ loki proof verify <id> # re-check the receipt against the repo (exit 0 clean, 1 tamper/drift)
89
+ ```
90
+
91
+ `loki proof verify` does two independent checks and prints the result as JSON:
92
+
93
+ - **Tamper check** -- recomputes the receipt's integrity hash and compares it to
94
+ the recorded one. If anyone edited the receipt after it was written, `hash_ok`
95
+ is `false`.
96
+ - **Drift check** -- re-runs the diff from the recorded base SHA against the
97
+ current repo and compares the file/insertion/deletion counts and `diff_sha256`
98
+ to what the receipt recorded. If the repo no longer matches, `diff_drift` is
99
+ `true`.
100
+
101
+ A clean receipt prints `"ok": true` and exits 0. A tampered or drifted receipt
102
+ exits 1. When a check cannot run (for example a receipt with no recorded base
103
+ SHA), the verifier reports it as unverifiable rather than passing it silently.
104
+
105
+ ```json
106
+ {
107
+ "hash_ok": true,
108
+ "diff_drift": false,
109
+ "gpg_ok": "n/a",
110
+ "degraded": [],
111
+ "reason": "",
112
+ "ok": true
113
+ }
114
+ ```
115
+
116
+ You can share a receipt as a self-contained HTML page (`loki proof open <id>`),
117
+ or publish it as a GitHub Gist with `loki proof share <id>` (opt-in; the page is
118
+ redacted before it leaves your machine). An optional, off-by-default GPG detached
119
+ signature (`LOKI_PROOF_GPG_KEY`) lets a third party confirm the receipt came from
120
+ you.
121
+
122
+ ---
123
+
48
124
  ## Get Started in 30 Seconds
49
125
 
50
126
  ```bash
package/SKILL.md CHANGED
@@ -3,12 +3,14 @@ name: loki-mode
3
3
  description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.85.0
6
+ # Loki Mode v7.87.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
10
10
  **Spec in, verified product out.** Spec-driven: a "spec" is whatever describes the work -- a Markdown PRD, a GitHub issue, an OpenAPI doc, a Jira ticket (a PRD is one form of spec). The differentiator is the trust layer: Loki does not call work done until it is verified. The RARV-C closure loop, 8 quality gates, the completion council, and the verified-completion evidence gate must all clear before completion is accepted.
11
11
 
12
+ **Evidence Receipt (verify it yourself).** Every run writes a receipt to `.loki/proofs/<run_id>/` (opt out with `LOKI_PROOF=0`) that separates deterministic FACTS (git diff with base/head SHAs and a `diff_sha256`, the test command + exit code, the build command + exit code, each gate verdict) from AI ASSESSMENTS (the council verdict, labeled judgment not proof). The headline is computed only from the facts: VERIFIED (tests ran a real command and exited 0, diff non-empty, nothing skipped), VERIFIED WITH GAPS (each gap listed by name), or NOT VERIFIED (a check ran and failed). Inspect and re-check with `loki proof list|show <id>|verify <id>` (aliased `loki receipt`); `loki proof verify` re-hashes the receipt (tamper) and re-derives the diff from the recorded base SHA against the live repo (drift), exiting 0 clean / 1 tamper-or-drift. This is honesty-of-done, not a claim that the code is bug-free.
13
+
12
14
  **Provider-agnostic (stable since v5.0.0):** runs on Claude/Codex/Cline/Aider with abstract model tiers and degraded mode for non-Claude providers; no vendor lock-in. Gemini deprecated v7.5.18. See `skills/providers.md`. **Current track (v7.7.x):** LSP grounding as first-class agent tool (v7.7.0-v7.7.9; lsp_get_diagnostics actually-returns-diagnostics regression fix v7.7.14), provider_source cli (v7.7.11-v7.7.12 bash/bun parity), Docker/bash-3.2 robustness (v7.7.13), audit chain cross-file verification fix (v7.7.15), Phase 1 RARV-C closure (real provider judges, gate-failure flock, synthetic PRD e2e, status `--json`).
13
15
 
14
16
  **Runtime migration:** Bash-to-Bun migration. Read-only commands (`version`, `status`, `stats`, `doctor`, `provider show/list`, `memory list/index`) flow through Bun runtime via `bin/loki` since v7.3.0. Every other command remains on the Bash runtime (`autonomy/loki`). Rollback: `LOKI_LEGACY_BASH=1`. See `UPGRADING.md` and `docs/architecture/ADR-001-runtime-migration.md`.
@@ -406,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
406
408
 
407
409
  ---
408
410
 
409
- **v7.85.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.87.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.85.0
1
+ 7.87.0
@@ -253,6 +253,52 @@ def _collect_build(loki_dir):
253
253
  return out
254
254
 
255
255
 
256
+ def _collect_security(loki_dir):
257
+ """Read .loki/quality/security-findings.json (the secure-by-default gate).
258
+
259
+ Deterministic FACT (pattern scan, not an LLM opinion). Tolerates an absent
260
+ file -> status not_run. Counts only ACTIVE (un-waived) findings; HIGH active
261
+ findings are the gap signal. Shape:
262
+ {ran, total, active, waived, high_active, status, findings:[{rule,severity}]}.
263
+ status: not_run (no scan) | clean (ran, no active findings) | findings
264
+ (ran, active findings present).
265
+ """
266
+ out = {
267
+ "ran": False, "total": 0, "active": 0, "waived": 0,
268
+ "high_active": 0, "status": "not_run", "findings": [],
269
+ }
270
+ raw = _read_json(
271
+ os.path.join(loki_dir, "quality", "security-findings.json"), default=None
272
+ )
273
+ if not isinstance(raw, dict):
274
+ return out
275
+ out["ran"] = True
276
+ findings = raw.get("findings") if isinstance(raw.get("findings"), list) else []
277
+ total = active = waived = high_active = 0
278
+ slim = []
279
+ for f in findings:
280
+ if not isinstance(f, dict):
281
+ continue
282
+ total += 1
283
+ is_waived = bool(f.get("waived"))
284
+ sev = str(f.get("severity") or "").upper()
285
+ if is_waived:
286
+ waived += 1
287
+ else:
288
+ active += 1
289
+ if sev == "HIGH":
290
+ high_active += 1
291
+ slim.append({"rule": str(f.get("rule") or ""), "severity": sev,
292
+ "waived": is_waived})
293
+ out["total"] = total
294
+ out["active"] = active
295
+ out["waived"] = waived
296
+ out["high_active"] = high_active
297
+ out["findings"] = slim
298
+ out["status"] = "findings" if active > 0 else "clean"
299
+ return out
300
+
301
+
256
302
  def _norm_tests_status(raw):
257
303
  """Map a recorded test status to {verified,failed,inconclusive,not_run}.
258
304
 
@@ -578,6 +624,7 @@ def _build_proof(args, loki_dir, target_dir, repo_root):
578
624
 
579
625
  build = _collect_build(loki_dir)
580
626
  tests = _collect_tests(loki_dir)
627
+ security = _collect_security(loki_dir)
581
628
  evidence_gate = _collect_evidence_gate(loki_dir)
582
629
 
583
630
  deployed_url = os.environ.get("LOKI_DEPLOYED_URL") or None
@@ -611,6 +658,7 @@ def _build_proof(args, loki_dir, target_dir, repo_root):
611
658
  {"name": g.get("name", ""), "status": g.get("status", "not_run")}
612
659
  for g in (quality_gates.get("gates") or [])
613
660
  ],
661
+ "security": security,
614
662
  "cost": cost,
615
663
  "meta": {
616
664
  "run_id": run_id,
@@ -711,6 +759,15 @@ def _compute_degraded(facts):
711
759
  out.append({"item": "quality_gate:%s" % g.get("name", ""),
712
760
  "status": g.get("status"),
713
761
  "reason": "gate %s" % g.get("status")})
762
+ # Secure-by-default gate: an ACTIVE (un-waived) HIGH security finding is a gap
763
+ # in the proof of done -- the receipt must surface it, never green-wash an app
764
+ # that ships a known-bad pattern. Waived findings are NOT a gap (the user
765
+ # accepted them with intent, recorded in the receipt).
766
+ sec = facts.get("security") or {}
767
+ if sec.get("ran") and (sec.get("high_active") or 0) > 0:
768
+ out.append({"item": "security", "status": "findings",
769
+ "reason": "%s un-waived HIGH security finding(s)"
770
+ % sec.get("high_active")})
714
771
  git = facts.get("git") or {}
715
772
  if not (git.get("diff") or {}).get("count"):
716
773
  out.append({"item": "git.diff", "status": "not_run",
@@ -736,11 +793,18 @@ def _compute_headline(facts, degraded):
736
793
  # negative signal than a not-run one: amber means "we did not check
737
794
  # everything", red means "something we checked did not pass". Conflating them
738
795
  # would let a failed test render amber, which understates the failure.
796
+ # An ACTIVE (un-waived) HIGH security finding is a hard failure too: shipping a
797
+ # known-bad pattern (a committed private key, a world-open datastore) is not a
798
+ # "gap", it is a verified-NO. Waived findings do not count (accepted with
799
+ # intent). This keeps the receipt honest about security, not just tests.
800
+ sec = facts.get("security") or {}
801
+ sec_high = bool(sec.get("ran") and (sec.get("high_active") or 0) > 0)
739
802
  any_failed = (
740
803
  tests.get("status") == "failed"
741
804
  or build.get("status") == "failed"
742
805
  or any(g.get("status") == "failed"
743
806
  for g in (facts.get("quality_gates") or []))
807
+ or sec_high
744
808
  )
745
809
  if any_failed:
746
810
  return "NOT VERIFIED"