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 +5 -0
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/gate_policy.py +66 -0
- package/autonomy/quickstart.sh +149 -24
- package/autonomy/run.sh +111 -0
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +55 -0
- package/docs/INSTALLATION.md +13 -1
- package/loki-ts/dist/loki.js +372 -361
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
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.
|
|
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.
|
|
473
|
+
**v9.22.9 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.22.
|
|
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
|
+
}
|
package/autonomy/quickstart.sh
CHANGED
|
@@ -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 [
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
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
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
|
628
|
-
printf ' --from-preview F Continue
|
|
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)
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -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
|
# =============================================================================
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -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.
|
|
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.
|