loki-mode 7.118.0 → 7.120.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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/proof-generator.py +14 -1
- package/autonomy/run.sh +214 -0
- package/autonomy/verify.sh +23 -1
- package/dashboard/__init__.py +1 -1
- package/docs/EVIDENCE-RECEIPT-BUILD-APPLICABILITY.md +79 -0
- package/docs/INSTALLATION.md +1 -1
- 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/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 v7.
|
|
6
|
+
# Loki Mode v7.120.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
408
408
|
|
|
409
409
|
---
|
|
410
410
|
|
|
411
|
-
**v7.
|
|
411
|
+
**v7.120.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.120.0
|
|
@@ -341,7 +341,20 @@ def _collect_build(loki_dir):
|
|
|
341
341
|
out["exit_code"] = _to_int(ec, None) if ec is not None else None
|
|
342
342
|
dur = raw.get("duration_sec")
|
|
343
343
|
out["duration_sec"] = _to_float(dur, None) if dur is not None else None
|
|
344
|
-
|
|
344
|
+
# HONEST APPLICABILITY (build has no writer historically -> facts.build was
|
|
345
|
+
# permanently "not_run" for every project, counting a CLI with no build step
|
|
346
|
+
# as a gap and dragging the headline to WITH GAPS). The writer now records a
|
|
347
|
+
# POSITIVE applicability signal: applicable:false means "this stack genuinely
|
|
348
|
+
# has no build phase" (a CLI, a plain script). That is N/A, not a skipped
|
|
349
|
+
# gap, so it must NOT be a degraded item. CRITICAL anti-fake-green rule:
|
|
350
|
+
# not_applicable ONLY from an EXPLICIT applicable:false in the file. An absent
|
|
351
|
+
# file or a missing/true applicable flag stays not_run (an honest gap) -- an
|
|
352
|
+
# un-built project must never flip green just because nobody wrote the file.
|
|
353
|
+
applicable = raw.get("applicable", True)
|
|
354
|
+
out["applicable"] = bool(applicable)
|
|
355
|
+
if applicable is False:
|
|
356
|
+
out["status"] = "not_applicable"
|
|
357
|
+
elif not ran:
|
|
345
358
|
out["status"] = "not_run"
|
|
346
359
|
elif out["exit_code"] == 0:
|
|
347
360
|
out["status"] = "verified"
|
package/autonomy/run.sh
CHANGED
|
@@ -8354,6 +8354,163 @@ COMPOUND_SCRIPT
|
|
|
8354
8354
|
# Results stored in .loki/quality/static-analysis.json
|
|
8355
8355
|
# ============================================================================
|
|
8356
8356
|
|
|
8357
|
+
# ---------------------------------------------------------------------------
|
|
8358
|
+
# enforce_build_check -- run the stack's build (if it has one) and record an
|
|
8359
|
+
# HONEST build fact for the Evidence Receipt (#47). Historically nothing wrote
|
|
8360
|
+
# .loki/quality/build-results.json, so facts.build was PERMANENTLY "not_run" for
|
|
8361
|
+
# every project -- a CLI with no build step read identically to a skipped real
|
|
8362
|
+
# build, and both dragged the receipt to "WITH GAPS". This is the missing writer.
|
|
8363
|
+
#
|
|
8364
|
+
# The distinction the receipt needs (and the founder demanded, no fabrication) is
|
|
8365
|
+
# THREE-WAY, not two (a council review caught the two-way version laundering
|
|
8366
|
+
# "unrecognized build system" into a fake N/A):
|
|
8367
|
+
# - a build command WE RUN exists (npm "build" / go / cargo) -> RUN it, record
|
|
8368
|
+
# ran/exit_code (verified on 0, failed otherwise -- a real gap kept honest).
|
|
8369
|
+
# - a build system EXISTS but we do not auto-run it (Make/Maven/Gradle/.NET, a
|
|
8370
|
+
# non-"build" npm script, a pyproject build-system) -> not_run (an HONEST GAP,
|
|
8371
|
+
# matching verify.sh:291's "skipped"). NEVER N/A -- a real, un-run build must
|
|
8372
|
+
# never read VERIFIED.
|
|
8373
|
+
# - genuinely NO build phase at all (a plain CLI/script) -> applicable:false
|
|
8374
|
+
# (status not_applicable). A POSITIVE "no build phase" determination, NOT an
|
|
8375
|
+
# absent file -- proof-generator maps applicable:false -> N/A (not a gap) but a
|
|
8376
|
+
# MISSING file or an unrecognized-real build stays not_run. One vocabulary, no
|
|
8377
|
+
# fake-green. Pinned by tests/test-build-check-applicability.sh.
|
|
8378
|
+
# ---------------------------------------------------------------------------
|
|
8379
|
+
enforce_build_check() {
|
|
8380
|
+
local tree="${TARGET_DIR:-.}"
|
|
8381
|
+
local loki_dir="$tree/.loki"
|
|
8382
|
+
local quality_dir="$loki_dir/quality"
|
|
8383
|
+
mkdir -p "$quality_dir"
|
|
8384
|
+
local out_file="$quality_dir/build-results.json"
|
|
8385
|
+
local timeout_s="${LOKI_GATE_TIMEOUT:-300}"
|
|
8386
|
+
local ts; ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
8387
|
+
|
|
8388
|
+
# THREE-WAY classification (detection is always cheap). The anti-fake-green line
|
|
8389
|
+
# (council-caught): N/A is ONLY for a POSITIVE "this stack has no build phase"
|
|
8390
|
+
# determination. An unrecognized-but-real build system (Make, Maven, Gradle,
|
|
8391
|
+
# .NET, a non-"build" npm build script) must stay an HONEST GAP (not_run), NOT
|
|
8392
|
+
# be laundered into N/A -- otherwise a real, un-run build reads VERIFIED. This
|
|
8393
|
+
# mirrors verify.sh:291, which records the same fall-through as a "skipped" gap.
|
|
8394
|
+
# runnable -> a build command WE run (npm build / go / cargo) -> verified/failed
|
|
8395
|
+
# present -> a build system EXISTS but we don't auto-run it -> not_run (GAP)
|
|
8396
|
+
# none -> positively no build phase at all -> not_applicable (N/A)
|
|
8397
|
+
local cmd="" ran="false" applicable="false" exit_code="null" status="not_run"
|
|
8398
|
+
local build_class="present"
|
|
8399
|
+
|
|
8400
|
+
# INVERTED classification (three council rounds each found a fake-green hole in
|
|
8401
|
+
# an allowlist of build systems -> the allowlist is structurally leaky). The
|
|
8402
|
+
# two error directions are NOT equal: a REAL build read as N/A -> VERIFIED is
|
|
8403
|
+
# DISHONEST over-claim (the cardinal fake-green sin); a no-build CLI read as a
|
|
8404
|
+
# not_run gap is an HONEST under-claim (merely the founder's original UX nit).
|
|
8405
|
+
# So not_run is the SAFE CATCH-ALL and N/A requires a POSITIVE proof-of-no-build.
|
|
8406
|
+
# A forgotten stack (Bazel, meson, autotools, Zig, Nix...) now falls to not_run
|
|
8407
|
+
# (honest), never to fake N/A -- fixing the whole CLASS, not each instance.
|
|
8408
|
+
# 1. runnable: a build command WE run -> verified/failed
|
|
8409
|
+
# 2. none: POSITIVE proof of no build phase -> not_applicable (N/A)
|
|
8410
|
+
# 3. else: not_run (honest gap) -- the default, covers every unknown stack
|
|
8411
|
+
|
|
8412
|
+
if [ -f "$tree/package.json" ] && grep -q '"build"[[:space:]]*:' "$tree/package.json" 2>/dev/null; then
|
|
8413
|
+
cmd="npm run build"; applicable="true"; build_class="runnable"
|
|
8414
|
+
elif [ -f "$tree/go.mod" ] && command -v go >/dev/null 2>&1; then
|
|
8415
|
+
cmd="go build ./..."; applicable="true"; build_class="runnable"
|
|
8416
|
+
elif [ -f "$tree/Cargo.toml" ] && command -v cargo >/dev/null 2>&1; then
|
|
8417
|
+
cmd="cargo build"; applicable="true"; build_class="runnable"
|
|
8418
|
+
else
|
|
8419
|
+
# POSITIVE proof-of-no-build (the ONLY path to N/A). Two narrow cases, and
|
|
8420
|
+
# anything short of a clear determination stays not_run:
|
|
8421
|
+
# (a) a package.json whose scripts exist but include NO build-ish script
|
|
8422
|
+
# AND there is no foreign build manifest -> a JS CLI/lib with no build.
|
|
8423
|
+
# (b) NO build manifest of ANY known kind at all -> a plain script dir.
|
|
8424
|
+
# The build-ish regex is used as an EXCLUSION so a build hidden under a
|
|
8425
|
+
# non-"build" script name (compile/dist/...) does NOT misfire N/A.
|
|
8426
|
+
local has_foreign_manifest="false"
|
|
8427
|
+
if [ -f "$tree/go.mod" ] || [ -f "$tree/Cargo.toml" ] \
|
|
8428
|
+
|| [ -f "$tree/Makefile" ] || [ -f "$tree/makefile" ] || [ -f "$tree/GNUmakefile" ] \
|
|
8429
|
+
|| [ -f "$tree/Makefile.in" ] || [ -f "$tree/configure" ] || [ -f "$tree/configure.ac" ] || [ -f "$tree/configure.in" ] \
|
|
8430
|
+
|| [ -f "$tree/CMakeLists.txt" ] || [ -f "$tree/meson.build" ] \
|
|
8431
|
+
|| [ -f "$tree/pom.xml" ] || [ -f "$tree/build.xml" ] \
|
|
8432
|
+
|| [ -f "$tree/build.gradle" ] || [ -f "$tree/build.gradle.kts" ] \
|
|
8433
|
+
|| [ -f "$tree/settings.gradle" ] || [ -f "$tree/settings.gradle.kts" ] || [ -f "$tree/gradlew" ] \
|
|
8434
|
+
|| [ -f "$tree/build.sbt" ] \
|
|
8435
|
+
|| [ -f "$tree/WORKSPACE" ] || [ -f "$tree/WORKSPACE.bazel" ] || [ -f "$tree/MODULE.bazel" ] \
|
|
8436
|
+
|| [ -f "$tree/setup.py" ] || [ -f "$tree/pyproject.toml" ] \
|
|
8437
|
+
|| compgen -G "$tree/*.csproj" >/dev/null 2>&1 \
|
|
8438
|
+
|| compgen -G "$tree/*.sln" >/dev/null 2>&1 \
|
|
8439
|
+
|| compgen -G "$tree/*.fsproj" >/dev/null 2>&1; then
|
|
8440
|
+
has_foreign_manifest="true"
|
|
8441
|
+
fi
|
|
8442
|
+
# N/A fires on POSITIVE proof-of-no-build, which we can only assert for a
|
|
8443
|
+
# package.json project: it EXISTS and its scripts are build-ish-free and no
|
|
8444
|
+
# foreign manifest / build-tool devDep is present -> a JS CLI/lib that
|
|
8445
|
+
# genuinely has no build step (the founder's invoice case). CRITICAL (the
|
|
8446
|
+
# council's convergence line): a project with NO package.json is "I don't
|
|
8447
|
+
# recognize a build here" = IGNORANCE, not proof-of-no-build. Ignorance must
|
|
8448
|
+
# map to not_run (honest gap), NEVER N/A -- else Zig/autotools/bare-C/meson/
|
|
8449
|
+
# bazel/anything-unborn silently reads VERIFIED. This makes the ENTIRE
|
|
8450
|
+
# no-package.json universe honest by CONSTRUCTION (list-free), which is why
|
|
8451
|
+
# this converges where four rounds of adding-to-a-list could not.
|
|
8452
|
+
if [ "$has_foreign_manifest" = "false" ] && [ -f "$tree/package.json" ]; then
|
|
8453
|
+
local has_build_devdep="false"
|
|
8454
|
+
# A build TOOL as a (dev)dependency means a build exists even if it runs
|
|
8455
|
+
# under a non-"build" script name -> stay not_run, do not claim N/A.
|
|
8456
|
+
if grep -qE '"(vite|webpack|rollup|esbuild|parcel|typescript|tsc|@swc/core|turbopack|rspack|tsup|microbundle)"[[:space:]]*:' "$tree/package.json" 2>/dev/null; then
|
|
8457
|
+
has_build_devdep="true"
|
|
8458
|
+
fi
|
|
8459
|
+
if [ "$has_build_devdep" = "false" ] \
|
|
8460
|
+
&& ! grep -qE '"(build|compile|dist|bundle|webpack|vite|tsc|rollup|esbuild|prepare|prepack)"[[:space:]]*:' "$tree/package.json" 2>/dev/null; then
|
|
8461
|
+
applicable="false"; build_class="none"
|
|
8462
|
+
fi
|
|
8463
|
+
# else: a build-ish script or a build-tool devDep we don't auto-run ->
|
|
8464
|
+
# stays not_run (an honest gap). Known bounded edge: a polyglot repo with
|
|
8465
|
+
# a package.json (no build) co-present with an UNLISTED foreign build
|
|
8466
|
+
# manifest could read N/A; not common and not list-free-fixable, so it is
|
|
8467
|
+
# a documented CONCERN, not a blocker.
|
|
8468
|
+
fi
|
|
8469
|
+
# No package.json (foreign or not) -> build_class stays "present" -> not_run.
|
|
8470
|
+
# has_foreign_manifest=true -> also not_run. Only the narrow package.json-
|
|
8471
|
+
# no-build case above reaches N/A.
|
|
8472
|
+
fi
|
|
8473
|
+
|
|
8474
|
+
if [ "$build_class" = "present" ]; then
|
|
8475
|
+
# A build phase exists but we did not run it -> honest not_run gap. applicable
|
|
8476
|
+
# stays true (a build IS applicable), status not_run (we did not verify it).
|
|
8477
|
+
applicable="true"; ran="false"; status="not_run"
|
|
8478
|
+
log_info "Build check: a build system is present but not auto-run -> honest gap (not verified)"
|
|
8479
|
+
elif [ "$applicable" = "true" ]; then
|
|
8480
|
+
# EXECUTION is expensive (a real vite/webpack/go/cargo build, seconds to
|
|
8481
|
+
# minutes) so we run it AT MOST ONCE per build run, not every iteration --
|
|
8482
|
+
# honoring the "fastest / lowest cost" goal. A freshness marker (mirrors the
|
|
8483
|
+
# test-results.iter pattern) records that the build already ran this run; a
|
|
8484
|
+
# later iteration reuses the recorded verified/failed status instead of
|
|
8485
|
+
# re-building. LOKI_BUILD_CHECK=0 opts out entirely (records N/A-style skip
|
|
8486
|
+
# without running). If a prior result exists and is still valid, keep it.
|
|
8487
|
+
local marker="$quality_dir/.build-check.done"
|
|
8488
|
+
if [ "${LOKI_BUILD_CHECK:-1}" = "0" ]; then
|
|
8489
|
+
log_info "Build check: execution disabled (LOKI_BUILD_CHECK=0); recording applicability only"
|
|
8490
|
+
ran="false"; exit_code="null"; status="not_run"
|
|
8491
|
+
elif [ -f "$marker" ] && [ -f "$out_file" ]; then
|
|
8492
|
+
log_info "Build check: already ran this build; reusing recorded result"
|
|
8493
|
+
return 0
|
|
8494
|
+
else
|
|
8495
|
+
local rc=0
|
|
8496
|
+
( cd "$tree" && timeout "$timeout_s" sh -c "$cmd" >/dev/null 2>&1 ) || rc=$?
|
|
8497
|
+
ran="true"; exit_code="$rc"
|
|
8498
|
+
if [ "$rc" -eq 0 ]; then status="verified"; else status="failed"; fi
|
|
8499
|
+
: > "$marker" 2>/dev/null || true
|
|
8500
|
+
log_info "Build check: $cmd -> exit $rc"
|
|
8501
|
+
fi
|
|
8502
|
+
else
|
|
8503
|
+
# build_class="none": a POSITIVE proof-of-no-build determination -> honest
|
|
8504
|
+
# N/A (the founder's CLI). applicable already false; status not_applicable.
|
|
8505
|
+
applicable="false"; ran="false"; status="not_applicable"
|
|
8506
|
+
log_info "Build check: no build step for this stack (N/A)"
|
|
8507
|
+
fi
|
|
8508
|
+
|
|
8509
|
+
printf '{"timestamp":"%s","command":"%s","ran":%s,"applicable":%s,"exit_code":%s,"duration_sec":null,"status":"%s"}\n' \
|
|
8510
|
+
"$ts" "$cmd" "$ran" "$applicable" "$exit_code" "$status" > "$out_file"
|
|
8511
|
+
return 0
|
|
8512
|
+
}
|
|
8513
|
+
|
|
8357
8514
|
enforce_static_analysis() {
|
|
8358
8515
|
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
8359
8516
|
local quality_dir="$loki_dir/quality"
|
|
@@ -9160,6 +9317,21 @@ _loki_zero_tests_executed() {
|
|
|
9160
9317
|
fi
|
|
9161
9318
|
return 1
|
|
9162
9319
|
;;
|
|
9320
|
+
unittest)
|
|
9321
|
+
# #139: `python3 -m unittest discover` on ZERO discovered tests prints
|
|
9322
|
+
# "Ran 0 tests" + "NO TESTS RAN". Depending on the Python version it
|
|
9323
|
+
# exits 5 (newer, already not-pass) OR 0 (older = a silent fake-green,
|
|
9324
|
+
# the #82/#89 trap). POSITIVE detection so it is inconclusive on BOTH:
|
|
9325
|
+
# a "Ran 0 tests" line (authoritative), or the "NO TESTS RAN" banner,
|
|
9326
|
+
# AND no ran-count >= 1. A real run prints "Ran N test(s)" with N>=1
|
|
9327
|
+
# and "OK"/"FAILED", so it is never downgraded. Verified live: zero ->
|
|
9328
|
+
# "Ran 0 tests"/"NO TESTS RAN"; one -> "Ran 1 test"/"OK".
|
|
9329
|
+
if printf '%s' "$_zt_out" | grep -qE '^Ran 0 tests' 2>/dev/null \
|
|
9330
|
+
|| printf '%s' "$_zt_out" | grep -qE '^NO TESTS RAN$' 2>/dev/null; then
|
|
9331
|
+
return 0
|
|
9332
|
+
fi
|
|
9333
|
+
return 1
|
|
9334
|
+
;;
|
|
9163
9335
|
*)
|
|
9164
9336
|
# Unknown/unparseable runner -> never claim zero-tests (safe default).
|
|
9165
9337
|
return 1
|
|
@@ -9326,6 +9498,17 @@ sys.stdout.write(t.strip())
|
|
|
9326
9498
|
-print -quit 2>/dev/null | grep -q .; then
|
|
9327
9499
|
has_python_project=true
|
|
9328
9500
|
fi
|
|
9501
|
+
elif find "${TARGET_DIR:-.}" -maxdepth 2 -type f \
|
|
9502
|
+
\( -name 'test_*.py' -o -name '*_test.py' \) \
|
|
9503
|
+
-not -path '*/.loki/*' -not -path '*/.git/*' -not -path '*/node_modules/*' \
|
|
9504
|
+
-not -path '*/.venv/*' -not -path '*/venv/*' \
|
|
9505
|
+
-print -quit 2>/dev/null | grep -q .; then
|
|
9506
|
+
# #139: a ROOT-LEVEL (or shallow) test_*.py / *_test.py with NO tests/
|
|
9507
|
+
# dir and NO config file. The founder's invoice CLI had exactly this
|
|
9508
|
+
# shape (test_invoice_cli.py at the root) -> was missed -> tests read
|
|
9509
|
+
# "not run" on a genuinely-passing 16-test unittest suite. This is the
|
|
9510
|
+
# same false-negative class as #79 (validated work reads as unvalidated).
|
|
9511
|
+
has_python_project=true
|
|
9329
9512
|
fi
|
|
9330
9513
|
if [ "$has_python_project" = "true" ] && command -v pytest &>/dev/null; then
|
|
9331
9514
|
test_runner="pytest"
|
|
@@ -9342,6 +9525,29 @@ sys.stdout.write(t.strip())
|
|
|
9342
9525
|
[ "$pytest_exit" -ne 0 ] && test_passed=false
|
|
9343
9526
|
details="pytest: $(echo "$output" | tail -5 | tr '\n' ' ')"
|
|
9344
9527
|
fi
|
|
9528
|
+
elif [ "$has_python_project" = "true" ] && command -v python3 &>/dev/null; then
|
|
9529
|
+
# #139: pytest ABSENT but a Python test suite exists. stdlib unittest
|
|
9530
|
+
# discovery runs test_*.py with ZERO third-party deps -- so a legit
|
|
9531
|
+
# unittest suite (the invoice CLI's 16 tests) is verified instead of
|
|
9532
|
+
# silently read as "not run". The zero-test guard (unittest case) makes
|
|
9533
|
+
# a zero-discovery run inconclusive, NOT a fake-green pass (unittest
|
|
9534
|
+
# prints "NO TESTS RAN" and exits 0 -- the #82/#89 trap, guarded).
|
|
9535
|
+
test_runner="unittest"
|
|
9536
|
+
local output unittest_exit _ut_to
|
|
9537
|
+
_ut_to="${LOKI_PYTEST_TIMEOUT:-${LOKI_GATE_TIMEOUT:-300}}"
|
|
9538
|
+
local _ut_cmd=(timeout "${_ut_to}s")
|
|
9539
|
+
command -v gtimeout &>/dev/null && _ut_cmd=(gtimeout "${_ut_to}s")
|
|
9540
|
+
command -v timeout &>/dev/null || command -v gtimeout &>/dev/null || _ut_cmd=()
|
|
9541
|
+
output=$(cd "${TARGET_DIR:-.}" && "${_ut_cmd[@]}" python3 -m unittest discover -p 'test_*.py' 2>&1)
|
|
9542
|
+
unittest_exit=$?
|
|
9543
|
+
if [ "$unittest_exit" -eq 124 ]; then
|
|
9544
|
+
test_passed=false
|
|
9545
|
+
log_warn "unittest gate timed out after ${_ut_to}s (exit 124)"
|
|
9546
|
+
details="unittest: TIMED OUT after ${_ut_to}s -- $(echo "$output" | tail -3 | tr '\n' ' ')"
|
|
9547
|
+
else
|
|
9548
|
+
[ "$unittest_exit" -ne 0 ] && test_passed=false
|
|
9549
|
+
details="unittest: $(echo "$output" | tail -5 | tr '\n' ' ')"
|
|
9550
|
+
fi
|
|
9345
9551
|
fi
|
|
9346
9552
|
fi
|
|
9347
9553
|
|
|
@@ -17574,6 +17780,14 @@ if __name__ == "__main__":
|
|
|
17574
17780
|
fi
|
|
17575
17781
|
emit_stage_complete "static_analysis" "$_stg_ok" "$_stg_t0"
|
|
17576
17782
|
fi
|
|
17783
|
+
# Build check (#47). Records .loki/quality/build-results.json each
|
|
17784
|
+
# iteration so the Evidence Receipt tells the truth about the build:
|
|
17785
|
+
# a real build that ran (verified/failed) or an HONEST N/A when the
|
|
17786
|
+
# stack has no build step. Advisory (best-effort): a build failure
|
|
17787
|
+
# surfaces as a receipt gap via facts.build, never a hard loop block,
|
|
17788
|
+
# mirroring the security scan. Absent writer historically = facts.build
|
|
17789
|
+
# permanently not_run for every project (the founder-reported defect).
|
|
17790
|
+
enforce_build_check || true
|
|
17577
17791
|
# Secure-by-default scan (v7.87.0). Advisory by default (never
|
|
17578
17792
|
# blocks); records .loki/quality/security-findings.json each
|
|
17579
17793
|
# iteration. Blocks only on un-waived HIGH when LOKI_SECURE_GATE=block.
|
package/autonomy/verify.sh
CHANGED
|
@@ -350,6 +350,18 @@ _verify_zero_tests_executed() {
|
|
|
350
350
|
fi
|
|
351
351
|
return 1
|
|
352
352
|
;;
|
|
353
|
+
unittest)
|
|
354
|
+
# #139: `python3 -m unittest discover` on ZERO discovered tests prints
|
|
355
|
+
# "Ran 0 tests" + "NO TESTS RAN". On older Python it EXITS 0 (a silent
|
|
356
|
+
# fake-green), on newer it exits 5 (already not-pass). POSITIVE detection
|
|
357
|
+
# so it is inconclusive on BOTH. A real run prints "Ran N test(s)" (N>=1)
|
|
358
|
+
# + "OK"/"FAILED" -> never downgraded. Mirrors run.sh's guard exactly.
|
|
359
|
+
if printf '%s' "$_zt_out" | grep -qE '^Ran 0 tests' 2>/dev/null \
|
|
360
|
+
|| printf '%s' "$_zt_out" | grep -qE '^NO TESTS RAN$' 2>/dev/null; then
|
|
361
|
+
return 0
|
|
362
|
+
fi
|
|
363
|
+
return 1
|
|
364
|
+
;;
|
|
353
365
|
*)
|
|
354
366
|
return 1
|
|
355
367
|
;;
|
|
@@ -421,9 +433,19 @@ verify_gate_tests() {
|
|
|
421
433
|
if command -v pytest >/dev/null 2>&1; then
|
|
422
434
|
runner="pytest"
|
|
423
435
|
out="$(cd "$tree" && timeout "$timeout_s" pytest --tb=short 2>&1)" || rc=$?
|
|
436
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
437
|
+
# #139 parity: pytest ABSENT but a Python suite exists -> run it with
|
|
438
|
+
# stdlib unittest (zero third-party deps) instead of going blindly
|
|
439
|
+
# inconclusive. A real passing test_*.py suite is now VERIFIED here
|
|
440
|
+
# too, matching run.sh's enforce_test_coverage. Zero-discovery is
|
|
441
|
+
# handled below (unittest exits 5 on newer Python = fail-not-pass, or
|
|
442
|
+
# a "Ran 0 tests" line detected by the zero-test guard on the run.sh
|
|
443
|
+
# mirror) so this never green-washes an empty suite.
|
|
444
|
+
runner="unittest"
|
|
445
|
+
out="$(cd "$tree" && timeout "$timeout_s" python3 -m unittest discover -p 'test_*.py' 2>&1)" || rc=$?
|
|
424
446
|
else
|
|
425
447
|
# Applicable but cannot run -> inconclusive (Entanglement 2).
|
|
426
|
-
_verify_add_gate "tests" "inconclusive" "pytest" "python project detected but pytest
|
|
448
|
+
_verify_add_gate "tests" "inconclusive" "pytest" "python project detected but no pytest/python3 on PATH" "true"
|
|
427
449
|
return 0
|
|
428
450
|
fi
|
|
429
451
|
fi
|
package/dashboard/__init__.py
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Evidence Receipt: honest build applicability (done once, for all customers)
|
|
2
|
+
|
|
3
|
+
## The defect (founder-reported, 2026-07-03)
|
|
4
|
+
|
|
5
|
+
A sophisticated user built a Node CLI (invoice tool) and saw the Evidence Receipt say
|
|
6
|
+
"Build: not run - build not run", "Live health check: not captured", "No screenshot",
|
|
7
|
+
headline "Working, with gaps". He concluded *nothing was validated* -- and that read is the
|
|
8
|
+
bug: it destroys trust in genuinely-validated work.
|
|
9
|
+
|
|
10
|
+
## Ground truth (from the persisted proof of run-20260703034719)
|
|
11
|
+
|
|
12
|
+
- `facts.tests` = `verified`, exit 0 -- tests GENUINELY ran and passed.
|
|
13
|
+
- `facts.security` = `ran:true, clean` -- security GENUINELY ran, real clean scan.
|
|
14
|
+
- `facts.build` = `ran:false, status:not_run, command:""` -- and this is counted as a GAP
|
|
15
|
+
by `_compute_degraded` (proof-generator.py:903-908), which forces "WITH GAPS".
|
|
16
|
+
- `facts.quality_gates` = `[]` (EMPTY) -- verify.sh's deterministic build gate does NOT
|
|
17
|
+
reach the SaaS proof.
|
|
18
|
+
|
|
19
|
+
## Root cause (systemic, every build, every customer)
|
|
20
|
+
|
|
21
|
+
`build-results.json` has **NO writer**. `_collect_build` (proof-generator.py:319) reads
|
|
22
|
+
`.loki/quality/build-results.json`, which nothing ever writes, so `facts.build` defaults to
|
|
23
|
+
`not_run` (line 333) for EVERY build. The sibling collectors DO have writers in run.sh
|
|
24
|
+
(`enforce_static_analysis` 8357, `run_secure_scan` 8720, `enforce_test_coverage` 9170 ->
|
|
25
|
+
test-results.json / security-findings.json / static-analysis.json). Build was never wired.
|
|
26
|
+
|
|
27
|
+
So "Build: not run" is not a per-project decision -- it is a permanent, structural
|
|
28
|
+
mislabel. A build that has no build step (a CLI) and a build that genuinely skipped a real
|
|
29
|
+
build both read identically as "not run" and both count as a gap.
|
|
30
|
+
|
|
31
|
+
## The fix (honest, no fabrication, no mocking -- the founder's hard constraint)
|
|
32
|
+
|
|
33
|
+
Add the missing writer as a sibling collector, with a POSITIVE applicability determination:
|
|
34
|
+
|
|
35
|
+
1. **run.sh `enforce_build_check()`** (new, sibling to enforce_static_analysis): detect the
|
|
36
|
+
stack's build command (npm "build" script / go build / cargo build -- same detectors as
|
|
37
|
+
verify.sh:250-291). Then:
|
|
38
|
+
- build command exists -> RUN it -> record `{ran:true, command, exit_code, status:
|
|
39
|
+
verified|failed}`.
|
|
40
|
+
- genuinely NO build step for this stack -> record `{ran:false, applicable:false,
|
|
41
|
+
status:not_applicable, command:""}`. This is a POSITIVE "no build phase" signal, not an
|
|
42
|
+
absent file.
|
|
43
|
+
Write `.loki/quality/build-results.json`. Call it where the sibling collectors are called.
|
|
44
|
+
|
|
45
|
+
2. **proof-generator.py `_collect_build`**: pass through `applicable:false` -> status
|
|
46
|
+
`not_applicable`. CRITICAL: `not_applicable` ONLY from an explicit `applicable:false` in
|
|
47
|
+
the file. File ABSENT or unknown status -> `not_run` (honest gap), NEVER N/A. Absent must
|
|
48
|
+
never mean N/A (that would flip un-built projects green -- fake-green).
|
|
49
|
+
|
|
50
|
+
3. **proof-generator.py `_compute_degraded`**: `not_applicable` is NOT a gap (excluded from
|
|
51
|
+
the weak set for build). A failed/skipped/not_run build stays a gap. Mirror-image guard:
|
|
52
|
+
a real build script that FAILS still emits `failed` -> gap -> never green.
|
|
53
|
+
|
|
54
|
+
4. **proof-generator.py status normalizer (line 215)**: `not_applicable` passes through.
|
|
55
|
+
|
|
56
|
+
5. **SPA adapter.ts**: `not_applicable` renders "N/A - no build step for this stack" (a
|
|
57
|
+
neutral/pass tier, NOT a warn/gap), distinct from "not run".
|
|
58
|
+
|
|
59
|
+
## Anti-fake-green invariants (the trust moat)
|
|
60
|
+
|
|
61
|
+
- N/A ONLY on positive `applicable:false`. Absent file / unknown status -> honest gap.
|
|
62
|
+
- A build script that got skipped or failed STILL shows a gap and CANNOT go green.
|
|
63
|
+
- The directional regression test (write FIRST): Node project WITH a build script whose
|
|
64
|
+
build FAILS -> `facts.build=failed` -> degraded -> headline NOT green. And: build gate
|
|
65
|
+
MISSING -> still a gap, not N/A.
|
|
66
|
+
- N/A does not gate the seal any more than a gap does; it is honest applicability, not a pass.
|
|
67
|
+
|
|
68
|
+
## Measurable before/after
|
|
69
|
+
|
|
70
|
+
Before: invoice CLI -> "Build: not run", headline "Working, with gaps".
|
|
71
|
+
After: fresh invoice CLI build -> "Build: N/A (no build step for this stack)", tests passed,
|
|
72
|
+
security scanned-clean, headline VERIFIED (no un-closed applicable gaps).
|
|
73
|
+
|
|
74
|
+
## Constraints
|
|
75
|
+
|
|
76
|
+
Verdict-core: council 3/3 + local-ci + 14 version locations. Verify via
|
|
77
|
+
function-extraction/golden harness -- NEVER run the engine build from the worktree. Run the
|
|
78
|
+
EXISTING tests/test_proof_generator.py green before AND after (it has build-results.json
|
|
79
|
+
fixtures that this change touches). Deploy to live SaaS is `bun install -g loki-mode@<ver>`.
|
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:** v7.
|
|
5
|
+
**Version:** v7.120.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.
|
|
2
|
+
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.120.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function R($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([c1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}Error: jq is required but not installed.${J}
|
|
3
3
|
`),process.stdout.write(`Install with:
|
|
4
4
|
`),process.stdout.write(` brew install jq (macOS)
|
|
5
5
|
`),process.stdout.write(` apt install jq (Debian/Ubuntu)
|
|
@@ -814,4 +814,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
814
814
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
815
815
|
`),process.stderr.write(K8),2}}e1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var SZ=await NZ(Bun.argv.slice(2));process.exit(SZ);
|
|
816
816
|
|
|
817
|
-
//# debugId=
|
|
817
|
+
//# debugId=4C6F828B3BD8743B64756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
3
|
"mcpName": "io.github.asklokesh/loki-mode",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.120.0",
|
|
5
5
|
"description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "loki-mode",
|
|
4
4
|
"displayName": "Loki Mode",
|
|
5
|
-
"version": "7.
|
|
5
|
+
"version": "7.120.0",
|
|
6
6
|
"description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Autonomi",
|