loki-mode 9.22.8 → 9.22.10
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 +18 -0
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/issue-parser.sh +30 -1
- package/autonomy/lib/gate_policy.py +66 -0
- package/autonomy/lib/proof-generator.py +13 -2
- package/autonomy/lib/proof-pr.sh +6 -1
- package/autonomy/loki +107 -3
- package/autonomy/quickstart.sh +110 -1
- package/autonomy/run.sh +111 -0
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +55 -0
- package/dashboard/static/index.html +91 -27
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +434 -423
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
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
|
# =============================================================================
|