loki-mode 7.89.1 → 7.90.1

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
@@ -119,6 +119,21 @@ redacted before it leaves your machine). An optional, off-by-default GPG detache
119
119
  signature (`LOKI_PROOF_GPG_KEY`) lets a third party confirm the receipt came from
120
120
  you.
121
121
 
122
+ ### Proven PR
123
+
124
+ When Loki opens a pull request, the PR body includes the Evidence Receipt
125
+ summary, so a reviewer does not have to take the agent on faith. It shows the
126
+ honest verdict (VERIFIED / VERIFIED WITH GAPS / NOT VERIFIED), the key facts
127
+ (diff hash, tests, secure-gate, cost), and a "verify this yourself" line:
128
+ `loki proof verify <id>` against the recorded base SHA. A green claim appears
129
+ only when the receipt's own headline is VERIFIED. This is on by default whenever
130
+ Loki opens or advises a PR; opt out with `LOKI_PROVEN_PR=0`.
131
+
132
+ An optional advisory status check (`loki: verified-completion`) maps the verdict
133
+ to a GitHub check-run. It is opt-in (`LOKI_PROVEN_PR_CHECK=1`) and can never block
134
+ a merge on its own. To make verified-completion blocking, add it as a required
135
+ status check in your repository's branch-protection settings.
136
+
122
137
  ---
123
138
 
124
139
  ## Get Started in 30 Seconds
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.89.1
6
+ # Loki Mode v7.90.1
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
408
408
 
409
409
  ---
410
410
 
411
- **v7.89.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.90.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.89.1
1
+ 7.90.1
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env bash
2
+ # proof-check.sh -- best-effort, advisory GitHub check-run for a Loki run.
3
+ #
4
+ # Posts an ADVISORY check-run named "loki: verified-completion" to a PR's head
5
+ # commit, mapping the deterministic honesty headline from the redacted proof.json
6
+ # 1:1 to a check-run conclusion. This surface is purely advisory.
7
+ #
8
+ # HONEST FRAMING (load-bearing invariant): Loki CANNOT block a merge. This posts
9
+ # an advisory check-run only. It NEVER sets any merge gate, NEVER marks the check
10
+ # as required, NEVER calls any merge-gate API. Requiring this check is the repo
11
+ # owner's setting (add "loki: verified-completion" as a required status check in
12
+ # repository settings). We do not overclaim.
13
+ #
14
+ # HONESTY (single source of truth): the ONLY input that can produce a green
15
+ # (success) conclusion is honesty.headline == "VERIFIED" read from the redacted
16
+ # proof.json. We never recompute a verdict, never read raw .loki state, never
17
+ # infer a conclusion from anything but the headline. A missing/unknown headline
18
+ # posts NOTHING (no fabricated green or red).
19
+ #
20
+ # set -e SAFE: this lib may be sourced under `set -uo pipefail` or
21
+ # `set -euo pipefail`. Every fallible command ends with `|| true` or sits in a
22
+ # guarded `if`; every optional tool is `command -v`-guarded; every var is
23
+ # defaulted with `${VAR:-}`; all paths `return 0` so a sourced call cannot abort
24
+ # the caller. This is pure best-effort: it NEVER fails the caller and NEVER
25
+ # blocks PR creation.
26
+ #
27
+ # This function is only ever called when the operator opted in (the call site
28
+ # guards on LOKI_PROVEN_PR_CHECK=1). It is also safe if called directly.
29
+
30
+ # Double-source guard.
31
+ [ -n "${_PROOF_CHECK_SH:-}" ] && return 0
32
+ _PROOF_CHECK_SH=1
33
+
34
+ # _proof_check_net <cmd...>
35
+ # Best-effort timeout wrapper so a hung network call cannot stall the caller.
36
+ # Mirrors the run.sh _loki_net idiom. Never fatal.
37
+ _proof_check_net() {
38
+ if command -v timeout >/dev/null 2>&1; then
39
+ timeout 30 "$@"
40
+ else
41
+ "$@"
42
+ fi
43
+ }
44
+
45
+ # _proof_check_headline <proof_json_path>
46
+ # Echoes the exact honesty.headline string from the redacted proof.json, or an
47
+ # empty string if the file is missing/unreadable/not-a-dict or the headline is
48
+ # absent. Reads ONLY the passed proof.json, NEVER raw .loki state. Best-effort.
49
+ _proof_check_headline() {
50
+ local proof_path="${1:-}"
51
+ [ -n "$proof_path" ] || { printf '%s' ""; return 0; }
52
+ [ -f "$proof_path" ] || { printf '%s' ""; return 0; }
53
+ command -v python3 >/dev/null 2>&1 || { printf '%s' ""; return 0; }
54
+
55
+ local headline=""
56
+ headline="$(python3 - "$proof_path" <<'PY' 2>/dev/null || true
57
+ import json, sys
58
+ try:
59
+ with open(sys.argv[1], "r", encoding="utf-8") as fh:
60
+ data = json.load(fh)
61
+ if not isinstance(data, dict):
62
+ sys.exit(0)
63
+ honesty = data.get("honesty")
64
+ if not isinstance(honesty, dict):
65
+ sys.exit(0)
66
+ h = honesty.get("headline")
67
+ if isinstance(h, str):
68
+ sys.stdout.write(h)
69
+ except Exception:
70
+ sys.exit(0)
71
+ PY
72
+ )"
73
+ printf '%s' "${headline:-}"
74
+ return 0
75
+ }
76
+
77
+ # _proof_check_proof_head_sha <proof_json_path>
78
+ # Echoes facts.git.head_sha from the redacted proof.json (the fallback head sha),
79
+ # or empty string. Reads ONLY the passed proof.json. Best-effort.
80
+ _proof_check_proof_head_sha() {
81
+ local proof_path="${1:-}"
82
+ [ -n "$proof_path" ] || { printf '%s' ""; return 0; }
83
+ [ -f "$proof_path" ] || { printf '%s' ""; return 0; }
84
+ command -v python3 >/dev/null 2>&1 || { printf '%s' ""; return 0; }
85
+
86
+ local sha=""
87
+ sha="$(python3 - "$proof_path" <<'PY' 2>/dev/null || true
88
+ import json, sys
89
+ try:
90
+ with open(sys.argv[1], "r", encoding="utf-8") as fh:
91
+ data = json.load(fh)
92
+ if not isinstance(data, dict):
93
+ sys.exit(0)
94
+ facts = data.get("facts")
95
+ if not isinstance(facts, dict):
96
+ sys.exit(0)
97
+ git = facts.get("git")
98
+ if not isinstance(git, dict):
99
+ sys.exit(0)
100
+ s = git.get("head_sha")
101
+ if isinstance(s, str):
102
+ sys.stdout.write(s.strip())
103
+ except Exception:
104
+ sys.exit(0)
105
+ PY
106
+ )"
107
+ printf '%s' "${sha:-}"
108
+ return 0
109
+ }
110
+
111
+ # _proof_check_run_id <proof_json_path>
112
+ # Echoes run_id from the redacted proof.json (for the verify-yourself hint in the
113
+ # advisory summary), or empty string. Best-effort.
114
+ _proof_check_run_id() {
115
+ local proof_path="${1:-}"
116
+ [ -n "$proof_path" ] || { printf '%s' ""; return 0; }
117
+ [ -f "$proof_path" ] || { printf '%s' ""; return 0; }
118
+ command -v python3 >/dev/null 2>&1 || { printf '%s' ""; return 0; }
119
+
120
+ local rid=""
121
+ rid="$(python3 - "$proof_path" <<'PY' 2>/dev/null || true
122
+ import json, sys
123
+ try:
124
+ with open(sys.argv[1], "r", encoding="utf-8") as fh:
125
+ data = json.load(fh)
126
+ if not isinstance(data, dict):
127
+ sys.exit(0)
128
+ r = data.get("run_id")
129
+ if isinstance(r, str):
130
+ sys.stdout.write(r)
131
+ except Exception:
132
+ sys.exit(0)
133
+ PY
134
+ )"
135
+ printf '%s' "${rid:-}"
136
+ return 0
137
+ }
138
+
139
+ # post_verified_completion_check <proof_json_path> <pr_url_or_empty>
140
+ #
141
+ # Best-effort: post an advisory GitHub check-run "loki: verified-completion" to
142
+ # the PR's head commit. Maps honesty.headline 1:1 to the check-run conclusion:
143
+ # VERIFIED -> success
144
+ # VERIFIED WITH GAPS -> neutral
145
+ # NOT VERIFIED -> failure
146
+ # A missing/unknown headline posts NOTHING. ALWAYS returns 0; NEVER fails the
147
+ # caller; NEVER blocks PR creation. This is advisory only: it NEVER makes the
148
+ # check required and NEVER calls any merge-gate API. Requiring the check is the
149
+ # repo owner's setting.
150
+ post_verified_completion_check() {
151
+ local proof_path="${1:-}"
152
+ local pr_url="${2:-}"
153
+
154
+ # --- Honesty gate FIRST: read + map the headline before touching gh. -------
155
+ # A missing/unknown headline must produce NO gh call at all (no fabricated
156
+ # green or red).
157
+ local headline=""
158
+ headline="$(_proof_check_headline "$proof_path")"
159
+
160
+ local conclusion=""
161
+ case "$headline" in
162
+ "VERIFIED") conclusion="success" ;;
163
+ "VERIFIED WITH GAPS") conclusion="neutral" ;;
164
+ "NOT VERIFIED") conclusion="failure" ;;
165
+ *)
166
+ # Missing or unknown headline -> do not post anything.
167
+ return 0
168
+ ;;
169
+ esac
170
+
171
+ # --- gh availability + auth (best-effort, never fatal). --------------------
172
+ if ! command -v gh >/dev/null 2>&1; then
173
+ printf '%s\n' "loki: advisory check-run not posted (gh CLI not found)." || true
174
+ return 0
175
+ fi
176
+ if ! _proof_check_net gh auth status >/dev/null 2>&1; then
177
+ printf '%s\n' "loki: advisory check-run not posted (gh not authenticated)." || true
178
+ return 0
179
+ fi
180
+
181
+ # --- Resolve owner/repo (nameWithOwner). ----------------------------------
182
+ # Prefer the current repo context (Loki's model is same-repo branch PRs).
183
+ local repo=""
184
+ repo="$(_proof_check_net gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)"
185
+
186
+ # --- Resolve head sha: PR head if a pr_url is given, else proof fallback. --
187
+ local head_sha=""
188
+ if [ -n "$pr_url" ]; then
189
+ head_sha="$(_proof_check_net gh pr view "$pr_url" --json headRefOid -q .headRefOid 2>/dev/null || true)"
190
+ fi
191
+ if [ -z "$head_sha" ]; then
192
+ head_sha="$(_proof_check_proof_head_sha "$proof_path")"
193
+ fi
194
+
195
+ # If we cannot identify both the repo and the head commit, do not guess.
196
+ if [ -z "$repo" ] || [ -z "$head_sha" ]; then
197
+ printf '%s\n' "loki: advisory check-run not posted (could not resolve repo or head commit)." || true
198
+ return 0
199
+ fi
200
+
201
+ # --- Build the advisory summary text. -------------------------------------
202
+ # Plainly states this is advisory and how the OWNER can make it blocking.
203
+ local run_id=""
204
+ run_id="$(_proof_check_run_id "$proof_path")"
205
+
206
+ local verify_line="Verify it yourself: loki proof verify"
207
+ if [ -n "$run_id" ]; then
208
+ verify_line="Verify it yourself: loki proof verify ${run_id}"
209
+ fi
210
+
211
+ local summary=""
212
+ summary="This is an advisory check posted by Loki. It reports the deterministic verified-completion headline (${headline}) for this run. Loki does not enforce a merge gate and does not make this check required. To make this gate blocking, the repository owner must add \"loki: verified-completion\" as a required status check in repository settings. ${verify_line}"
213
+
214
+ # --- Post the advisory check-run (best-effort). ---------------------------
215
+ # gh api supports nested fields via key[subkey]=value, so conclusion appears
216
+ # as a direct flat parameter. We POST to the check-runs endpoint only; we
217
+ # NEVER touch any merge-gate / required-status endpoint.
218
+ if _proof_check_net gh api \
219
+ -X POST \
220
+ "repos/${repo}/check-runs" \
221
+ -f "name=loki: verified-completion" \
222
+ -f "head_sha=${head_sha}" \
223
+ -f "status=completed" \
224
+ -f "conclusion=${conclusion}" \
225
+ -f "output[title]=loki: verified-completion" \
226
+ -f "output[summary]=${summary}" \
227
+ >/dev/null 2>&1; then
228
+ printf '%s\n' "loki: posted advisory check-run \"loki: verified-completion\" (${conclusion})." || true
229
+ else
230
+ printf '%s\n' "loki: advisory check-run not posted (gh API error or insufficient check permission)." || true
231
+ fi
232
+
233
+ return 0
234
+ }
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env bash
2
+ # proof-pr.sh -- shared, PRINT-ONLY Evidence Receipt renderer for PR bodies.
3
+ #
4
+ # LOAD-BEARING INVARIANT: this lib is pure and print-only. It NEVER runs
5
+ # `git push`, NEVER runs `gh pr create`, NEVER mutates the repo, NEVER posts a
6
+ # check-run. It reads ONLY the already-redacted proof.json passed to it (past the
7
+ # redaction chokepoint at proof-generator.py:1086) and prints a markdown block
8
+ # that gets appended into a PR body. It is the single source of truth sourced by
9
+ # autonomy/run.sh (the three create_session_pr / body-file / delegate PR sites)
10
+ # and autonomy/loki (cmd_github) so every PR surface renders a byte-identical,
11
+ # correct receipt and cannot drift. Mirrors the contract of git-pr-advisory.sh.
12
+ #
13
+ # HONESTY GATE (R-HON-1): the ONLY input that may produce a green/VERIFIED claim
14
+ # is honesty.headline == "VERIFIED" read from the redacted proof.json. The
15
+ # renderer NEVER recomputes a verdict, NEVER reads council/LLM opinion to turn
16
+ # green, NEVER infers VERIFIED from a bare pass.
17
+ #
18
+ # DETERMINISM (R-DET-2): when an expected_head_sha is supplied AND the proof is
19
+ # VERIFIED, the renderer cross-checks it against facts.git.head_sha; on mismatch
20
+ # it does NOT render green, it prints an honest "does not match this branch head"
21
+ # line. Production callers pass an empty expected_head_sha (the session commit at
22
+ # run.sh sits BETWEEN proof generation and PR creation, so the post-commit branch
23
+ # head is structurally offset from the proof's pre-commit head; feeding it would
24
+ # false-degrade every legitimate receipt). The anti-stale guarantee on the
25
+ # production path is R-DET-1: the persisted run_id pointer (.loki/state/
26
+ # last-proof-id.txt), not a head comparison. The R-DET-2 capability stays intact
27
+ # and is exercised by the SDET fixtures.
28
+ #
29
+ # set -e SAFE: this lib may be sourced under `set -uo pipefail` (run.sh) AND
30
+ # `set -euo pipefail` (loki). Every fallible command ends with `|| true` or sits
31
+ # in a guarded `if`; no bare `((..))`; every var defaulted with `${VAR:-}`;
32
+ # every optional tool is `command -v`-guarded. All print paths `return 0` so a
33
+ # sourced call cannot abort the caller under set -e.
34
+
35
+ # Double-source guard.
36
+ [ -n "${_PROOF_PR_SH:-}" ] && return 0
37
+ _PROOF_PR_SH=1
38
+
39
+ # render_evidence_receipt_md <proof_json_path> [expected_head_sha] [expected_base_sha]
40
+ # Prints the Evidence Receipt markdown block for a PR body. PRINT-ONLY: never
41
+ # pushes, never creates a PR, never mutates the repo. Always returns 0. A missing
42
+ # or unreadable proof prints ONE honest "unavailable" line and returns 0 so it
43
+ # can NEVER crash the caller or block PR creation.
44
+ #
45
+ # expected_base_sha is accepted for call-site symmetry but is intentionally
46
+ # unused for any green/red gate: the proof's base is a sha (_LOKI_ITER_START_SHA)
47
+ # while a PR base is a branch NAME, so a base comparison would misfire. The base
48
+ # is informational only and is printed from the proof itself.
49
+ render_evidence_receipt_md() {
50
+ local proof_json_path="${1:-}"
51
+ local expected_head_sha="${2:-}"
52
+ local _expected_base_sha="${3:-}"
53
+ : "$_expected_base_sha"
54
+
55
+ # No python3 -> degrade honestly, never crash.
56
+ if ! command -v python3 >/dev/null 2>&1; then
57
+ printf '%s\n' "Evidence Receipt: unavailable for this run."
58
+ return 0
59
+ fi
60
+
61
+ # Pass every input as argv (sys.argv), NEVER interpolated into the heredoc:
62
+ # quote-safe and injection-safe against a hostile proof path. The heredoc
63
+ # delimiter is quoted so bash performs no expansion inside the program.
64
+ # Capture into a var so a non-zero python exit degrades honestly without a
65
+ # brace group on the heredoc command (which bash mis-parses). The program
66
+ # always handles its own errors and prints, so a non-zero exit is a last
67
+ # resort (interpreter crash) -> print the single honest line.
68
+ local _receipt_out=""
69
+ local _receipt_rc=0
70
+ _receipt_out="$(python3 - "$proof_json_path" "$expected_head_sha" <<'PROOF_PR_PY' 2>/dev/null
71
+ import json
72
+ import sys
73
+
74
+
75
+ def _line(s=""):
76
+ sys.stdout.write(s + "\n")
77
+
78
+
79
+ def main():
80
+ argv = sys.argv[1:]
81
+ proof_path = argv[0] if len(argv) > 0 else ""
82
+ expected_head = (argv[1] if len(argv) > 1 else "").strip()
83
+
84
+ if not proof_path:
85
+ _line("Evidence Receipt: unavailable for this run.")
86
+ return 0
87
+ try:
88
+ with open(proof_path, "r") as f:
89
+ proof = json.load(f)
90
+ except Exception:
91
+ _line("Evidence Receipt: unavailable for this run.")
92
+ return 0
93
+ if not isinstance(proof, dict):
94
+ _line("Evidence Receipt: unavailable for this run.")
95
+ return 0
96
+
97
+ honesty = proof.get("honesty")
98
+ honesty = honesty if isinstance(honesty, dict) else {}
99
+ headline = str(honesty.get("headline") or "").strip()
100
+ if not headline:
101
+ _line("Evidence Receipt: unavailable for this run.")
102
+ return 0
103
+
104
+ facts = proof.get("facts")
105
+ facts = facts if isinstance(facts, dict) else {}
106
+ git = facts.get("git") if isinstance(facts.get("git"), dict) else {}
107
+ tests = facts.get("tests") if isinstance(facts.get("tests"), dict) else {}
108
+ build = facts.get("build") if isinstance(facts.get("build"), dict) else {}
109
+ security = facts.get("security") if isinstance(facts.get("security"), dict) else {}
110
+ cost = facts.get("cost") if isinstance(facts.get("cost"), dict) else {}
111
+ meta = facts.get("meta") if isinstance(facts.get("meta"), dict) else {}
112
+
113
+ diff = git.get("diff") if isinstance(git.get("diff"), dict) else {}
114
+ diff_count = diff.get("count")
115
+ diff_sha = str(git.get("diff_sha256") or "")
116
+ base_sha = str(git.get("base_sha") or "")
117
+ head_sha = str(git.get("head_sha") or "")
118
+
119
+ # run_id: prefer facts.meta.run_id, fall back to the top-level mirror.
120
+ run_id = str(meta.get("run_id") or proof.get("run_id") or "").strip()
121
+
122
+ # R-DET-2 cross-check. Only a supplied expected_head AND a VERIFIED headline
123
+ # can trigger it. On mismatch we do NOT print a VERIFIED banner; we print an
124
+ # honest line so a stale / wrong-run proof fails safe, never fake-green.
125
+ head_mismatch = (
126
+ bool(expected_head)
127
+ and headline == "VERIFIED"
128
+ and head_sha != ""
129
+ and head_sha != expected_head
130
+ )
131
+
132
+ # ---- Render -----------------------------------------------------------
133
+ _line("### Evidence Receipt")
134
+ _line()
135
+
136
+ if head_mismatch:
137
+ _line(
138
+ "Evidence Receipt: available but does not match this branch head "
139
+ "(proof head " + (head_sha or "(none)")
140
+ + ", branch head " + (expected_head or "(none)") + "). Run "
141
+ "`loki proof verify " + (run_id or "<run_id>") + "` to inspect."
142
+ )
143
+ _line()
144
+ # Fall through: still render facts + verify-yourself so the reviewer can
145
+ # check, but emit NO green headline label.
146
+ effective_headline = ""
147
+ else:
148
+ # Headline mapped 1:1 from honesty.headline. Plain text label, NO color
149
+ # codes -- this goes into a PR body. R-HON-1: only VERIFIED is green.
150
+ effective_headline = headline
151
+ _line("Headline: " + headline)
152
+ _line()
153
+
154
+ # Facts table. Deterministic, non-LLM facts a skeptic can recompute.
155
+ def _stat(d):
156
+ s = str(d.get("status") or "").strip()
157
+ return s if s else "not_run"
158
+
159
+ tests_cell = _stat(tests)
160
+ tests_cmd = str(tests.get("command") or "").strip()
161
+ if tests_cmd:
162
+ tests_cell = tests_cell + " (`" + tests_cmd + "`)"
163
+ build_cell = _stat(build)
164
+ build_cmd = str(build.get("command") or "").strip()
165
+ if build_cmd:
166
+ build_cell = build_cell + " (`" + build_cmd + "`)"
167
+
168
+ sec_cell = _stat(security)
169
+ if security.get("ran"):
170
+ ha = security.get("high_active") or 0
171
+ try:
172
+ ha = int(ha)
173
+ except Exception:
174
+ ha = 0
175
+ if ha > 0:
176
+ sec_cell = sec_cell + " (" + str(ha) + " un-waived HIGH)"
177
+
178
+ cost_usd = cost.get("usd")
179
+ cost_cell = "not recorded" if cost_usd is None else ("$" + str(cost_usd))
180
+
181
+ files_cell = "0" if diff_count is None else str(diff_count)
182
+
183
+ _line("| Fact | Value |")
184
+ _line("| --- | --- |")
185
+ _line("| Files changed | " + files_cell + " |")
186
+ _line("| Diff sha256 | `" + (diff_sha or "(none)") + "` |")
187
+ _line("| Tests | " + tests_cell + " |")
188
+ _line("| Build | " + build_cell + " |")
189
+ _line("| Security | " + sec_cell + " |")
190
+ _line("| Cost | " + cost_cell + " |")
191
+ _line("| Base sha | `" + (base_sha or "(none)") + "` |")
192
+ _line("| Head sha | `" + (head_sha or "(none)") + "` |")
193
+ _line()
194
+
195
+ # Gaps: when headline != VERIFIED, list honesty.degraded[] verbatim. By
196
+ # _compute_degraded design an empty list with a non-VERIFIED headline is
197
+ # impossible, but guard anyway (R-HON-2).
198
+ if effective_headline != "VERIFIED":
199
+ degraded = honesty.get("degraded")
200
+ degraded = degraded if isinstance(degraded, list) else []
201
+ if degraded:
202
+ _line("Not yet verified:")
203
+ for d in degraded:
204
+ if not isinstance(d, dict):
205
+ continue
206
+ item = str(d.get("item") or "").strip()
207
+ status = str(d.get("status") or "").strip()
208
+ reason = str(d.get("reason") or "").strip()
209
+ _line(
210
+ "- " + (item or "(item)")
211
+ + ": " + (status or "(status)")
212
+ + (" -- " + reason if reason else "")
213
+ )
214
+ _line()
215
+
216
+ # Verify-yourself block. ALWAYS rendered, even on NOT VERIFIED -- the whole
217
+ # point is that the reviewer can recompute the verdict and does not have to
218
+ # trust Loki.
219
+ _line("You do not have to trust this. Verify it yourself:")
220
+ _line()
221
+ _line("```")
222
+ _line(
223
+ "loki proof verify " + (run_id or "<run_id>")
224
+ + " (base " + (base_sha or "(none)") + ")"
225
+ )
226
+ _line("```")
227
+ _line()
228
+
229
+ # What the headline means: a first-time reviewer must understand that
230
+ # VERIFIED WITH GAPS is honest, not a failure.
231
+ _line(
232
+ "What the headline means: VERIFIED means every recorded check passed; "
233
+ "VERIFIED WITH GAPS means the checks that ran passed but some checks "
234
+ "were not run (listed above); NOT VERIFIED means a check failed or "
235
+ "nothing could be verified. The headline is computed only from "
236
+ "deterministic, re-derivable facts, never from an AI opinion."
237
+ )
238
+ return 0
239
+
240
+
241
+ sys.exit(main())
242
+ PROOF_PR_PY
243
+ )" || _receipt_rc=$?
244
+
245
+ if [ "$_receipt_rc" != "0" ] || [ -z "$_receipt_out" ]; then
246
+ printf '%s\n' "Evidence Receipt: unavailable for this run."
247
+ return 0
248
+ fi
249
+ printf '%s\n' "$_receipt_out"
250
+ return 0
251
+ }
package/autonomy/loki CHANGED
@@ -125,6 +125,14 @@ if [ -f "$_LOKI_SCRIPT_DIR/lib/git-pr-advisory.sh" ]; then
125
125
  source "$_LOKI_SCRIPT_DIR/lib/git-pr-advisory.sh"
126
126
  fi
127
127
 
128
+ # Proven PR (Loop 6): shared print-only Evidence Receipt renderer for PR bodies
129
+ # (render_evidence_receipt_md). Sourced so cmd_github can append the receipt to
130
+ # the PR body. Self-guarded against double-source; existence-guarded.
131
+ if [ -f "$_LOKI_SCRIPT_DIR/lib/proof-pr.sh" ]; then
132
+ # shellcheck source=autonomy/lib/proof-pr.sh
133
+ source "$_LOKI_SCRIPT_DIR/lib/proof-pr.sh"
134
+ fi
135
+
128
136
  # Unified config-file support (#691). Canonical LOKI_CONFIG_MAP + the config-file
129
137
  # loader (loki_maybe_apply_config_file / loki_apply_config_file) and the
130
138
  # example/schema/validate generators. Side-effect-free on source: defines the
@@ -7574,7 +7582,37 @@ if [[ "$LOKI_CREATE_PR" == "true" ]]; then
7574
7582
  branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
7575
7583
  if [[ -n "$branch_current" && "$branch_current" != "main" && "$branch_current" != "master" ]]; then
7576
7584
  git push origin "$branch_current" 2>/dev/null || true
7577
- gh pr create --title "$LOKI_PR_TITLE" --body "Implemented by Loki Mode" --head "$branch_current" 2>/dev/null || true
7585
+ # Proven PR (v7.90.0): render this run's Evidence Receipt into the PR body
7586
+ # so even the detached --pr/--ship path proves itself. The proof + the
7587
+ # run-id pointer (.loki/state/last-proof-id.txt) exist by now (the
7588
+ # `loki start` above generated them). On by default (LOKI_PROVEN_PR=0
7589
+ # opts out); degrades safely to the plain body if anything is missing.
7590
+ # The renderer is print-only + honesty-gated (green only when the proof
7591
+ # headline is VERIFIED); it can never make a receipt-less PR look proven.
7592
+ _pr_body="Implemented by Loki Mode"
7593
+ if [[ "${LOKI_PROVEN_PR:-1}" != "0" ]]; then
7594
+ # Resolve the renderer from the ALREADY-RESOLVED autonomy dir passed
7595
+ # in by the parent (LOKI_SCRIPT_DIR_RESOLVED = _LOKI_SCRIPT_DIR, which
7596
+ # is symlink-safe). Do NOT recompute from `command -v loki` -- on
7597
+ # npm/bun/brew global installs that is an UNRESOLVED bin symlink, so
7598
+ # dirname-based guessing misses the lib and the receipt silently no-ops
7599
+ # on the dominant install channels (council cIb_r2). Fall back to the
7600
+ # old dirname guesses only if the resolved dir was not passed.
7601
+ _pp_lib="${LOKI_SCRIPT_DIR_RESOLVED:-}/lib/proof-pr.sh"
7602
+ [[ -f "$_pp_lib" ]] || _pp_lib="$(dirname "$LOKI_CMD")/../autonomy/lib/proof-pr.sh"
7603
+ [[ -f "$_pp_lib" ]] || _pp_lib="$(dirname "$LOKI_CMD")/autonomy/lib/proof-pr.sh"
7604
+ _pp_id=""
7605
+ [[ -f .loki/state/last-proof-id.txt ]] && _pp_id="$(cat .loki/state/last-proof-id.txt 2>/dev/null)"
7606
+ _pp_proof=".loki/proofs/${_pp_id}/proof.json"
7607
+ if [[ -n "$_pp_id" && -f "$_pp_proof" && -f "$_pp_lib" ]]; then
7608
+ # shellcheck source=/dev/null
7609
+ if source "$_pp_lib" 2>/dev/null && declare -f render_evidence_receipt_md >/dev/null 2>&1; then
7610
+ _pp_block="$(render_evidence_receipt_md "$_pp_proof" "" "" 2>/dev/null || true)"
7611
+ [[ -n "$_pp_block" ]] && _pr_body="Implemented by Loki Mode"$'\n\n'"$_pp_block"
7612
+ fi
7613
+ fi
7614
+ fi
7615
+ gh pr create --title "$LOKI_PR_TITLE" --body "$_pr_body" --head "$branch_current" 2>/dev/null || true
7578
7616
  fi
7579
7617
  fi
7580
7618
  # Post-completion: auto-merge if requested
@@ -7592,6 +7630,7 @@ INNER_SCRIPT_EOF
7592
7630
  # Pass all variables safely via environment
7593
7631
  LOKI_RUN_DIR="$(pwd)" \
7594
7632
  LOKI_CMD="$loki_cmd" \
7633
+ LOKI_SCRIPT_DIR_RESOLVED="$_LOKI_SCRIPT_DIR" \
7595
7634
  LOKI_SESSION_ID="$session_id" \
7596
7635
  LOKI_WORKTREE_BRANCH="$branch_name" \
7597
7636
  LOKI_PRD_PATH="$detach_prd" \
@@ -7682,6 +7721,28 @@ Provider: ${issue_provider}
7682
7721
  ## Changes
7683
7722
  $(git log --oneline "main..HEAD" 2>/dev/null || echo "See diff")"
7684
7723
 
7724
+ # Proven PR (Loop 6): append the Evidence Receipt to the body. One
7725
+ # append covers BOTH the gh and glab branches below. Default-on;
7726
+ # LOKI_PROVEN_PR=0 -> body byte-identical to before. The proof lives
7727
+ # under the project .loki resolved from the run_id pointer; empty
7728
+ # expected_head_sha by design (R-DET-1 run_id pointer is the guard).
7729
+ if [ "${LOKI_PROVEN_PR:-1}" != "0" ] && declare -f render_evidence_receipt_md >/dev/null 2>&1; then
7730
+ local _gh_idfile=".loki/state/last-proof-id.txt"
7731
+ if [ -s "$_gh_idfile" ]; then
7732
+ local _gh_rid=""
7733
+ _gh_rid="$(cat "$_gh_idfile" 2>/dev/null || true)"
7734
+ if [ -n "$_gh_rid" ] && [ -f ".loki/proofs/$_gh_rid/proof.json" ]; then
7735
+ local _gh_receipt=""
7736
+ _gh_receipt="$(render_evidence_receipt_md ".loki/proofs/$_gh_rid/proof.json" "" "" 2>/dev/null || true)"
7737
+ if [ -n "$_gh_receipt" ]; then
7738
+ pr_body="${pr_body}
7739
+
7740
+ ${_gh_receipt}"
7741
+ fi
7742
+ fi
7743
+ fi
7744
+ fi
7745
+
7685
7746
  case "${issue_provider:-github}" in
7686
7747
  github)
7687
7748
  if command -v gh &>/dev/null; then