loki-mode 7.55.0 → 7.57.0
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 +1 -1
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/app-runner.sh +101 -0
- package/autonomy/lib/prd-enrich.sh +437 -0
- package/autonomy/loki +244 -8
- package/autonomy/run.sh +175 -60
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +382 -2
- package/dashboard/static/index.html +164 -151
- package/docs/INSTALLATION.md +2 -2
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/skills/quality-gates.md +135 -11
package/autonomy/loki
CHANGED
|
@@ -726,8 +726,8 @@ show_help() {
|
|
|
726
726
|
echo " help Show this help ('loki help aliases' for old names)"
|
|
727
727
|
echo ""
|
|
728
728
|
echo "More commands (grill, spec, cleanup, init, watch, demo, web, api,"
|
|
729
|
-
echo "logs, github, import, council, proof, audit, agent, template,
|
|
730
|
-
echo "docs, wiki, ci, test, bench, secrets, telemetry, crash, worktree,"
|
|
729
|
+
echo "logs, github, import, council, proof, audit, compliance, agent, template,"
|
|
730
|
+
echo "magic, docs, wiki, ci, test, bench, secrets, telemetry, crash, worktree,"
|
|
731
731
|
echo "failover, monitor, remote, ...) are dispatchable and documented via"
|
|
732
732
|
echo "'loki <command> --help'."
|
|
733
733
|
echo ""
|
|
@@ -14530,6 +14530,9 @@ main() {
|
|
|
14530
14530
|
audit)
|
|
14531
14531
|
cmd_audit "$@"
|
|
14532
14532
|
;;
|
|
14533
|
+
compliance)
|
|
14534
|
+
cmd_compliance "$@"
|
|
14535
|
+
;;
|
|
14533
14536
|
review)
|
|
14534
14537
|
cmd_review "$@"
|
|
14535
14538
|
;;
|
|
@@ -15853,6 +15856,211 @@ STATE_QUERY_PY
|
|
|
15853
15856
|
}
|
|
15854
15857
|
|
|
15855
15858
|
# Agent action audit log and quality scanning
|
|
15859
|
+
# Resolve the repo root (where src/audit lives) from the loki script dir.
|
|
15860
|
+
# _LOKI_SCRIPT_DIR is autonomy/; the JS audit modules live at ../src/audit.
|
|
15861
|
+
_loki_audit_repo_root() {
|
|
15862
|
+
( cd "$_LOKI_SCRIPT_DIR/.." && pwd )
|
|
15863
|
+
}
|
|
15864
|
+
|
|
15865
|
+
# Link the run manifest (.loki/loki-run.json) into the agent audit chain for
|
|
15866
|
+
# the TARGET project (not the loki repo). Honest no-op when the manifest is
|
|
15867
|
+
# absent. Exit 0 on link or honest no-op; exit 1 only on a real error.
|
|
15868
|
+
cmd_audit_link_manifest() {
|
|
15869
|
+
local target_dir="${1:-$PWD}"
|
|
15870
|
+
if ! command -v node &>/dev/null; then
|
|
15871
|
+
echo -e "${RED}Error: node is required for 'loki audit link-manifest'${NC}"
|
|
15872
|
+
echo "Install Node.js, then re-run."
|
|
15873
|
+
return 1
|
|
15874
|
+
fi
|
|
15875
|
+
local repo_root
|
|
15876
|
+
repo_root="$(_loki_audit_repo_root)"
|
|
15877
|
+
local abs_target
|
|
15878
|
+
abs_target="$( cd "$target_dir" 2>/dev/null && pwd )" || {
|
|
15879
|
+
echo -e "${RED}Error: project dir not found: $target_dir${NC}"
|
|
15880
|
+
return 1
|
|
15881
|
+
}
|
|
15882
|
+
|
|
15883
|
+
echo -e "${BOLD}Linking run manifest into the audit chain...${NC}"
|
|
15884
|
+
echo " Project: $abs_target"
|
|
15885
|
+
|
|
15886
|
+
LOKI_AUDIT_MOD="$repo_root/src/audit" LOKI_AUDIT_TARGET="$abs_target" \
|
|
15887
|
+
node -e '
|
|
15888
|
+
const audit = require(process.env.LOKI_AUDIT_MOD);
|
|
15889
|
+
const res = audit.linkManifest({ projectDir: process.env.LOKI_AUDIT_TARGET });
|
|
15890
|
+
if (res.linked) {
|
|
15891
|
+
console.log(" linked: yes");
|
|
15892
|
+
console.log(" manifest: " + res.manifestPath);
|
|
15893
|
+
console.log(" sha256: " + res.manifestSha256);
|
|
15894
|
+
if (res.manifestSchema) console.log(" schema: " + res.manifestSchema);
|
|
15895
|
+
if (res.anchor && typeof res.anchor.seq !== "undefined") console.log(" anchor seq: " + res.anchor.seq);
|
|
15896
|
+
} else {
|
|
15897
|
+
console.log(" linked: no (" + (res.reason || "no-op") + ")");
|
|
15898
|
+
console.log(" manifest: " + res.manifestPath);
|
|
15899
|
+
}
|
|
15900
|
+
' || {
|
|
15901
|
+
echo -e "${RED}Error: manifest link failed${NC}"
|
|
15902
|
+
return 1
|
|
15903
|
+
}
|
|
15904
|
+
return 0
|
|
15905
|
+
}
|
|
15906
|
+
|
|
15907
|
+
# Verify the run-manifest link for the TARGET project. Honest empty case
|
|
15908
|
+
# (no anchor recorded yet) exits 0; a real tamper/integrity failure exits 1.
|
|
15909
|
+
cmd_audit_verify_manifest() {
|
|
15910
|
+
local target_dir="${1:-$PWD}"
|
|
15911
|
+
if ! command -v node &>/dev/null; then
|
|
15912
|
+
echo -e "${RED}Error: node is required for 'loki audit verify-manifest'${NC}"
|
|
15913
|
+
echo "Install Node.js, then re-run."
|
|
15914
|
+
return 1
|
|
15915
|
+
fi
|
|
15916
|
+
local repo_root
|
|
15917
|
+
repo_root="$(_loki_audit_repo_root)"
|
|
15918
|
+
local abs_target
|
|
15919
|
+
abs_target="$( cd "$target_dir" 2>/dev/null && pwd )" || {
|
|
15920
|
+
echo -e "${RED}Error: project dir not found: $target_dir${NC}"
|
|
15921
|
+
return 1
|
|
15922
|
+
}
|
|
15923
|
+
|
|
15924
|
+
echo -e "${BOLD}Verifying run-manifest link...${NC}"
|
|
15925
|
+
echo " Project: $abs_target"
|
|
15926
|
+
|
|
15927
|
+
LOKI_AUDIT_MOD="$repo_root/src/audit" LOKI_AUDIT_TARGET="$abs_target" \
|
|
15928
|
+
node -e '
|
|
15929
|
+
const audit = require(process.env.LOKI_AUDIT_MOD);
|
|
15930
|
+
const res = audit.verifyManifestLink({ projectDir: process.env.LOKI_AUDIT_TARGET });
|
|
15931
|
+
const chainValid = res.chain && res.chain.valid;
|
|
15932
|
+
console.log(" chain integrity: " + (chainValid ? "valid" : "INVALID"));
|
|
15933
|
+
if (!res.present) {
|
|
15934
|
+
console.log(" manifest link: none recorded yet (no-op)");
|
|
15935
|
+
// Honest empty case: success only if the chain itself is valid.
|
|
15936
|
+
process.exit(chainValid ? 0 : 1);
|
|
15937
|
+
}
|
|
15938
|
+
const m = res.manifest || {};
|
|
15939
|
+
console.log(" manifest: " + (m.manifestPath || "?"));
|
|
15940
|
+
console.log(" pinned sha256: " + (m.pinnedSha256 || "?"));
|
|
15941
|
+
console.log(" current sha256: " + (m.currentSha256 || "(absent)"));
|
|
15942
|
+
if (res.valid) {
|
|
15943
|
+
console.log(" verdict: VALID (manifest matches anchored hash)");
|
|
15944
|
+
process.exit(0);
|
|
15945
|
+
} else {
|
|
15946
|
+
console.log(" verdict: TAMPERED / INVALID");
|
|
15947
|
+
if (m.error) console.log(" reason: " + m.error);
|
|
15948
|
+
process.exit(1);
|
|
15949
|
+
}
|
|
15950
|
+
'
|
|
15951
|
+
return $?
|
|
15952
|
+
}
|
|
15953
|
+
|
|
15954
|
+
# loki compliance - periodic compliance snapshot (SOC2 / ISO27001 / GDPR).
|
|
15955
|
+
# The scheduler is default-disabled (LOKI_COMPLIANCE_SNAPSHOT_INTERVAL_HOURS
|
|
15956
|
+
# unset or 0). `loki compliance snapshot` runs the self-rate-limiting check;
|
|
15957
|
+
# `--force` generates a snapshot now regardless of interval/elapsed gate.
|
|
15958
|
+
cmd_compliance() {
|
|
15959
|
+
local subcommand="${1:-help}"
|
|
15960
|
+
|
|
15961
|
+
case "$subcommand" in
|
|
15962
|
+
snapshot)
|
|
15963
|
+
shift
|
|
15964
|
+
local force="" target_dir="$PWD"
|
|
15965
|
+
while [[ $# -gt 0 ]]; do
|
|
15966
|
+
case "$1" in
|
|
15967
|
+
--force|-f) force="1"; shift ;;
|
|
15968
|
+
--help|-h)
|
|
15969
|
+
echo -e "${BOLD}loki compliance snapshot${NC} - Generate/check a compliance snapshot"
|
|
15970
|
+
echo ""
|
|
15971
|
+
echo "Usage: loki compliance snapshot [PROJECT_DIR] [--force]"
|
|
15972
|
+
echo ""
|
|
15973
|
+
echo "Without --force, respects the default-disabled interval"
|
|
15974
|
+
echo "(LOKI_COMPLIANCE_SNAPSHOT_INTERVAL_HOURS). Reports honestly:"
|
|
15975
|
+
echo " disabled interval unset/0 (default)"
|
|
15976
|
+
echo " not-elapsed interval not yet elapsed since last snapshot"
|
|
15977
|
+
echo " generated a new snapshot was written"
|
|
15978
|
+
echo ""
|
|
15979
|
+
echo "With --force, a snapshot is generated now regardless."
|
|
15980
|
+
echo "Snapshots are written to .loki/audit/compliance-snapshots/ in the project dir."
|
|
15981
|
+
return 0
|
|
15982
|
+
;;
|
|
15983
|
+
-*) echo -e "${RED}Unknown option: $1${NC}"; return 1 ;;
|
|
15984
|
+
*) target_dir="$1"; shift ;;
|
|
15985
|
+
esac
|
|
15986
|
+
done
|
|
15987
|
+
|
|
15988
|
+
if ! command -v node &>/dev/null; then
|
|
15989
|
+
echo -e "${RED}Error: node is required for 'loki compliance snapshot'${NC}"
|
|
15990
|
+
echo "Install Node.js, then re-run."
|
|
15991
|
+
return 1
|
|
15992
|
+
fi
|
|
15993
|
+
local repo_root
|
|
15994
|
+
repo_root="$(_loki_audit_repo_root)"
|
|
15995
|
+
local abs_target
|
|
15996
|
+
abs_target="$( cd "$target_dir" 2>/dev/null && pwd )" || {
|
|
15997
|
+
echo -e "${RED}Error: project dir not found: $target_dir${NC}"
|
|
15998
|
+
return 1
|
|
15999
|
+
}
|
|
16000
|
+
|
|
16001
|
+
echo -e "${BOLD}Compliance snapshot${NC}"
|
|
16002
|
+
echo " Project: $abs_target"
|
|
16003
|
+
|
|
16004
|
+
LOKI_SCHED_MOD="$repo_root/src/audit/compliance-scheduler.js" \
|
|
16005
|
+
LOKI_SCHED_TARGET="$abs_target" LOKI_SCHED_FORCE="$force" \
|
|
16006
|
+
node -e '
|
|
16007
|
+
const sched = require(process.env.LOKI_SCHED_MOD);
|
|
16008
|
+
const target = process.env.LOKI_SCHED_TARGET;
|
|
16009
|
+
const force = process.env.LOKI_SCHED_FORCE === "1";
|
|
16010
|
+
if (force) {
|
|
16011
|
+
const now = Date.now();
|
|
16012
|
+
const snapshot = sched.buildSnapshot({ projectDir: target, nowMs: now });
|
|
16013
|
+
const file = sched.persistSnapshot({ projectDir: target, snapshot: snapshot, nowMs: now });
|
|
16014
|
+
console.log(" result: generated (forced)");
|
|
16015
|
+
console.log(" path: " + file);
|
|
16016
|
+
console.log(" audit entries: " + snapshot.totalAuditEntries);
|
|
16017
|
+
process.exit(0);
|
|
16018
|
+
}
|
|
16019
|
+
const res = sched.maybeGenerateSnapshot({ projectDir: target });
|
|
16020
|
+
if (res.generated) {
|
|
16021
|
+
console.log(" result: generated");
|
|
16022
|
+
console.log(" path: " + res.path);
|
|
16023
|
+
console.log(" interval (hours): " + res.intervalHours);
|
|
16024
|
+
} else if (res.reason === "disabled") {
|
|
16025
|
+
console.log(" result: disabled (scheduler off; default)");
|
|
16026
|
+
console.log(" enable: set LOKI_COMPLIANCE_SNAPSHOT_INTERVAL_HOURS=<N>, or use --force to generate now");
|
|
16027
|
+
} else if (res.reason === "not-elapsed") {
|
|
16028
|
+
console.log(" result: not-elapsed (interval not yet reached)");
|
|
16029
|
+
console.log(" interval (hours): " + res.intervalHours);
|
|
16030
|
+
if (res.nextEligibleAtMs) console.log(" next eligible: " + new Date(res.nextEligibleAtMs).toISOString());
|
|
16031
|
+
console.log(" override: use --force to generate now");
|
|
16032
|
+
} else {
|
|
16033
|
+
console.log(" result: " + (res.reason || "no-op"));
|
|
16034
|
+
}
|
|
16035
|
+
process.exit(0);
|
|
16036
|
+
' || {
|
|
16037
|
+
echo -e "${RED}Error: compliance snapshot failed${NC}"
|
|
16038
|
+
return 1
|
|
16039
|
+
}
|
|
16040
|
+
return 0
|
|
16041
|
+
;;
|
|
16042
|
+
--help|-h|help)
|
|
16043
|
+
echo -e "${BOLD}loki compliance${NC} - Periodic compliance snapshots"
|
|
16044
|
+
echo ""
|
|
16045
|
+
echo "Usage: loki compliance <subcommand> [options]"
|
|
16046
|
+
echo ""
|
|
16047
|
+
echo "Subcommands:"
|
|
16048
|
+
echo " snapshot [DIR] [--force] Generate/check a compliance snapshot"
|
|
16049
|
+
echo " help Show this help"
|
|
16050
|
+
echo ""
|
|
16051
|
+
echo "The compliance scheduler is default-disabled. A snapshot bundles the"
|
|
16052
|
+
echo "SOC2, ISO27001 and GDPR reports plus the audit chain-integrity verdict"
|
|
16053
|
+
echo "into .loki/audit/compliance-snapshots/. Enable periodic generation by setting"
|
|
16054
|
+
echo "LOKI_COMPLIANCE_SNAPSHOT_INTERVAL_HOURS, or run with --force on demand."
|
|
16055
|
+
;;
|
|
16056
|
+
*)
|
|
16057
|
+
echo -e "${RED}Unknown compliance subcommand: $subcommand${NC}"
|
|
16058
|
+
echo "Run 'loki compliance help' for usage."
|
|
16059
|
+
exit 1
|
|
16060
|
+
;;
|
|
16061
|
+
esac
|
|
16062
|
+
}
|
|
16063
|
+
|
|
15856
16064
|
cmd_audit() {
|
|
15857
16065
|
local subcommand="${1:-help}"
|
|
15858
16066
|
local audit_file="$LOKI_DIR/logs/agent-audit.jsonl"
|
|
@@ -16148,23 +16356,51 @@ print()
|
|
|
16148
16356
|
fi
|
|
16149
16357
|
;;
|
|
16150
16358
|
|
|
16359
|
+
link-manifest)
|
|
16360
|
+
# Fold the run manifest (.loki/loki-run.json bill-of-materials) into
|
|
16361
|
+
# the agent audit chain so it becomes tamper-evident and verifiable
|
|
16362
|
+
# against the evidence chain. Explicit, on-demand command: cmd_start
|
|
16363
|
+
# execs run.sh and replaces the loki process, so there is no
|
|
16364
|
+
# post-completion return point in this CLI wrapper to auto-invoke
|
|
16365
|
+
# from. Honest no-op (exit 0) when the manifest is absent.
|
|
16366
|
+
shift
|
|
16367
|
+
local target_dir="${1:-$PWD}"
|
|
16368
|
+
cmd_audit_link_manifest "$target_dir"
|
|
16369
|
+
;;
|
|
16370
|
+
verify-manifest)
|
|
16371
|
+
# Verify the run-manifest link: agent chain integrity AND the on-disk
|
|
16372
|
+
# manifest still matching the hash pinned by the most recent
|
|
16373
|
+
# manifest-link anchor. Honest empty case (exit 0) when no anchor was
|
|
16374
|
+
# recorded yet; nonzero only on a real tamper/integrity failure.
|
|
16375
|
+
shift
|
|
16376
|
+
local target_dir="${1:-$PWD}"
|
|
16377
|
+
cmd_audit_verify_manifest "$target_dir"
|
|
16378
|
+
;;
|
|
16151
16379
|
--help|-h|help)
|
|
16152
16380
|
echo -e "${BOLD}loki audit${NC} - Agent audit log and quality scanning"
|
|
16153
16381
|
echo ""
|
|
16154
16382
|
echo "Usage: loki audit <subcommand> [options]"
|
|
16155
16383
|
echo ""
|
|
16156
16384
|
echo "Subcommands:"
|
|
16157
|
-
echo " log [N]
|
|
16158
|
-
echo " count
|
|
16159
|
-
echo " scan
|
|
16160
|
-
echo " lint
|
|
16161
|
-
echo " test
|
|
16162
|
-
echo "
|
|
16385
|
+
echo " log [N] Show last N audit log entries (default: 50)"
|
|
16386
|
+
echo " count Count actions by type"
|
|
16387
|
+
echo " scan Run quality scan against dashboard API"
|
|
16388
|
+
echo " lint Run static analysis on project files"
|
|
16389
|
+
echo " test Run test coverage check"
|
|
16390
|
+
echo " link-manifest [P] Link the run manifest into the audit chain (tamper-evidence)"
|
|
16391
|
+
echo " verify-manifest [P] Verify the run-manifest link against the evidence chain"
|
|
16392
|
+
echo " help Show this help"
|
|
16163
16393
|
echo ""
|
|
16164
16394
|
echo "Quality Scan Options (loki audit scan):"
|
|
16165
16395
|
echo " --preset NAME Compliance preset (default|healthcare|fintech|government)"
|
|
16166
16396
|
echo " --export Save report to .loki/quality/report-{date}.json"
|
|
16167
16397
|
echo ""
|
|
16398
|
+
echo "Manifest tamper-evidence (loki audit link-manifest / verify-manifest):"
|
|
16399
|
+
echo " Hashes .loki/loki-run.json (the build bill-of-materials) into the"
|
|
16400
|
+
echo " agent audit chain. link-manifest no-ops honestly if the manifest is"
|
|
16401
|
+
echo " absent; verify-manifest reports honestly if no anchor exists yet."
|
|
16402
|
+
echo " Optional [P] is the project dir (default: current directory)."
|
|
16403
|
+
echo ""
|
|
16168
16404
|
echo "The agent audit log records actions taken during Loki sessions,"
|
|
16169
16405
|
echo "including CLI invocations, git commits, and session lifecycle events."
|
|
16170
16406
|
echo "Log file: $audit_file"
|
package/autonomy/run.sh
CHANGED
|
@@ -8320,23 +8320,31 @@ enforce_mutation_integrity() {
|
|
|
8320
8320
|
}
|
|
8321
8321
|
|
|
8322
8322
|
# ============================================================================
|
|
8323
|
-
# Semantic Test-Authenticity Gate (P1-3): wire tests/detect-semantic-test-problems.sh
|
|
8324
|
-
#
|
|
8325
|
-
#
|
|
8326
|
-
#
|
|
8327
|
-
#
|
|
8323
|
+
# Semantic Test-Authenticity Gate (P1-3): wire tests/detect-semantic-test-problems.sh.
|
|
8324
|
+
# The detector catches the harder class of fake tests that the regex detectors
|
|
8325
|
+
# (gates 5+6) miss: assertions that look real but verify nothing because the
|
|
8326
|
+
# asserted value never flows through code under test (literal-via-variable echo
|
|
8327
|
+
# HIGH, mock-return echo MED, deleted assertions MED).
|
|
8328
8328
|
#
|
|
8329
|
-
#
|
|
8330
|
-
#
|
|
8331
|
-
#
|
|
8332
|
-
#
|
|
8333
|
-
#
|
|
8334
|
-
#
|
|
8335
|
-
#
|
|
8336
|
-
#
|
|
8337
|
-
#
|
|
8338
|
-
#
|
|
8339
|
-
#
|
|
8329
|
+
# POSTURE (v7.57.0): this enforce_* helper is the shared core for TWO callers:
|
|
8330
|
+
# 1) the DEFAULT-ON mid-iteration ADVISORY arm (gated on LOKI_GATE_SEMANTIC_TESTS,
|
|
8331
|
+
# default true) -- runs every iteration, writes findings, and on a CRIT/HIGH
|
|
8332
|
+
# result the arm only calls track_gate_failure (surfaces to the next prompt),
|
|
8333
|
+
# NEVER PAUSEs / NEVER rejects completion (clone of the mock arm).
|
|
8334
|
+
# 2) the OPT-IN completion-BLOCKING elif (gated on LOKI_GATE_SEMANTIC_TESTS_BLOCK,
|
|
8335
|
+
# default false) -- when set, rejects the completion claim on a CRIT/HIGH.
|
|
8336
|
+
# The default-on flip applies ONLY to surfacing; blocking stays opt-in via the
|
|
8337
|
+
# separate _BLOCK flag, so surfacing-default-on does NOT make blocking default-on.
|
|
8338
|
+
#
|
|
8339
|
+
# NO-DEADLOCK CONTRACT: it runs the detector with --block-high (clean exit-code
|
|
8340
|
+
# contract: rc 2 iff a CRITICAL/HIGH finding exists). It surfaces ALL severities
|
|
8341
|
+
# to a findings file (advisory) and returns nonzero ONLY on rc 2. Every other
|
|
8342
|
+
# exit -- rc 0 (clean), rc 124 (timeout), detector absent, no test files,
|
|
8343
|
+
# malformed output -- returns 0 (pass/fall-through), so the autonomous loop can
|
|
8344
|
+
# NEVER deadlock on a clean run (default-on surfacing is therefore deadlock-safe,
|
|
8345
|
+
# exactly as the mock/mutation gates prove in production). Mirrors
|
|
8346
|
+
# enforce_mock_integrity's invocation (cd TARGET_DIR + LOKI_SCAN_DIR=TARGET_DIR +
|
|
8347
|
+
# timeout), swapping --strict for --block-high and deciding on the rc-2 contract.
|
|
8340
8348
|
# ============================================================================
|
|
8341
8349
|
enforce_semantic_integrity() {
|
|
8342
8350
|
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
@@ -8416,9 +8424,19 @@ _semantic_gate_and_surface() {
|
|
|
8416
8424
|
# redirect the scan).
|
|
8417
8425
|
# The PLURAL token LOKI_GATE_INVARIANTS is used deliberately to match the Bun
|
|
8418
8426
|
# readToggles flag name; the detector's own reference comment suggests a
|
|
8419
|
-
# singular
|
|
8420
|
-
#
|
|
8421
|
-
#
|
|
8427
|
+
# singular variant (no trailing S), which is NOT used here (parity needs the
|
|
8428
|
+
# plural).
|
|
8429
|
+
#
|
|
8430
|
+
# POSTURE (v7.57.0): this enforce_* helper is the shared core for TWO callers,
|
|
8431
|
+
# mirroring enforce_semantic_integrity:
|
|
8432
|
+
# 1) the DEFAULT-ON mid-iteration ADVISORY arm (gated on LOKI_GATE_INVARIANTS,
|
|
8433
|
+
# default true) -- runs every iteration, writes invariant-findings.txt, and
|
|
8434
|
+
# on a CRIT/HIGH result the arm only calls track_gate_failure (surfaces to
|
|
8435
|
+
# the next prompt via the invariant injector in build_prompt), NEVER PAUSEs /
|
|
8436
|
+
# NEVER rejects completion.
|
|
8437
|
+
# 2) the OPT-IN completion-BLOCKING elif (gated on LOKI_GATE_INVARIANTS_BLOCK,
|
|
8438
|
+
# default false) -- when set, rejects the completion claim on a CRIT/HIGH.
|
|
8439
|
+
# Surfacing-default-on does NOT make blocking default-on (separate _BLOCK flag).
|
|
8422
8440
|
enforce_invariant_integrity() {
|
|
8423
8441
|
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
8424
8442
|
local quality_dir="$loki_dir/quality"
|
|
@@ -12435,6 +12453,21 @@ if d.get('blocked'):
|
|
|
12435
12453
|
fi
|
|
12436
12454
|
fi
|
|
12437
12455
|
|
|
12456
|
+
# P1-4 / v7.57.0: surface specific invariant/property findings (which file,
|
|
12457
|
+
# which violated invariant) when the default-on advisory gate (or the opt-in
|
|
12458
|
+
# LOKI_GATE_INVARIANTS_BLOCK gate) wrote them, so a block converges and an
|
|
12459
|
+
# advisory run still informs the next iteration. The file exists only when
|
|
12460
|
+
# the gate ran AND found something (cleared on clean), so this is zero-cost on
|
|
12461
|
+
# a clean run and when the surfacing gate is opted out. Mirrors the semantic
|
|
12462
|
+
# injector above and is independent of gate-failures.txt presence.
|
|
12463
|
+
if [ -f "${TARGET_DIR:-.}/.loki/quality/invariant-findings.txt" ]; then
|
|
12464
|
+
local inv_findings
|
|
12465
|
+
inv_findings=$(grep -E '\[(CRITICAL|HIGH|MEDIUM|LOW)\]' "${TARGET_DIR:-.}/.loki/quality/invariant-findings.txt" 2>/dev/null | head -20 || true)
|
|
12466
|
+
if [ -n "$inv_findings" ]; then
|
|
12467
|
+
gate_failure_context="${gate_failure_context} INVARIANT/PROPERTY FINDINGS (fix the violated invariants; the code must preserve the stated property/metamorphic relation, not just pass example-based tests): ${inv_findings}"
|
|
12468
|
+
fi
|
|
12469
|
+
fi
|
|
12470
|
+
|
|
12438
12471
|
# P2-2: high-severity spec-assumption context. When DISCOVERY recorded any
|
|
12439
12472
|
# high-severity assumption (the spec was ambiguous in a high-impact place),
|
|
12440
12473
|
# surface it to the build agent so it implements with the gap in view (or
|
|
@@ -13461,9 +13494,12 @@ if not features:
|
|
|
13461
13494
|
continue
|
|
13462
13495
|
clean_name = strip_numbering(section_name)
|
|
13463
13496
|
if len(section_content) > 20 and len(clean_name) > 5:
|
|
13497
|
+
# BUG-A fix: store the REAL section/heading key (not a hardcoded
|
|
13498
|
+
# "Requirements") so sections.get(feat["section"]) later resolves
|
|
13499
|
+
# to this section's body and the description is not just the title.
|
|
13464
13500
|
features.append({
|
|
13465
13501
|
"title": clean_name,
|
|
13466
|
-
"section":
|
|
13502
|
+
"section": section_name,
|
|
13467
13503
|
})
|
|
13468
13504
|
|
|
13469
13505
|
if not features:
|
|
@@ -13601,6 +13637,20 @@ PRD_PARSE_EOF
|
|
|
13601
13637
|
return 0
|
|
13602
13638
|
fi
|
|
13603
13639
|
|
|
13640
|
+
# LLM enrichment post-pass (v7.52.0). The python heredoc above has no model
|
|
13641
|
+
# access, so stub tasks (description == title, or the templated user_story)
|
|
13642
|
+
# are enriched here via one batched provider call. Best-effort: on any
|
|
13643
|
+
# failure (non-claude provider, degraded mode, no binary, timeout, parse
|
|
13644
|
+
# error) the deterministic output is left intact. Never blocks the queue.
|
|
13645
|
+
local _prd_enrich_lib="$SCRIPT_DIR/lib/prd-enrich.sh"
|
|
13646
|
+
if [ -f "$_prd_enrich_lib" ]; then
|
|
13647
|
+
# shellcheck source=lib/prd-enrich.sh
|
|
13648
|
+
source "$_prd_enrich_lib" 2>/dev/null || true
|
|
13649
|
+
if declare -f loki_prd_enrich >/dev/null 2>&1; then
|
|
13650
|
+
loki_prd_enrich ".loki/queue/pending.json" "$effective_prd" || true
|
|
13651
|
+
fi
|
|
13652
|
+
fi
|
|
13653
|
+
|
|
13604
13654
|
touch ".loki/queue/.prd-populated"
|
|
13605
13655
|
log_info "PRD task parsing complete"
|
|
13606
13656
|
}
|
|
@@ -15146,27 +15196,39 @@ if __name__ == "__main__":
|
|
|
15146
15196
|
log_warn "Mutation integrity gate FAILED ($mt_count consecutive) - HIGH test-fitting detected"
|
|
15147
15197
|
fi
|
|
15148
15198
|
fi
|
|
15149
|
-
# LSP diagnostics gate (P1-5 bash-route parity, v7.51.0
|
|
15150
|
-
# parity gap: the Bun
|
|
15151
|
-
# (loki-ts/src/runner/quality_gates.ts)
|
|
15152
|
-
# writer (mcp/lsp_proxy.py); the bash
|
|
15153
|
-
#
|
|
15154
|
-
#
|
|
15155
|
-
#
|
|
15156
|
-
#
|
|
15157
|
-
#
|
|
15158
|
-
#
|
|
15159
|
-
#
|
|
15199
|
+
# LSP diagnostics gate (P1-5 bash-route parity, v7.51.0; default-on
|
|
15200
|
+
# advisory-surfacing as of v7.57.0). Closes the parity gap: the Bun
|
|
15201
|
+
# route ships runLSPDiagnostics (loki-ts/src/runner/quality_gates.ts)
|
|
15202
|
+
# with a route-neutral Python writer (mcp/lsp_proxy.py); the bash
|
|
15203
|
+
# route had NO writer/reader.
|
|
15204
|
+
#
|
|
15205
|
+
# POSTURE (v7.57.0): DEFAULT-ON SURFACING (no blocking arm exists).
|
|
15206
|
+
# This is the mid-iteration advisory arm -- it RUNS by default (like
|
|
15207
|
+
# the mock/mutation gates), writes lsp-diagnostics.json, and surfaces
|
|
15208
|
+
# errors to the NEXT iteration via track_gate_failure (the
|
|
15209
|
+
# lsp_diagnostics token flows into gate-failures.txt -> build_prompt).
|
|
15210
|
+
# It does NOT reject completion: the "block*" case below only calls
|
|
15211
|
+
# track_gate_failure, never PAUSE / never the completion-promise arm,
|
|
15212
|
+
# exactly like the mock gate. Default-on surfacing CANNOT deadlock
|
|
15213
|
+
# (mock/mutation prove this in production); there is therefore NO
|
|
15214
|
+
# separate blocking knob for LSP (none ever existed on the bash route).
|
|
15215
|
+
# - Toggle: LOKI_GATE_LSP_DIAGNOSTICS (default TRUE = surfacing on;
|
|
15216
|
+
# opt out with =false). Accepts "true" or "1". Single knob (no
|
|
15217
|
+
# blocking arm exists on the bash route, so there is no _BLOCK flag).
|
|
15218
|
+
# - count_errors > 0 -> surface (track_gate_failure), mirroring the
|
|
15219
|
+
# TS "errorCount > 0" finding at quality_gates.ts:1667 but WITHOUT
|
|
15220
|
+
# rejecting completion (advisory-first on the bash route).
|
|
15160
15221
|
# - warnings only -> advisory PASS (quality_gates.ts:1673).
|
|
15161
|
-
# - artifact absent/malformed -> honest pass-through, NEVER
|
|
15162
|
-
#
|
|
15222
|
+
# - artifact absent/malformed/timeout -> honest pass-through, NEVER
|
|
15223
|
+
# surface a block, NEVER deadlock (deny-filter; mirrors
|
|
15224
|
+
# quality_gates.ts:1646 returning passed:true on null artifact).
|
|
15163
15225
|
# The writer is OPT-OUT-able with LOKI_GATE_LSP_WRITER=0 (operator can
|
|
15164
15226
|
# supply a pre-built artifact), matching the TS escape hatch
|
|
15165
15227
|
# (quality_gates.ts:1630). cwd must be the install dir (PROJECT_DIR =
|
|
15166
15228
|
# $SCRIPT_DIR/.. ) so `-m mcp.lsp_proxy` imports, while --root points
|
|
15167
15229
|
# at the TARGET project the loop is building (mirrors
|
|
15168
15230
|
# runLSPDiagnosticsWriter: cwd=REPO_ROOT, --root=ctx.cwd).
|
|
15169
|
-
if [ "${LOKI_GATE_LSP_DIAGNOSTICS:-
|
|
15231
|
+
if { [ "${LOKI_GATE_LSP_DIAGNOSTICS:-true}" = "true" ] || [ "${LOKI_GATE_LSP_DIAGNOSTICS:-true}" = "1" ]; } && [ "$ITERATION_COUNT" -gt 0 ]; then
|
|
15170
15232
|
log_info "Quality gate: LSP diagnostics..."
|
|
15171
15233
|
# WRITER: route-neutral Python, same program as the Bun route.
|
|
15172
15234
|
if [ "${LOKI_GATE_LSP_WRITER:-1}" != "0" ]; then
|
|
@@ -15228,6 +15290,54 @@ else:
|
|
|
15228
15290
|
;;
|
|
15229
15291
|
esac
|
|
15230
15292
|
fi
|
|
15293
|
+
# Semantic test-authenticity gate -- mid-iteration ADVISORY arm
|
|
15294
|
+
# (v7.57.0 default-on surfacing). Clones the mock arm (~15126)
|
|
15295
|
+
# byte-for-byte: runs enforce_semantic_integrity, which writes
|
|
15296
|
+
# semantic-findings.txt and returns 1 ONLY on a CRITICAL/HIGH
|
|
15297
|
+
# fake-test finding (clean / no-test-files / detector-absent / timeout
|
|
15298
|
+
# / malformed all collapse to rc 0 inside the function -- deny-filter).
|
|
15299
|
+
# On rc 1 we ONLY track_gate_failure (surface to the next prompt via
|
|
15300
|
+
# the semantic-findings injector at build_prompt); we NEVER PAUSE and
|
|
15301
|
+
# NEVER reject completion here. Default-on surfacing cannot deadlock
|
|
15302
|
+
# (mock/mutation prove this in production). The opt-in completion-
|
|
15303
|
+
# BLOCKING arm lives behind LOKI_GATE_SEMANTIC_TESTS_BLOCK at the
|
|
15304
|
+
# completion-promise elif below; this surfacing arm is independent.
|
|
15305
|
+
# - Toggle: LOKI_GATE_SEMANTIC_TESTS (default TRUE = surfacing on;
|
|
15306
|
+
# opt out with =false). Accepts "true" or "1".
|
|
15307
|
+
if { [ "${LOKI_GATE_SEMANTIC_TESTS:-true}" = "true" ] || [ "${LOKI_GATE_SEMANTIC_TESTS:-true}" = "1" ]; } && [ "$ITERATION_COUNT" -gt 0 ]; then
|
|
15308
|
+
log_info "Quality gate: semantic test-authenticity (advisory)..."
|
|
15309
|
+
if enforce_semantic_integrity; then
|
|
15310
|
+
clear_gate_failure "semantic_tests"
|
|
15311
|
+
else
|
|
15312
|
+
local sem_count
|
|
15313
|
+
sem_count=$(track_gate_failure "semantic_tests")
|
|
15314
|
+
gate_failures="${gate_failures}semantic_tests,"
|
|
15315
|
+
log_warn "Semantic test-authenticity gate FAILED ($sem_count consecutive) - CRITICAL/HIGH fake-test problems (advisory; surfaced to next iteration)"
|
|
15316
|
+
fi
|
|
15317
|
+
fi
|
|
15318
|
+
# Invariant/property gate -- mid-iteration ADVISORY arm (v7.57.0
|
|
15319
|
+
# default-on surfacing). Mirrors the semantic arm above: runs
|
|
15320
|
+
# enforce_invariant_integrity, which writes invariant-findings.txt and
|
|
15321
|
+
# returns 1 ONLY on a CRITICAL/HIGH invariant violation (clean /
|
|
15322
|
+
# detector-absent / timeout / malformed all collapse to rc 0 inside
|
|
15323
|
+
# the function -- deny-filter). On rc 1 we ONLY track_gate_failure
|
|
15324
|
+
# (surfaced to the next prompt via the invariant-findings injector in
|
|
15325
|
+
# build_prompt); we NEVER PAUSE and NEVER reject completion here. The
|
|
15326
|
+
# opt-in completion-BLOCKING arm lives behind LOKI_GATE_INVARIANTS_BLOCK
|
|
15327
|
+
# at the completion-promise elif below.
|
|
15328
|
+
# - Toggle: LOKI_GATE_INVARIANTS (default TRUE = surfacing on; opt
|
|
15329
|
+
# out with =false). Accepts "true" or "1".
|
|
15330
|
+
if { [ "${LOKI_GATE_INVARIANTS:-true}" = "true" ] || [ "${LOKI_GATE_INVARIANTS:-true}" = "1" ]; } && [ "$ITERATION_COUNT" -gt 0 ]; then
|
|
15331
|
+
log_info "Quality gate: invariant/property (advisory)..."
|
|
15332
|
+
if enforce_invariant_integrity; then
|
|
15333
|
+
clear_gate_failure "invariants"
|
|
15334
|
+
else
|
|
15335
|
+
local inv_count
|
|
15336
|
+
inv_count=$(track_gate_failure "invariants")
|
|
15337
|
+
gate_failures="${gate_failures}invariants,"
|
|
15338
|
+
log_warn "Invariant gate FAILED ($inv_count consecutive) - CRITICAL/HIGH invariant/property violations (advisory; surfaced to next iteration)"
|
|
15339
|
+
fi
|
|
15340
|
+
fi
|
|
15231
15341
|
# Code review gate (upgraded from advisory, with escalation)
|
|
15232
15342
|
if [ "$PHASE_CODE_REVIEW" = "true" ] && [ "$ITERATION_COUNT" -gt 0 ]; then
|
|
15233
15343
|
log_info "Quality gate: code review..."
|
|
@@ -15534,35 +15644,40 @@ else:
|
|
|
15534
15644
|
log_warn "Completion claim rejected: assumption ledger gate found unresolved high-severity spec assumption(s)."
|
|
15535
15645
|
log_warn " Details under .loki/council/assumption-block.json ; opt out with LOKI_ASSUMPTION_GATE=0"
|
|
15536
15646
|
# Fall through; keep iterating until high-sev assumptions resolve.
|
|
15537
|
-
# P1-3: semantic test-authenticity gate (OPT-IN,
|
|
15538
|
-
# fake tests that look real but verify nothing
|
|
15539
|
-
# echo etc.) that the regex gates 5+6 miss.
|
|
15540
|
-
#
|
|
15541
|
-
#
|
|
15542
|
-
#
|
|
15543
|
-
#
|
|
15544
|
-
#
|
|
15545
|
-
#
|
|
15546
|
-
#
|
|
15547
|
-
|
|
15647
|
+
# P1-3: semantic test-authenticity completion-BLOCKING gate (OPT-IN,
|
|
15648
|
+
# default OFF). Catches fake tests that look real but verify nothing
|
|
15649
|
+
# (literal-via-variable echo etc.) that the regex gates 5+6 miss.
|
|
15650
|
+
# v7.57.0: the SURFACING of these findings is now default-on (see the
|
|
15651
|
+
# mid-iteration advisory arm above, gated on LOKI_GATE_SEMANTIC_TESTS,
|
|
15652
|
+
# default true). This completion-BLOCKING arm is a SEPARATE opt-in,
|
|
15653
|
+
# gated on LOKI_GATE_SEMANTIC_TESTS_BLOCK (default OFF; accepts "true"
|
|
15654
|
+
# or "1"), so the surfacing-default-on flip does NOT make blocking
|
|
15655
|
+
# default-on. When the BLOCK flag is set it runs the detector with --block-high and
|
|
15656
|
+
# rejects completion ONLY on a CRITICAL/HIGH finding; clean /
|
|
15657
|
+
# no-test-files / detector-absent / timeout / malformed all collapse
|
|
15658
|
+
# to a pass inside _semantic_gate_and_surface, so the autonomous loop
|
|
15659
|
+
# can never deadlock on a clean run. Mirrors the evidence / held-out /
|
|
15660
|
+
# assumption arms above.
|
|
15661
|
+
elif [ "$_completion_claimed" = 1 ] && { [ "${LOKI_GATE_SEMANTIC_TESTS_BLOCK:-false}" = "true" ] || [ "${LOKI_GATE_SEMANTIC_TESTS_BLOCK:-false}" = "1" ]; } && type _semantic_gate_and_surface &>/dev/null && ! _semantic_gate_and_surface; then
|
|
15548
15662
|
log_warn "Completion claim rejected: semantic test-authenticity gate found CRITICAL/HIGH fake-test problem(s)."
|
|
15549
|
-
log_warn " Details under .loki/quality/semantic-findings.txt ; opt-in
|
|
15663
|
+
log_warn " Details under .loki/quality/semantic-findings.txt ; opt-in blocking -- disable with LOKI_GATE_SEMANTIC_TESTS_BLOCK=false"
|
|
15550
15664
|
# Fall through; keep iterating until the fake tests are fixed.
|
|
15551
|
-
# P1-4: invariant/property gate (OPT-IN, default
|
|
15552
|
-
# semantic arm above and the Bun route's invariants
|
|
15553
|
-
# (loki-ts/src/runner/quality_gates.ts:2057).
|
|
15554
|
-
#
|
|
15555
|
-
#
|
|
15556
|
-
#
|
|
15557
|
-
#
|
|
15558
|
-
#
|
|
15559
|
-
#
|
|
15560
|
-
#
|
|
15561
|
-
# collapse to a pass
|
|
15562
|
-
# autonomous loop can never
|
|
15563
|
-
|
|
15665
|
+
# P1-4: invariant/property completion-BLOCKING gate (OPT-IN, default
|
|
15666
|
+
# OFF). Mirrors the semantic arm above and the Bun route's invariants
|
|
15667
|
+
# toggle (loki-ts/src/runner/quality_gates.ts:2057). v7.57.0: the
|
|
15668
|
+
# SURFACING of these findings is now default-on (see the mid-iteration
|
|
15669
|
+
# advisory arm above, gated on LOKI_GATE_INVARIANTS, default true).
|
|
15670
|
+
# This completion-BLOCKING arm is a SEPARATE opt-in, gated on
|
|
15671
|
+
# LOKI_GATE_INVARIANTS_BLOCK (default OFF), so the surfacing-default-on
|
|
15672
|
+
# flip does NOT make blocking default-on. Accepts "true" or "1". When
|
|
15673
|
+
# the BLOCK flag is set it runs detect-invariant-violations.sh --strict
|
|
15674
|
+
# and rejects completion ONLY on a CRITICAL/HIGH (rc 1) finding;
|
|
15675
|
+
# clean / detector-absent / timeout / malformed all collapse to a pass
|
|
15676
|
+
# inside _invariant_gate_and_surface, so the autonomous loop can never
|
|
15677
|
+
# deadlock on a clean run.
|
|
15678
|
+
elif [ "$_completion_claimed" = 1 ] && { [ "${LOKI_GATE_INVARIANTS_BLOCK:-false}" = "true" ] || [ "${LOKI_GATE_INVARIANTS_BLOCK:-false}" = "1" ]; } && type _invariant_gate_and_surface &>/dev/null && ! _invariant_gate_and_surface; then
|
|
15564
15679
|
log_warn "Completion claim rejected: invariant gate found CRITICAL/HIGH invariant/property violation(s)."
|
|
15565
|
-
log_warn " Details under .loki/quality/invariant-findings.txt ; opt-in
|
|
15680
|
+
log_warn " Details under .loki/quality/invariant-findings.txt ; opt-in blocking -- disable with LOKI_GATE_INVARIANTS_BLOCK=false"
|
|
15566
15681
|
# Fall through; keep iterating until the invariant violations are fixed.
|
|
15567
15682
|
elif [ "$_completion_claimed" = 1 ]; then
|
|
15568
15683
|
echo ""
|