loki-mode 9.22.8 → 9.22.9

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
@@ -101,12 +101,17 @@ object instead of terminal text:
101
101
 
102
102
  ```bash
103
103
  loki quickstart "a todo app with user accounts" --dry-run --json > preview.json
104
+ loki quickstart --verify-preview preview.json --json
104
105
  loki quickstart --from-preview preview.json --yes
105
106
  ```
106
107
 
107
108
  The object contains the input kind, deterministic selected template (or `null`
108
109
  for an existing PRD), the exact estimator response under `plan`, and a bounded
109
110
  continuation containing the exact idea/template or the PRD path and SHA-256.
111
+ `--verify-preview` validates the same bounded duplicate-key-rejecting schema and
112
+ requires either a currently shipped idea template or the unchanged digest-bound
113
+ PRD, while emitting no idea or PRD path. It accepts a file or piped stdin and
114
+ returns before provider discovery, estimation, writes, or build execution.
110
115
  `--from-preview` requires explicit argv `--yes`, rejects malformed, conflicting,
111
116
  symlinked, or changed inputs before provider and build boundaries, then uses the
112
117
  existing no-clobber quickstart path. The saved plan is evidence rather than
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.22.8
6
+ # Loki Mode v9.22.9
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.22.8 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
473
+ **v9.22.9 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 9.22.8
1
+ 9.22.9
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+ """Read-only quality-gate policy reporting for CLI and dashboard consumers."""
3
+
4
+ import json
5
+ import os
6
+
7
+ SCHEMA_VERSION = 1
8
+
9
+ PROMOTABLE = {
10
+ "magic_debate": ("LOKI_GATE_MAGIC_DEBATE_BLOCKING", "true", "spec-vs-implementation debate"),
11
+ "test_coverage": ("LOKI_COV_ENFORCE", "1", "project test runner pass/fail"),
12
+ "policy_approval": ("LOKI_POLICY_APPROVAL_ENFORCE", "1", "staged-autonomy approval policy"),
13
+ }
14
+
15
+ ALWAYS_BLOCKING = {
16
+ "static_analysis": "static-analysis findings on the diff",
17
+ "code_review": "blind review Critical/High findings",
18
+ "mock_integrity": "tautological assertions and excessive mocking",
19
+ "mutation_integrity": "test-fitting assertion churn",
20
+ }
21
+
22
+
23
+ def _counts(loki_dir):
24
+ path = os.path.join(loki_dir, "quality", "gate-failure-count.json")
25
+ try:
26
+ with open(path, encoding="utf-8") as handle:
27
+ data = json.load(handle)
28
+ return data if isinstance(data, dict) else None
29
+ except (OSError, ValueError):
30
+ return None
31
+
32
+
33
+ def assess(loki_dir=".loki", env=None):
34
+ """Return policy without mutating files or environment.
35
+
36
+ `audit_hits` remains null when the ledger is absent or malformed. Reporting
37
+ zero there would falsely claim the gate ran and never fired.
38
+ """
39
+ env = os.environ if env is None else env
40
+ counts = _counts(loki_dir)
41
+ gates = []
42
+ for name, why in sorted(ALWAYS_BLOCKING.items()):
43
+ gates.append({
44
+ "gate": name,
45
+ "mode": "blocking",
46
+ "promotable": False,
47
+ "audit_hits": counts.get(name) if counts is not None else None,
48
+ "why": why,
49
+ "promote_with": None,
50
+ })
51
+ for name, (variable, value, why) in sorted(PROMOTABLE.items()):
52
+ enabled = str(env.get(variable, "")).strip().lower() in ("1", "true", "yes")
53
+ gates.append({
54
+ "gate": name,
55
+ "mode": "blocking" if enabled else "advisory",
56
+ "promotable": True,
57
+ "audit_hits": counts.get(name) if counts is not None else None,
58
+ "why": why,
59
+ "promote_with": None if enabled else f"{variable}={value}",
60
+ })
61
+ return {
62
+ "schema_version": SCHEMA_VERSION,
63
+ "status": "measured",
64
+ "ledger": "present" if counts is not None else "absent",
65
+ "gates": gates,
66
+ }
@@ -623,6 +623,86 @@ PY
623
623
  python3 -c "$validator_code" "$preview_path"
624
624
  }
625
625
 
626
+ # _qs_verify_preview <path> <json>: prove that one bounded schema-v1 preview is
627
+ # still actionable without crossing provider, estimator, PRD-write, or build
628
+ # boundaries. Idea previews must name a currently shipped template. PRD
629
+ # previews must still resolve to the exact readable non-symlink file digest.
630
+ # Output deliberately excludes the idea and PRD path.
631
+ _qs_verify_preview() {
632
+ local preview_path="$1" json_output="${2:-false}"
633
+ local fields="" kind="" template="" encoded="" digest="" value=""
634
+ fields=$(_qs_load_preview "$preview_path") || {
635
+ printf 'Preview JSON is malformed or incompatible.\n' >&2
636
+ return 2
637
+ }
638
+ IFS='|' read -r kind template encoded digest <<< "$fields"
639
+ value=$(python3 -c 'import base64,sys; sys.stdout.buffer.write(base64.b64decode(sys.argv[1], validate=True))' "$encoded" 2>/dev/null) || {
640
+ printf 'Preview JSON continuation is invalid.\n' >&2
641
+ return 2
642
+ }
643
+ [ -n "$value" ] || {
644
+ printf 'Preview JSON continuation is empty.\n' >&2
645
+ return 2
646
+ }
647
+
648
+ local verdict=""
649
+ if [ "$kind" = "idea" ]; then
650
+ if ! _qs_template_exists "$template"; then
651
+ printf 'Preview template is not currently shipped: %s\n' "$template" >&2
652
+ return 2
653
+ fi
654
+ verdict="SHIPPED_TEMPLATE_MATCH"
655
+ elif [ "$kind" = "prd" ]; then
656
+ if [ ! -f "$value" ] || [ ! -r "$value" ] || [ -L "$value" ]; then
657
+ printf 'Preview PRD is not a readable regular non-symlink file.\n' >&2
658
+ return 2
659
+ fi
660
+ local current_digest=""
661
+ current_digest=$(shasum -a 256 "$value" 2>/dev/null | awk '{print $1}') || current_digest=""
662
+ if [ -z "$current_digest" ] || [ "$current_digest" != "$digest" ]; then
663
+ printf 'Preview PRD has changed since the saved preview; run --dry-run --json again.\n' >&2
664
+ return 2
665
+ fi
666
+ verdict="EXACT_PRD_MATCH"
667
+ else
668
+ printf 'Preview JSON continuation kind is unsupported.\n' >&2
669
+ return 2
670
+ fi
671
+
672
+ if [ "$json_output" = true ]; then
673
+ python3 - "$kind" "$template" "$digest" "$verdict" <<'PY'
674
+ import json
675
+ import sys
676
+
677
+ kind, template, digest, verdict = sys.argv[1:5]
678
+ json.dump(
679
+ {
680
+ "command": "loki quickstart",
681
+ "input_kind": kind,
682
+ "mode": "verify-preview",
683
+ "prd_sha256": digest if kind == "prd" else None,
684
+ "schema_version": 1,
685
+ "selected_template": template if kind == "idea" else None,
686
+ "valid": True,
687
+ "verdict": verdict,
688
+ },
689
+ sys.stdout,
690
+ separators=(",", ":"),
691
+ sort_keys=True,
692
+ )
693
+ sys.stdout.write("\n")
694
+ PY
695
+ return $?
696
+ fi
697
+
698
+ if [ "$kind" = "idea" ]; then
699
+ printf 'VERIFIED / %s / template=%s\n' "$verdict" "$template"
700
+ else
701
+ printf 'VERIFIED / %s / sha256=%s\n' "$verdict" "$digest"
702
+ fi
703
+ return 0
704
+ }
705
+
626
706
  # _qs_help: concise usage for `loki quickstart --help`.
627
707
  _qs_help() {
628
708
  printf '%sloki quickstart%s - guided first build (setup, idea, template, plan, go)\n' "$_QS_BOLD" "$_QS_NC"
@@ -639,8 +719,9 @@ _qs_help() {
639
719
  printf 'Options:\n'
640
720
  printf ' --yes, -y Auto-confirm the final build prompt (still shows the plan)\n'
641
721
  printf ' --dry-run Preview the selected template and plan; write/start nothing\n'
642
- printf ' --json With --dry-run, emit one machine-readable JSON object\n'
722
+ printf ' --json Emit machine-readable output for a read-only command\n'
643
723
  printf ' --from-preview F Continue saved JSON from file F (or - for piped stdin); requires --yes\n'
724
+ printf ' --verify-preview F Verify saved JSON from file F (or - for piped stdin); executes nothing\n'
644
725
  printf ' --template N Use the exact shipped template N for an IDEA\n'
645
726
  printf ' --list-templates List every shipped template and its purpose\n'
646
727
  printf ' --help, -h Show this help and exit\n'
@@ -660,6 +741,7 @@ _qs_help() {
660
741
  printf ' Add --json for versioned JSON only; --json requires --dry-run.\n'
661
742
  printf ' Save that JSON, then continue it with --from-preview FILE --yes.\n'
662
743
  printf ' Or pipe it with --from-preview - --yes; terminal stdin is refused.\n'
744
+ printf ' Verify it without execution using --verify-preview FILE (and optional --json).\n'
663
745
  printf '\n'
664
746
  printf 'Steps:\n'
665
747
  printf ' 1. Setup Check for an AI provider for execution (skipped in preview)\n'
@@ -697,6 +779,8 @@ cmd_quickstart() {
697
779
  local list_templates_flag_seen=false
698
780
  local from_preview=""
699
781
  local from_preview_flag_seen=false
782
+ local verify_preview=""
783
+ local verify_preview_flag_seen=false
700
784
  local preview_prd_digest=""
701
785
  if _qs_assume_yes; then assume_yes=true; fi
702
786
 
@@ -765,6 +849,19 @@ cmd_quickstart() {
765
849
  from_preview="$2"
766
850
  shift 2
767
851
  ;;
852
+ --verify-preview)
853
+ if [ "$verify_preview_flag_seen" = true ]; then
854
+ printf '%s--verify-preview may be specified only once.%s\n' "$_QS_RED" "$_QS_NC" >&2
855
+ exit 2
856
+ fi
857
+ verify_preview_flag_seen=true
858
+ if [ $# -lt 2 ] || [ -z "${2:-}" ] || [[ "${2:-}" == --* ]]; then
859
+ printf '%s--verify-preview requires a preview JSON path.%s\n' "$_QS_RED" "$_QS_NC" >&2
860
+ exit 2
861
+ fi
862
+ verify_preview="$2"
863
+ shift 2
864
+ ;;
768
865
  --*)
769
866
  printf '%sUnknown option: %s%s\n' "$_QS_RED" "$1" "$_QS_NC" >&2
770
867
  printf "Run 'loki quickstart --help' for usage.\n" >&2
@@ -783,6 +880,18 @@ cmd_quickstart() {
783
880
  esac
784
881
  done
785
882
 
883
+ # Verification is a standalone read-only shape. It accepts only optional
884
+ # JSON formatting, validates the full current continuation boundary, and
885
+ # returns before terminal, provider, estimator, PRD-write, and build seams.
886
+ if [ "$verify_preview_flag_seen" = true ]; then
887
+ if [ -n "$positional" ] || [ "$yes_flag" = true ] || [ "$dry_run" = true ] || [ "$template_flag_seen" = true ] || [ "$list_templates" = true ] || [ "$from_preview_flag_seen" = true ]; then
888
+ printf '%s--verify-preview accepts only a preview JSON path and optional --json.%s\n' "$_QS_RED" "$_QS_NC" >&2
889
+ exit 2
890
+ fi
891
+ _qs_verify_preview "$verify_preview" "$json_output"
892
+ return $?
893
+ fi
894
+
786
895
  # Continuation is a standalone execution shape. Explicit argv consent is
787
896
  # mandatory, and no caller-supplied input or selector may compete with the
788
897
  # reviewed preview. Validation happens before every provider, estimator,
package/autonomy/run.sh CHANGED
@@ -1709,12 +1709,15 @@ fi
1709
1709
  if [ "$BASH_VERSION_MAJOR" -ge 4 ] 2>/dev/null; then
1710
1710
  declare -A WORKTREE_PIDS=()
1711
1711
  declare -A WORKTREE_PATHS=()
1712
+ declare -A WORKTREE_BASE_SHAS=()
1712
1713
  else
1713
1714
  # Fallback: parallel mode will check and warn
1714
1715
  # shellcheck disable=SC2178
1715
1716
  WORKTREE_PIDS=""
1716
1717
  # shellcheck disable=SC2178
1717
1718
  WORKTREE_PATHS=""
1719
+ # shellcheck disable=SC2178
1720
+ WORKTREE_BASE_SHAS=""
1718
1721
  fi
1719
1722
  # Track background install PIDs for cleanup (indexed array, works on all bash versions)
1720
1723
  WORKTREE_INSTALL_PIDS=()
@@ -5184,6 +5187,81 @@ ${_del_receipt}"
5184
5187
  # Parallel Workflow Functions (Git Worktrees)
5185
5188
  #===============================================================================
5186
5189
 
5190
+ # Production bridge for the Bun execution-manifest intelligence. The feature is
5191
+ # explicitly opt-in; with LOKI_EXEC_MANIFEST unset this performs no I/O and the
5192
+ # legacy parallel workflow remains byte-for-byte on its old path.
5193
+ _loki_exec_manifest() {
5194
+ [ "${LOKI_EXEC_MANIFEST:-0}" = "1" ] || return 0
5195
+ bun "${SCRIPT_DIR}/../loki-ts/dist/loki.js" internal exec-manifest "$@"
5196
+ }
5197
+
5198
+ init_exec_manifest() {
5199
+ [ "${LOKI_EXEC_MANIFEST:-0}" = "1" ] || return 0
5200
+ local base_sha plan_file
5201
+ # create_worktree's built-in streams branch from main (with HEAD only as its
5202
+ # final fallback), so the manifest must pin that exact production base.
5203
+ base_sha=$(git -C "$TARGET_DIR" rev-parse main 2>/dev/null) || \
5204
+ base_sha=$(git -C "$TARGET_DIR" rev-parse HEAD 2>/dev/null) || return 1
5205
+ mkdir -p "${TARGET_DIR}/.loki"
5206
+ plan_file=$(mktemp "${TARGET_DIR}/.loki/.exec-manifest-plan.XXXXXX") || return 1
5207
+ LOKI_PLAN_FILE="$plan_file" LOKI_BASE_SHA="$base_sha" \
5208
+ LOKI_PARALLEL_TESTING_VALUE="$PARALLEL_TESTING" \
5209
+ LOKI_PARALLEL_DOCS_VALUE="$PARALLEL_DOCS" \
5210
+ LOKI_PARALLEL_BLOG_VALUE="$PARALLEL_BLOG" python3 <<'PY'
5211
+ import json, os
5212
+ streams = []
5213
+ if os.environ["LOKI_PARALLEL_TESTING_VALUE"] == "true":
5214
+ streams.append({"name": "testing", "paths": ["tests", "loki-ts/tests"],
5215
+ "acceptance": "test stream exits successfully"})
5216
+ if os.environ["LOKI_PARALLEL_DOCS_VALUE"] == "true":
5217
+ streams.append({"name": "docs", "paths": ["docs", "README.md", "CHANGELOG.md"],
5218
+ "acceptance": "documentation stream exits successfully"})
5219
+ if os.environ["LOKI_PARALLEL_BLOG_VALUE"] == "true":
5220
+ streams.append({"name": "blog", "paths": ["blog"],
5221
+ "acceptance": "blog stream exits successfully"})
5222
+ with open(os.environ["LOKI_PLAN_FILE"], "w") as f:
5223
+ json.dump({"baseSha": os.environ["LOKI_BASE_SHA"],
5224
+ "integrationOwner": "parallel-orchestrator",
5225
+ "streams": streams,
5226
+ "env": {"LOKI_EXEC_MANIFEST": "1"}}, f)
5227
+ PY
5228
+ _loki_exec_manifest plan "$plan_file" "${TARGET_DIR}/.loki" >/dev/null
5229
+ local rc=$?
5230
+ rm -f "$plan_file"
5231
+ return "$rc"
5232
+ }
5233
+
5234
+ validate_exec_manifest_result() {
5235
+ local stream_name="$1" branch="$2"
5236
+ [ "${LOKI_EXEC_MANIFEST:-0}" = "1" ] || return 0
5237
+ local base_sha result_file
5238
+ base_sha="${WORKTREE_BASE_SHAS[$stream_name]:-}"
5239
+ [ -n "$base_sha" ] || base_sha=$(git -C "$TARGET_DIR" merge-base "$branch" HEAD 2>/dev/null) || return 1
5240
+ result_file=$(mktemp "${TARGET_DIR}/.loki/.exec-manifest-result.XXXXXX") || return 1
5241
+ LOKI_RESULT_FILE="$result_file" LOKI_RESULT_STREAM="$stream_name" \
5242
+ LOKI_RESULT_BASE="$base_sha" LOKI_RESULT_BRANCH="$branch" \
5243
+ LOKI_RESULT_REPO="$TARGET_DIR" python3 <<'PY'
5244
+ import json, os, subprocess
5245
+ paths = subprocess.check_output(
5246
+ ["git", "-C", os.environ["LOKI_RESULT_REPO"], "diff", "--name-only",
5247
+ f'{os.environ["LOKI_RESULT_BASE"]}..{os.environ["LOKI_RESULT_BRANCH"]}'],
5248
+ text=True).splitlines()
5249
+ with open(os.environ["LOKI_RESULT_FILE"], "w") as f:
5250
+ json.dump({"name": os.environ["LOKI_RESULT_STREAM"],
5251
+ "baseSha": os.environ["LOKI_RESULT_BASE"],
5252
+ "changedPaths": paths, "acceptanceMet": True}, f)
5253
+ PY
5254
+ local outcome rc=0
5255
+ outcome=$(_loki_exec_manifest validate "$result_file" "${TARGET_DIR}/.loki" 2>&1) || rc=$?
5256
+ rm -f "$result_file"
5257
+ if [ "$rc" -ne 0 ]; then
5258
+ log_error "Execution manifest rejected $stream_name: $outcome"
5259
+ return 1
5260
+ fi
5261
+ log_info "Execution manifest accepted: $stream_name"
5262
+ return 0
5263
+ }
5264
+
5187
5265
  # Check if parallel mode is supported (bash 4+ required for associative arrays)
5188
5266
  check_parallel_support() {
5189
5267
  if [ "$BASH_VERSION_MAJOR" -lt 4 ] 2>/dev/null; then
@@ -5211,6 +5289,9 @@ create_worktree() {
5211
5289
  if [ -d "$worktree_path" ]; then
5212
5290
  log_info "Worktree already exists: $stream_name"
5213
5291
  WORKTREE_PATHS[$stream_name]="$worktree_path"
5292
+ if [ "${LOKI_EXEC_MANIFEST:-0}" = "1" ]; then
5293
+ WORKTREE_BASE_SHAS[$stream_name]="$(python3 -c "import json; print(json.load(open('${TARGET_DIR}/.loki/manifest/exec-manifest.json'))['base_sha'])" 2>/dev/null)"
5294
+ fi
5214
5295
  return 0
5215
5296
  fi
5216
5297
 
@@ -5231,6 +5312,7 @@ create_worktree() {
5231
5312
 
5232
5313
  if [ $wt_exit -eq 0 ]; then
5233
5314
  WORKTREE_PATHS[$stream_name]="$worktree_path"
5315
+ WORKTREE_BASE_SHAS[$stream_name]="$(git -C "$worktree_path" rev-parse HEAD 2>/dev/null)"
5234
5316
 
5235
5317
  # Copy .loki state to worktree
5236
5318
  if [ -d "$TARGET_DIR/.loki" ]; then
@@ -5304,6 +5386,7 @@ remove_worktree() {
5304
5386
 
5305
5387
  unset "WORKTREE_PATHS[$stream_name]"
5306
5388
  unset "WORKTREE_PIDS[$stream_name]"
5389
+ unset "WORKTREE_BASE_SHAS[$stream_name]"
5307
5390
 
5308
5391
  log_info "Removed worktree: $stream_name"
5309
5392
  }
@@ -5609,6 +5692,12 @@ merge_worktree() {
5609
5692
 
5610
5693
  log_step "Merging worktree: $stream_name (branch: $branch)"
5611
5694
 
5695
+ # Validate the actual branch result at the integration seam, immediately
5696
+ # before any checkout or merge can mutate the integration tree.
5697
+ if ! validate_exec_manifest_result "$stream_name" "$branch"; then
5698
+ return 1
5699
+ fi
5700
+
5612
5701
  # BUG-PAR-009: Verify git checkout main before merge
5613
5702
  local current_branch
5614
5703
  current_branch=$(git -C "${TARGET_DIR:-.}" branch --show-current 2>/dev/null)
@@ -5797,8 +5886,25 @@ merge_feature() {
5797
5886
  local clean_feature="${feature#feature-}"
5798
5887
  local branch="feature/$clean_feature"
5799
5888
 
5889
+ # The session signal is authoritative for worktree branches (for example
5890
+ # parallel-testing). The historical feature/<name> convention remains the
5891
+ # fallback for legacy feature streams.
5892
+ local _mf_signal="$TARGET_DIR/.loki/signals/MERGE_REQUESTED_$feature"
5893
+ if [ -f "$_mf_signal" ]; then
5894
+ local _mf_branch=""
5895
+ _mf_branch=$(LOKI_SIGNAL_FILE="$_mf_signal" python3 -c \
5896
+ "import json,os; print(json.load(open(os.environ['LOKI_SIGNAL_FILE'])).get('branch',''))" 2>/dev/null || true)
5897
+ [ -n "$_mf_branch" ] && branch="$_mf_branch"
5898
+ fi
5899
+
5800
5900
  log_step "Merging feature: $clean_feature"
5801
5901
 
5902
+ # This is the autonomous orchestrator's real integration seam. Reject a
5903
+ # stale, out-of-scope, unknown, or unsuccessful stream before checkout/merge.
5904
+ if ! validate_exec_manifest_result "$feature" "$branch"; then
5905
+ return 1
5906
+ fi
5907
+
5802
5908
  # BUG-PAR-011: Ensure we're on main using git -C (no subshell)
5803
5909
  git -C "$TARGET_DIR" checkout main 2>/dev/null
5804
5910
 
@@ -5847,6 +5953,11 @@ init_parallel_streams() {
5847
5953
 
5848
5954
  log_header "Initializing Parallel Workflows"
5849
5955
 
5956
+ if ! init_exec_manifest; then
5957
+ log_error "Failed to initialize execution manifest"
5958
+ return 1
5959
+ fi
5960
+
5850
5961
  local active_streams=0
5851
5962
 
5852
5963
  # Create testing worktree (always tracks main)
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "9.22.8"
10
+ __version__ = "9.22.9"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -8361,6 +8361,61 @@ async def get_trust_trajectory():
8361
8361
  return traj
8362
8362
 
8363
8363
 
8364
+ # Gate policy is derived in one deterministic module so the dashboard never
8365
+ # invents whether a gate blocks or whether an absent measurement means zero.
8366
+ _GATE_POLICY_MODULE = None
8367
+
8368
+
8369
+ def _load_gate_policy_module():
8370
+ global _GATE_POLICY_MODULE
8371
+ if _GATE_POLICY_MODULE is not None:
8372
+ return _GATE_POLICY_MODULE
8373
+ repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
8374
+ module_path = os.path.join(repo_root, "autonomy", "lib", "gate_policy.py")
8375
+ if not os.path.isfile(module_path):
8376
+ return None
8377
+ try:
8378
+ import importlib.util as importlib_util
8379
+
8380
+ spec = importlib_util.spec_from_file_location("gate_policy", module_path)
8381
+ if spec is None or spec.loader is None:
8382
+ return None
8383
+ module = importlib_util.module_from_spec(spec)
8384
+ spec.loader.exec_module(module)
8385
+ _GATE_POLICY_MODULE = module
8386
+ return module
8387
+ except Exception:
8388
+ return None
8389
+
8390
+
8391
+ @app.get("/api/gate-policy", dependencies=[Depends(auth.require_scope("read"))])
8392
+ async def get_gate_policy():
8393
+ """Report blocking/advisory policy; never promote or mutate a gate."""
8394
+ module = _load_gate_policy_module()
8395
+ if module is None:
8396
+ return {
8397
+ "schema_version": 1,
8398
+ "available": False,
8399
+ "status": "unavailable",
8400
+ "ledger": "absent",
8401
+ "gates": [],
8402
+ "error": "gate_policy module not found",
8403
+ }
8404
+ try:
8405
+ result = module.assess(str(_get_loki_dir()))
8406
+ except Exception as error:
8407
+ return {
8408
+ "schema_version": 1,
8409
+ "available": False,
8410
+ "status": "unavailable",
8411
+ "ledger": "absent",
8412
+ "gates": [],
8413
+ "error": f"gate policy assessment failed: {error}",
8414
+ }
8415
+ result["available"] = True
8416
+ return result
8417
+
8418
+
8364
8419
  # =============================================================================
8365
8420
  # Pricing API
8366
8421
  # =============================================================================
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v9.8.1
5
+ **Version:** v9.22.9
6
6
 
7
7
  ---
8
8