loki-mode 9.50.2 → 9.50.4
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/lib/proof-generator.py +7 -5
- package/autonomy/run.sh +264 -6
- package/dashboard/__init__.py +1 -1
- package/docs/COMPETITIVE-INTEL-2026-09.md +444 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/ONE-RUN-AUDIT.md +298 -0
- package/loki-ts/dist/loki.js +4 -4
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/skills/00-index.md +15 -0
- package/skills/factory-operations.md +508 -0
- package/skills/release-cadence.md +382 -0
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.50.
|
|
6
|
+
# Loki Mode v9.50.4
|
|
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.50.
|
|
473
|
+
**v9.50.4 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.50.
|
|
1
|
+
9.50.4
|
|
@@ -1536,11 +1536,13 @@ def _build_proof(args, loki_dir, target_dir, repo_root):
|
|
|
1536
1536
|
"honesty": honesty,
|
|
1537
1537
|
}
|
|
1538
1538
|
|
|
1539
|
-
# Top-level mirror for the intervention axis.
|
|
1540
|
-
# reads proof["interventions"]
|
|
1541
|
-
#
|
|
1542
|
-
#
|
|
1543
|
-
#
|
|
1539
|
+
# Top-level mirror for the intervention axis. _interventions_value in
|
|
1540
|
+
# trust_trajectory.py reads proof["interventions"]; the counter itself is
|
|
1541
|
+
# written by handle_pause in autonomy/run.sh, which increments
|
|
1542
|
+
# .loki/state/interventions.json once per blocking pause. Mirrored (not
|
|
1543
|
+
# moved) for the same back-compat reason the other flat keys are mirrored.
|
|
1544
|
+
# Only ever set when actually measured, so the axis stays honestly
|
|
1545
|
+
# "unavailable" rather than showing a fabricated zero.
|
|
1544
1546
|
if isinstance(journey, dict) and isinstance(journey.get("interventions"), int):
|
|
1545
1547
|
proof["interventions"] = journey["interventions"]
|
|
1546
1548
|
|
package/autonomy/run.sh
CHANGED
|
@@ -4332,6 +4332,63 @@ else:
|
|
|
4332
4332
|
' 2>/dev/null || true
|
|
4333
4333
|
}
|
|
4334
4334
|
|
|
4335
|
+
# _loki_receipt_facts <loki_dir>
|
|
4336
|
+
#
|
|
4337
|
+
# Echo three tab-separated facts about THIS run's Evidence Receipt, or nothing
|
|
4338
|
+
# at all: "<run_id>\t<proof_dir>\t<headline>".
|
|
4339
|
+
#
|
|
4340
|
+
# WHY THIS EXISTS (#209). The receipt is generated automatically and opt-OUT
|
|
4341
|
+
# (LOKI_PROOF defaults to 1), but the only place its location ever reached a
|
|
4342
|
+
# user was the TTFV first-run block, which fires solely on a zero-config first
|
|
4343
|
+
# run. A user who does not already know `loki proof` exists could therefore
|
|
4344
|
+
# finish a successful run without ever learning a checkable receipt was written.
|
|
4345
|
+
#
|
|
4346
|
+
# HONESTY RULES, deliberately fail-silent:
|
|
4347
|
+
# - Resolved from the PERSISTED pointer (.loki/state/last-proof-id.txt),
|
|
4348
|
+
# never newest-by-mtime, so it names the run the generator actually wrote.
|
|
4349
|
+
# - Emits NOTHING unless the pointer, the proof dir, and proof.json all exist.
|
|
4350
|
+
# the summary builder (build_completion_summary) is itself invoked mid-pause
|
|
4351
|
+
# before the teardown receipt on the success path, where no receipt exists
|
|
4352
|
+
# yet -- printing a path there would be a promise, not a fact.
|
|
4353
|
+
# - The headline is honesty.headline, which the generator computes
|
|
4354
|
+
# DETERMINISTICALLY from recorded facts (VERIFIED / VERIFIED WITH GAPS /
|
|
4355
|
+
# NOT VERIFIED). It is never the council verdict, which the generator
|
|
4356
|
+
# itself labels "AI judgment, not deterministic proof". A missing or
|
|
4357
|
+
# unreadable headline yields an empty third field, and the callers then
|
|
4358
|
+
# print the path without a verdict rather than inventing one.
|
|
4359
|
+
# Best-effort: never fails a run.
|
|
4360
|
+
_loki_receipt_facts() {
|
|
4361
|
+
local loki_dir="${1:-}"
|
|
4362
|
+
[ -n "$loki_dir" ] || return 0
|
|
4363
|
+
local id_file="$loki_dir/state/last-proof-id.txt"
|
|
4364
|
+
[ -s "$id_file" ] || return 0
|
|
4365
|
+
local rid=""
|
|
4366
|
+
rid="$(cat "$id_file" 2>/dev/null || true)"
|
|
4367
|
+
# Confine the run id to the alphabet the generator actually mints. The
|
|
4368
|
+
# headline is sanitized below; the id was not, so an interior newline or a
|
|
4369
|
+
# tab in the pointer split the tab-separated record and handed the consumers
|
|
4370
|
+
# a field boundary that is not there. Reject rather than repair: a pointer
|
|
4371
|
+
# outside this alphabet is corrupt, and a repaired id would name a directory
|
|
4372
|
+
# that does not exist.
|
|
4373
|
+
case "$rid" in
|
|
4374
|
+
''|*[!A-Za-z0-9._-]*) return 0 ;;
|
|
4375
|
+
esac
|
|
4376
|
+
[ -n "$rid" ] || return 0
|
|
4377
|
+
local pj="$loki_dir/proofs/$rid/proof.json"
|
|
4378
|
+
[ -f "$pj" ] || return 0
|
|
4379
|
+
local headline=""
|
|
4380
|
+
headline="$(python3 -c "
|
|
4381
|
+
import json, sys
|
|
4382
|
+
try:
|
|
4383
|
+
d = json.load(open(sys.argv[1]))
|
|
4384
|
+
h = (d.get('honesty') or {}).get('headline') or ''
|
|
4385
|
+
print(str(h).replace('\t', ' ').replace('\n', ' ').strip())
|
|
4386
|
+
except Exception:
|
|
4387
|
+
print('')" "$pj" 2>/dev/null || true)"
|
|
4388
|
+
printf '%s\t%s\t%s\n' "$rid" "$loki_dir/proofs/$rid" "$headline"
|
|
4389
|
+
return 0
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4335
4392
|
build_completion_summary() {
|
|
4336
4393
|
local outcome="${1:-complete}"
|
|
4337
4394
|
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
@@ -4366,6 +4423,20 @@ build_completion_summary() {
|
|
|
4366
4423
|
# checkable receipt. Name it.
|
|
4367
4424
|
council_force_approved) outcome_label="Completed (force-approved)"
|
|
4368
4425
|
notify_title="Run complete (force-approved)" ;;
|
|
4426
|
+
# The three gate-stuck terminals. Spelled out one literal arm each
|
|
4427
|
+
# rather than as a single wildcard pattern:
|
|
4428
|
+
# tests/test-completion-outcome-labels.sh derives outcomes from the call
|
|
4429
|
+
# sites and matches `^ *<outcome>\)`, so a wildcard arm renders correctly
|
|
4430
|
+
# at runtime while reading as "no label arm" to the guard. Literal arms
|
|
4431
|
+
# keep that guard strictly literal, which is what makes a NEWLY-added
|
|
4432
|
+
# outcome fail there instead of shipping as a raw enum. Same reason the
|
|
4433
|
+
# block above names each outcome.
|
|
4434
|
+
gate_stuck_static_analysis) outcome_label="Stopped (static analysis gate would not clear)"
|
|
4435
|
+
notify_title="Run stopped (gate not clearing)" ;;
|
|
4436
|
+
gate_stuck_mock_integrity) outcome_label="Stopped (mock integrity gate would not clear)"
|
|
4437
|
+
notify_title="Run stopped (gate not clearing)" ;;
|
|
4438
|
+
gate_stuck_mutation_integrity) outcome_label="Stopped (mutation integrity gate would not clear)"
|
|
4439
|
+
notify_title="Run stopped (gate not clearing)" ;;
|
|
4369
4440
|
*) outcome_label="$outcome"; notify_title="Run finished" ;;
|
|
4370
4441
|
esac
|
|
4371
4442
|
|
|
@@ -4867,6 +4938,49 @@ except Exception:
|
|
|
4867
4938
|
echo "Resolve those in the spec, then re-run."
|
|
4868
4939
|
;;
|
|
4869
4940
|
esac
|
|
4941
|
+
|
|
4942
|
+
# Evidence Receipt (#209). The receipt is written automatically on every
|
|
4943
|
+
# run, success or failure, but its location only ever reached the user
|
|
4944
|
+
# through the zero-config first-run block. State it here, where every
|
|
4945
|
+
# terminal outcome passes, so a user who has never heard of `loki proof`
|
|
4946
|
+
# still learns a checkable receipt exists and how to re-check it.
|
|
4947
|
+
# Prints NOTHING when no receipt has been written yet (mid-pause, or the
|
|
4948
|
+
# first summary on the success path): a path that does not exist is a
|
|
4949
|
+
# promise, not a fact. The verdict line is the generator's deterministic
|
|
4950
|
+
# headline, omitted entirely when unreadable rather than invented.
|
|
4951
|
+
_cs_receipt="$(_loki_receipt_facts "$loki_dir" 2>/dev/null || true)"
|
|
4952
|
+
if [ -n "$_cs_receipt" ]; then
|
|
4953
|
+
_cs_rid="${_cs_receipt%% *}"
|
|
4954
|
+
_cs_rest="${_cs_receipt#* }"
|
|
4955
|
+
_cs_dir="${_cs_rest%% *}"
|
|
4956
|
+
_cs_headline="${_cs_rest#* }"
|
|
4957
|
+
echo ""
|
|
4958
|
+
echo "Evidence Receipt (this run):"
|
|
4959
|
+
if [ -n "$_cs_headline" ]; then
|
|
4960
|
+
echo " Verdict: $_cs_headline"
|
|
4961
|
+
fi
|
|
4962
|
+
# Gated on the PAGE, not the receipt. The helper gates on
|
|
4963
|
+
# proof.json, but the generator writes proof.json and THEN renders
|
|
4964
|
+
# index.html unwrapped (proof-generator.py), so a render failure
|
|
4965
|
+
# leaves the data present and the page absent. Naming a page that
|
|
4966
|
+
# is not there is the same class proof.ts:118 refuses ("worse than
|
|
4967
|
+
# an absent one") and `loki proof open` refuses with "Proof page
|
|
4968
|
+
# not found". Gate only this line: proof verify reads proof.json,
|
|
4969
|
+
# so the id and the re-check below still work without the page.
|
|
4970
|
+
if [ -f "$_cs_dir/index.html" ]; then
|
|
4971
|
+
echo " Receipt: $_cs_dir/index.html"
|
|
4972
|
+
fi
|
|
4973
|
+
echo " Re-check it yourself, do not take our word for it:"
|
|
4974
|
+
# Cwd-independent: the receipt is resolved from the run's target
|
|
4975
|
+
# dir, which need not be where the user is standing. A bare
|
|
4976
|
+
# `loki proof verify <id>` then fails for exactly the user we just
|
|
4977
|
+
# told to check our work.
|
|
4978
|
+
if [ "$(cd "$loki_dir/.." 2>/dev/null && pwd -P)" = "$(pwd -P)" ]; then
|
|
4979
|
+
echo " loki proof verify $_cs_rid"
|
|
4980
|
+
else
|
|
4981
|
+
echo " (cd $(cd "$loki_dir/.." 2>/dev/null && pwd -P) && loki proof verify $_cs_rid)"
|
|
4982
|
+
fi
|
|
4983
|
+
fi
|
|
4870
4984
|
} > "$loki_dir/COMPLETION.txt" 2>/dev/null || true
|
|
4871
4985
|
|
|
4872
4986
|
# ---- Durable machine-readable file: .loki/state/completion.json -----------
|
|
@@ -5144,6 +5258,12 @@ EOF
|
|
|
5144
5258
|
force_stopped) _label="Stopped (not verified-complete)" ;;
|
|
5145
5259
|
failed) _label="Failed" ;;
|
|
5146
5260
|
intervention) _label="Needs input" ;;
|
|
5261
|
+
# Mirror of build_completion_summary's gate-stuck arms. Both must move
|
|
5262
|
+
# together or the card and COMPLETION.txt disagree on the same run.
|
|
5263
|
+
# Literal, not a glob, for the guard reason recorded there.
|
|
5264
|
+
gate_stuck_static_analysis) _label="Stopped (static analysis gate would not clear)" ;;
|
|
5265
|
+
gate_stuck_mock_integrity) _label="Stopped (mock integrity gate would not clear)" ;;
|
|
5266
|
+
gate_stuck_mutation_integrity) _label="Stopped (mutation integrity gate would not clear)" ;;
|
|
5147
5267
|
*) _label="$_outcome" ;;
|
|
5148
5268
|
esac
|
|
5149
5269
|
|
|
@@ -5199,6 +5319,20 @@ except Exception:
|
|
|
5199
5319
|
echo -e "${GREEN}|${NC}"
|
|
5200
5320
|
echo -e "${GREEN}|${NC} ${DIM}Review the work:${NC}"
|
|
5201
5321
|
echo -e "${GREEN}|${NC} ${_review}"
|
|
5322
|
+
# NO Evidence Receipt line here, deliberately. The card renders from inside
|
|
5323
|
+
# emit_completion_summary, which is reached only from within run_autonomous
|
|
5324
|
+
# -- thousands of lines BEFORE the teardown that generates this run's
|
|
5325
|
+
# receipt. Announcing here would therefore be silent on a normal success
|
|
5326
|
+
# run (no proof exists yet), and on a SECOND run in the same directory it
|
|
5327
|
+
# would have been actively wrong before #211: .loki/state/last-proof-id.txt
|
|
5328
|
+
# was never cleared at run start, so the card would read the PREVIOUS run's
|
|
5329
|
+
# pointer and print that receipt, with that run's verdict, as though it
|
|
5330
|
+
# described this run. The pointer is now cleared during run init (search
|
|
5331
|
+
# "Same reasoning for the proof pointer"), which closes the cross-run leak,
|
|
5332
|
+
# but this site stays silent anyway: it renders thousands of lines before
|
|
5333
|
+
# THIS run's receipt exists, so it would print nothing on a normal success
|
|
5334
|
+
# run. The announcement lives at the teardown instead, after the final
|
|
5335
|
+
# generate_proof_of_run, where the pointer is guaranteed current.
|
|
5202
5336
|
echo -e "${GREEN}+================================================================+${NC}"
|
|
5203
5337
|
echo ""
|
|
5204
5338
|
return 0
|
|
@@ -6529,6 +6663,15 @@ init_loki_dir() {
|
|
|
6529
6663
|
mkdir -p .loki/metrics/efficiency
|
|
6530
6664
|
# Clear stale metrics from previous sessions so loki metrics shows current run data (#75)
|
|
6531
6665
|
rm -f .loki/metrics/efficiency/iteration-*.json 2>/dev/null || true
|
|
6666
|
+
# Same reasoning for the proof pointer (#211). .loki/state/last-proof-id.txt
|
|
6667
|
+
# is written by generate_proof_of_run and was never cleared, so a run that
|
|
6668
|
+
# died before generating a proof left the PREVIOUS run's id behind. Every
|
|
6669
|
+
# reader then resolved a receipt describing different work, and
|
|
6670
|
+
# the summary builder (build_completion_summary) would print it under the
|
|
6671
|
+
# literal heading "Evidence
|
|
6672
|
+
# Receipt (this run):". A receipt naming the wrong run is worse than no
|
|
6673
|
+
# receipt: absence reads as "no data", a stale one reads as evidence.
|
|
6674
|
+
rm -f .loki/state/last-proof-id.txt 2>/dev/null || true
|
|
6532
6675
|
mkdir -p .loki/rules
|
|
6533
6676
|
mkdir -p .loki/signals
|
|
6534
6677
|
|
|
@@ -24098,6 +24241,14 @@ EOF
|
|
|
24098
24241
|
"gate=static_analysis" \
|
|
24099
24242
|
"consecutive=$sa_count" 2>/dev/null || true
|
|
24100
24243
|
save_state "${retry:-0}" "gate_stuck_static_analysis" 20 2>/dev/null || true
|
|
24244
|
+
# Same rule as the COUNCIL_FORCE_STOPPED terminal below
|
|
24245
|
+
# ("No on_run_complete: a force-stop must never open a
|
|
24246
|
+
# 'done' PR"): a non-verified stop never opens a PR, but
|
|
24247
|
+
# it MUST still write COMPLETION.txt and ping. Without
|
|
24248
|
+
# this, the only terminal in run_autonomous that tells
|
|
24249
|
+
# the user nothing is the one that stopped because a
|
|
24250
|
+
# gate would not clear.
|
|
24251
|
+
emit_completion_summary gate_stuck_static_analysis
|
|
24101
24252
|
return 20
|
|
24102
24253
|
fi
|
|
24103
24254
|
fi
|
|
@@ -24224,6 +24375,7 @@ EOF
|
|
|
24224
24375
|
"gate=mock_integrity" \
|
|
24225
24376
|
"consecutive=$mk_count" 2>/dev/null || true
|
|
24226
24377
|
save_state "${retry:-0}" "gate_stuck_mock_integrity" 20 2>/dev/null || true
|
|
24378
|
+
emit_completion_summary gate_stuck_mock_integrity
|
|
24227
24379
|
return 20
|
|
24228
24380
|
fi
|
|
24229
24381
|
;;
|
|
@@ -24272,6 +24424,7 @@ EOF
|
|
|
24272
24424
|
"gate=mutation_integrity" \
|
|
24273
24425
|
"consecutive=$mt_count" 2>/dev/null || true
|
|
24274
24426
|
save_state "${retry:-0}" "gate_stuck_mutation_integrity" 20 2>/dev/null || true
|
|
24427
|
+
emit_completion_summary gate_stuck_mutation_integrity
|
|
24275
24428
|
return 20
|
|
24276
24429
|
fi
|
|
24277
24430
|
fi
|
|
@@ -25728,18 +25881,45 @@ except Exception:
|
|
|
25728
25881
|
|
|
25729
25882
|
log_header "Execution Paused"
|
|
25730
25883
|
echo ""
|
|
25731
|
-
|
|
25884
|
+
# The keypress half of this line is only true on an interactive terminal.
|
|
25885
|
+
# Off a TTY (--bg, a container, a CI job) no key can be read, so advertising
|
|
25886
|
+
# it there tells the operator to do something that cannot work.
|
|
25887
|
+
if [ -t 0 ]; then
|
|
25888
|
+
log_info "To resume: Remove .loki/PAUSE or press Enter"
|
|
25889
|
+
else
|
|
25890
|
+
log_info "To resume: Remove .loki/PAUSE (no TTY: keypress resume unavailable)"
|
|
25891
|
+
fi
|
|
25732
25892
|
log_info "To add instructions: echo 'your instructions' > .loki/HUMAN_INPUT.md"
|
|
25733
25893
|
log_info "To stop completely: touch .loki/STOP"
|
|
25734
25894
|
echo ""
|
|
25735
25895
|
|
|
25736
|
-
# Create resume instructions file
|
|
25896
|
+
# Create resume instructions file.
|
|
25897
|
+
#
|
|
25898
|
+
# The resume line is built OUTSIDE the heredoc because the heredoc is quoted
|
|
25899
|
+
# (<< 'EOF') and must stay that way: its body contains `rm .loki/PAUSE` and
|
|
25900
|
+
# `touch .loki/STOP` in backticks, so unquoting to interpolate a variable
|
|
25901
|
+
# would execute them and delete the PAUSE file this function just wrote.
|
|
25902
|
+
#
|
|
25903
|
+
# This file is the surface a NON-INTERACTIVE operator actually reads (--bg,
|
|
25904
|
+
# a container, a CI job), and it was the last place still promising a
|
|
25905
|
+
# keypress that cannot arrive there -- the same defect the console banner
|
|
25906
|
+
# above already fixed. The no-TTY wording is byte-identical to that banner's
|
|
25907
|
+
# so one grep spans both surfaces and any future divergence is visible.
|
|
25908
|
+
local _resume_line
|
|
25909
|
+
if [ -t 0 ]; then
|
|
25910
|
+
_resume_line='1. **Resume**: Press Enter in terminal or `rm .loki/PAUSE`'
|
|
25911
|
+
else
|
|
25912
|
+
_resume_line='1. **Resume**: `rm .loki/PAUSE` (no TTY: keypress resume unavailable)'
|
|
25913
|
+
fi
|
|
25914
|
+
|
|
25737
25915
|
cat > "$loki_dir/PAUSED.md" << 'EOF'
|
|
25738
25916
|
# Loki Mode - Paused
|
|
25739
25917
|
|
|
25740
25918
|
Execution is currently paused. Options:
|
|
25741
25919
|
|
|
25742
|
-
|
|
25920
|
+
EOF
|
|
25921
|
+
printf '%s\n' "$_resume_line" >> "$loki_dir/PAUSED.md"
|
|
25922
|
+
cat >> "$loki_dir/PAUSED.md" << 'EOF'
|
|
25743
25923
|
2. **Add Instructions**: `echo "Focus on fixing the login bug" > .loki/HUMAN_INPUT.md`
|
|
25744
25924
|
3. **Stop**: `touch .loki/STOP`
|
|
25745
25925
|
|
|
@@ -25805,8 +25985,31 @@ except Exception:
|
|
|
25805
25985
|
break
|
|
25806
25986
|
fi
|
|
25807
25987
|
|
|
25808
|
-
# Check for any key press (non-blocking)
|
|
25809
|
-
|
|
25988
|
+
# Check for any key press (non-blocking). GATED ON AN INTERACTIVE STDIN
|
|
25989
|
+
# ([ -t 0 ], the established idiom in this file) because off a TTY this
|
|
25990
|
+
# arm is not merely useless, it is wrong in BOTH directions:
|
|
25991
|
+
#
|
|
25992
|
+
# 1. stdin is /dev/null (--bg, a container, a CI job): the read can
|
|
25993
|
+
# never succeed, so the loop spins on `sleep 1` forever with nobody
|
|
25994
|
+
# able to press anything. That is the reported hang.
|
|
25995
|
+
# 2. stdin is a PIPE OR FILE THAT HAS BYTES (stdin inherited from a
|
|
25996
|
+
# parent, a heredoc, `< somefile`): the read SUCCEEDS on the first
|
|
25997
|
+
# stray byte and the next line deletes .loki/PAUSE. A gate
|
|
25998
|
+
# escalation that paused at GATE_PAUSE_LIMIT is then silently
|
|
25999
|
+
# resumed by data nobody typed -- a false resume, which is worse
|
|
26000
|
+
# than the hang because the run continues past a blocking gate.
|
|
26001
|
+
#
|
|
26002
|
+
# Both were reproduced directly: with bytes on stdin `read -t 1 -n 1`
|
|
26003
|
+
# returns 0, with /dev/null it returns non-zero.
|
|
26004
|
+
#
|
|
26005
|
+
# The human escape path is UNCHANGED. The STOP and PAUSE-removal checks
|
|
26006
|
+
# above are file-based, run every second, and are what the dashboard,
|
|
26007
|
+
# the CLI, and `rm .loki/PAUSE` already use -- so a non-interactive
|
|
26008
|
+
# operator keeps every way out they had. Only the keypress, which that
|
|
26009
|
+
# operator never had, is skipped. On a real TTY this is byte-identical.
|
|
26010
|
+
# No timeout is imposed: a bounded wait would invent a new terminal
|
|
26011
|
+
# outcome and could fail a legitimate long human pause.
|
|
26012
|
+
if [ -t 0 ] && read -t 1 -n 1 2>/dev/null; then
|
|
25810
26013
|
rm -f "$loki_dir/PAUSE"
|
|
25811
26014
|
PAUSED=false
|
|
25812
26015
|
break
|
|
@@ -26980,6 +27183,56 @@ except Exception:
|
|
|
26980
27183
|
generate_proof_of_run "$result" || true
|
|
26981
27184
|
fi
|
|
26982
27185
|
|
|
27186
|
+
# Evidence Receipt (#209): tell the user, on screen, that a checkable
|
|
27187
|
+
# receipt exists and how to re-check it.
|
|
27188
|
+
#
|
|
27189
|
+
# POSITION IS LOAD-BEARING. This sits immediately after the FINAL
|
|
27190
|
+
# generate_proof_of_run, which is the only point where the receipt for THIS
|
|
27191
|
+
# run is guaranteed written and .loki/state/last-proof-id.txt is guaranteed
|
|
27192
|
+
# to point at it. Every earlier surface is either too early (the completion
|
|
27193
|
+
# card renders from inside run_autonomous, long before any proof exists) or
|
|
27194
|
+
# unreachable to a foreground user (COMPLETION.txt self-heals here, but only
|
|
27195
|
+
# a --bg launch is ever told to read it). Before #211 the pointer was never
|
|
27196
|
+
# cleared at run start, so announcing from an earlier site printed the
|
|
27197
|
+
# PREVIOUS run's receipt and verdict on a second run in the same directory.
|
|
27198
|
+
# Run init now clears it (search "Same reasoning for the proof pointer"), so
|
|
27199
|
+
# a stale pointer no longer survives into a new run; the position still
|
|
27200
|
+
# matters because an earlier site is simply too early for THIS run's proof.
|
|
27201
|
+
#
|
|
27202
|
+
# TTY-gated the same way print_ttfv_next_steps is above: machine output and
|
|
27203
|
+
# --bg stay byte-identical, and those readers already get the same facts
|
|
27204
|
+
# from COMPLETION.txt. Fail-silent and best-effort: prints nothing at all
|
|
27205
|
+
# when no receipt was written (LOKI_PROOF=0, or generation failed), and
|
|
27206
|
+
# never fails the run.
|
|
27207
|
+
if [ -t 1 ] && [ "${BACKGROUND_MODE:-false}" != "true" ]; then
|
|
27208
|
+
_rcpt="$(_loki_receipt_facts "${TARGET_DIR:-.}/.loki" 2>/dev/null || true)"
|
|
27209
|
+
if [ -n "${_rcpt:-}" ]; then
|
|
27210
|
+
_rcpt_id="${_rcpt%% *}"
|
|
27211
|
+
_rcpt_rest="${_rcpt#* }"
|
|
27212
|
+
_rcpt_dir="${_rcpt_rest%% *}"
|
|
27213
|
+
_rcpt_headline="${_rcpt_rest#* }"
|
|
27214
|
+
echo ""
|
|
27215
|
+
if [ -n "$_rcpt_headline" ]; then
|
|
27216
|
+
echo "Evidence Receipt for this run: $_rcpt_headline"
|
|
27217
|
+
else
|
|
27218
|
+
echo "Evidence Receipt for this run:"
|
|
27219
|
+
fi
|
|
27220
|
+
# Gated on the PAGE existing -- see the COMPLETION.txt site above.
|
|
27221
|
+
if [ -f "$_rcpt_dir/index.html" ]; then
|
|
27222
|
+
echo " $_rcpt_dir/index.html"
|
|
27223
|
+
fi
|
|
27224
|
+
echo " Re-check it yourself, do not take our word for it:"
|
|
27225
|
+
# Cwd-independent, same reasoning as the COMPLETION.txt site.
|
|
27226
|
+
_rcpt_root="$(cd "${TARGET_DIR:-.}" 2>/dev/null && pwd -P)"
|
|
27227
|
+
if [ "$_rcpt_root" = "$(pwd -P)" ]; then
|
|
27228
|
+
echo " loki proof verify $_rcpt_id"
|
|
27229
|
+
else
|
|
27230
|
+
echo " (cd $_rcpt_root && loki proof verify $_rcpt_id)"
|
|
27231
|
+
fi
|
|
27232
|
+
echo ""
|
|
27233
|
+
fi
|
|
27234
|
+
fi
|
|
27235
|
+
|
|
26983
27236
|
# Close the teardown window here rather than after cleanup: everything below
|
|
26984
27237
|
# is process reaping and file removal, while everything above is the work a
|
|
26985
27238
|
# user waits on (commit, PR, summary, proof). Emitting before cleanup also
|
|
@@ -27068,7 +27321,12 @@ except Exception:
|
|
|
27068
27321
|
# The operator raises the cap (or narrows the spec) and submits a
|
|
27069
27322
|
# NEW Job -- the same remedy as max_iterations_reached, which is why
|
|
27070
27323
|
# it shares that code.
|
|
27071
|
-
|
|
27324
|
+
# gate_stuck_* is deterministic for the same reason: the same gate
|
|
27325
|
+
# failed for the same reason N times, so a retry reaches the same
|
|
27326
|
+
# verdict. It already arrived here as 20 via save_state, but only
|
|
27327
|
+
# by falling through `*)`, which logs it as "crash, retryable" and
|
|
27328
|
+
# leaves a k8s podFailurePolicy reading a value nothing asserts.
|
|
27329
|
+
failed|max_iterations_reached|max_retries_exceeded|budget_exceeded|max_duration_reached|policy_blocked|inconclusive_spec_contradiction|force_stopped|gate_stuck_static_analysis|gate_stuck_mock_integrity|gate_stuck_mutation_integrity)
|
|
27072
27330
|
result=20 ;;
|
|
27073
27331
|
*)
|
|
27074
27332
|
# Unknown/running/exited terminal: leave $result as-is (nonzero on a
|