loki-mode 9.22.7 → 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.7
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.7 | [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.7
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
+ }
@@ -509,21 +509,29 @@ json.dump(payload, sys.stdout, separators=(",", ":"), sort_keys=True)
509
509
  # cmd_quickstart always recomputes and displays the current estimator result.
510
510
  _qs_load_preview() {
511
511
  local preview_path="$1"
512
- if [ ! -f "$preview_path" ] || [ ! -r "$preview_path" ] || [ -L "$preview_path" ]; then
513
- printf 'Preview path is not a readable regular non-symlink file: %s\n' "$preview_path" >&2
514
- return 2
515
- fi
516
- local preview_size
517
- preview_size=$(wc -c < "$preview_path" 2>/dev/null | tr -d '[:space:]') || return 2
518
- case "$preview_size" in
519
- ""|*[!0-9]*) return 2;;
520
- esac
521
- if [ "$preview_size" -eq 0 ] || [ "$preview_size" -gt 1048576 ]; then
522
- printf 'Preview JSON must be between 1 byte and 1 MiB.\n' >&2
523
- return 2
512
+ if [ "$preview_path" = "-" ]; then
513
+ if [ -t 0 ]; then
514
+ printf 'Preview stdin must be piped; refusing to wait on a terminal.\n' >&2
515
+ return 2
516
+ fi
517
+ else
518
+ if [ ! -f "$preview_path" ] || [ ! -r "$preview_path" ] || [ -L "$preview_path" ]; then
519
+ printf 'Preview path is not a readable regular non-symlink file: %s\n' "$preview_path" >&2
520
+ return 2
521
+ fi
522
+ local preview_size
523
+ preview_size=$(wc -c < "$preview_path" 2>/dev/null | tr -d '[:space:]') || return 2
524
+ case "$preview_size" in
525
+ ""|*[!0-9]*) return 2;;
526
+ esac
527
+ if [ "$preview_size" -eq 0 ] || [ "$preview_size" -gt 1048576 ]; then
528
+ printf 'Preview JSON must be between 1 byte and 1 MiB.\n' >&2
529
+ return 2
530
+ fi
524
531
  fi
525
532
 
526
- python3 - "$preview_path" <<'PY'
533
+ local validator_code=""
534
+ validator_code=$(cat <<'PY'
527
535
  import base64
528
536
  import json
529
537
  import os
@@ -533,15 +541,20 @@ import sys
533
541
 
534
542
  path = sys.argv[1]
535
543
  try:
536
- flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0)
537
- descriptor = os.open(path, flags)
538
- try:
539
- metadata = os.fstat(descriptor)
540
- if not stat.S_ISREG(metadata.st_mode) or metadata.st_size < 1 or metadata.st_size > 1048576:
541
- raise ValueError("unsafe preview")
542
- raw = os.read(descriptor, 1048577)
543
- finally:
544
- os.close(descriptor)
544
+ if path == "-":
545
+ raw = sys.stdin.buffer.read(1048577)
546
+ if len(raw) < 1 or len(raw) > 1048576:
547
+ raise ValueError("unsafe preview stdin")
548
+ else:
549
+ flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0)
550
+ descriptor = os.open(path, flags)
551
+ try:
552
+ metadata = os.fstat(descriptor)
553
+ if not stat.S_ISREG(metadata.st_mode) or metadata.st_size < 1 or metadata.st_size > 1048576:
554
+ raise ValueError("unsafe preview")
555
+ raw = os.read(descriptor, 1048577)
556
+ finally:
557
+ os.close(descriptor)
545
558
  if len(raw) > 1048576:
546
559
  raise ValueError("oversized")
547
560
  def reject_duplicate_keys(pairs):
@@ -606,6 +619,88 @@ elif kind == "prd":
606
619
  else:
607
620
  sys.exit(2)
608
621
  PY
622
+ )
623
+ python3 -c "$validator_code" "$preview_path"
624
+ }
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
609
704
  }
610
705
 
611
706
  # _qs_help: concise usage for `loki quickstart --help`.
@@ -624,8 +719,9 @@ _qs_help() {
624
719
  printf 'Options:\n'
625
720
  printf ' --yes, -y Auto-confirm the final build prompt (still shows the plan)\n'
626
721
  printf ' --dry-run Preview the selected template and plan; write/start nothing\n'
627
- printf ' --json With --dry-run, emit one machine-readable JSON object\n'
628
- printf ' --from-preview F Continue a saved JSON preview; requires explicit --yes\n'
722
+ printf ' --json Emit machine-readable output for a read-only command\n'
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'
629
725
  printf ' --template N Use the exact shipped template N for an IDEA\n'
630
726
  printf ' --list-templates List every shipped template and its purpose\n'
631
727
  printf ' --help, -h Show this help and exit\n'
@@ -644,6 +740,8 @@ _qs_help() {
644
740
  printf ' no file is written, and no build is started. Do not combine with --yes.\n'
645
741
  printf ' Add --json for versioned JSON only; --json requires --dry-run.\n'
646
742
  printf ' Save that JSON, then continue it with --from-preview FILE --yes.\n'
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'
647
745
  printf '\n'
648
746
  printf 'Steps:\n'
649
747
  printf ' 1. Setup Check for an AI provider for execution (skipped in preview)\n'
@@ -681,6 +779,8 @@ cmd_quickstart() {
681
779
  local list_templates_flag_seen=false
682
780
  local from_preview=""
683
781
  local from_preview_flag_seen=false
782
+ local verify_preview=""
783
+ local verify_preview_flag_seen=false
684
784
  local preview_prd_digest=""
685
785
  if _qs_assume_yes; then assume_yes=true; fi
686
786
 
@@ -749,6 +849,19 @@ cmd_quickstart() {
749
849
  from_preview="$2"
750
850
  shift 2
751
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
+ ;;
752
865
  --*)
753
866
  printf '%sUnknown option: %s%s\n' "$_QS_RED" "$1" "$_QS_NC" >&2
754
867
  printf "Run 'loki quickstart --help' for usage.\n" >&2
@@ -767,6 +880,18 @@ cmd_quickstart() {
767
880
  esac
768
881
  done
769
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
+
770
895
  # Continuation is a standalone execution shape. Explicit argv consent is
771
896
  # mandatory, and no caller-supplied input or selector may compete with the
772
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.7"
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
 
@@ -194,6 +194,18 @@ Use `--dry-run` instead of `--yes` to preview the same template and plan without
194
194
  provider discovery, file writes, or execution; add `--json` for one versioned
195
195
  machine-readable object.
196
196
 
197
+ For a local pipeline, pass that object to `--from-preview - --yes`. Loki accepts
198
+ at most 1 MiB from non-terminal stdin, revalidates the schema, and recomputes the
199
+ current estimate before the explicitly consented build starts:
200
+
201
+ ```bash
202
+ loki quickstart "an internal reporting workspace" --template dashboard --dry-run --json \
203
+ | loki quickstart --from-preview - --yes
204
+ ```
205
+
206
+ Omit `--yes`, pipe malformed or oversized JSON, or use `-` from a terminal and
207
+ the continuation exits `2` before provider discovery, PRD writes, or execution.
208
+
197
209
  Drop a spec -- any artifact that describes what you want built -- and Loki
198
210
  Mode takes it from spec to deployed app. Specs can be a markdown PRD, a
199
211
  GitHub issue URL, or a YAML feature description.