loki-mode 9.57.0 → 9.59.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 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.57.0
6
+ # Loki Mode v9.59.0
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.57.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
473
+ **v9.59.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 9.57.0
1
+ 9.59.0
@@ -72,6 +72,38 @@ for _cc_cand in "$_LOKI_CC_DIR/lib/secret-scan.sh" "${SCRIPT_DIR:-}/lib/secret-s
72
72
  done
73
73
  unset _LOKI_CC_DIR _cc_cand
74
74
 
75
+ # BACKLOG 134: the three test-result readers (council_heuristic_review,
76
+ # council_evaluate_member, council_devils_advocate_review) resolve their
77
+ # interpreter through run.sh's _loki_snapshot_py_tool and run it -I -S: -E alone
78
+ # still loads user site-packages, whose .pth "import" lines run before any
79
+ # sys.path scrub and can forge json.load (see that function's comment in
80
+ # run.sh for the full rationale and accepted gaps). run.sh sources this file
81
+ # BEFORE it defines _loki_snapshot_py_tool, so in production run.sh's own
82
+ # definition replaces this one; the copy below only serves callers that source
83
+ # this file on its own (tests). run.sh is the source of truth: keep this copy
84
+ # byte-identical to it.
85
+ declare -F _loki_snapshot_py_tool >/dev/null 2>&1 || \
86
+ _loki_snapshot_py_tool() {
87
+ local c
88
+ for c in /usr/bin/python3 /bin/python3; do
89
+ [ -x "$c" ] && [ ! -d "$c" ] && "$c" -I -S -c '' >/dev/null 2>&1 && { printf '%s\n' "$c"; return 0; }
90
+ done
91
+ local dir
92
+ local IFS=:
93
+ for dir in $PATH; do
94
+ case "$dir" in
95
+ /*) ;;
96
+ *) continue ;;
97
+ esac
98
+ if [ -x "$dir/python3" ] && [ ! -d "$dir/python3" ] \
99
+ && "$dir/python3" -I -S -c '' >/dev/null 2>&1; then
100
+ printf '%s\n' "$dir/python3"
101
+ return 0
102
+ fi
103
+ done
104
+ return 1
105
+ }
106
+
75
107
  # Council configuration
76
108
  COUNCIL_ENABLED=${LOKI_COUNCIL_ENABLED:-true}
77
109
  COUNCIL_SIZE=${LOKI_COUNCIL_SIZE:-3}
@@ -3484,8 +3516,10 @@ council_heuristic_review() {
3484
3516
  # evidence -> REJECT (keep iterating), never a heuristic APPROVE.
3485
3517
  local _tr_file=".loki/quality/test-results.json"
3486
3518
  local _tests_ok=0
3487
- if [ -f "$_tr_file" ] && command -v python3 >/dev/null 2>&1; then
3488
- _tests_ok=$(_TR="$_tr_file" python3 -E -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
3519
+ local _tr_py=""
3520
+ [ -f "$_tr_file" ] && _tr_py="$(_loki_snapshot_py_tool)" || _tr_py=""
3521
+ if [ -n "$_tr_py" ]; then
3522
+ _tests_ok=$(_TR="$_tr_file" "$_tr_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
3489
3523
  import json, os
3490
3524
  try:
3491
3525
  with open(os.environ['_TR']) as f:
@@ -3668,9 +3702,11 @@ council_evaluate_member() {
3668
3702
  local tr_file="$loki_dir/quality/test-results.json"
3669
3703
  local test_evidence="absent" # absent | pass | fail | inconclusive
3670
3704
  local test_runner_seen="none"
3671
- if [ -f "$tr_file" ]; then
3705
+ local _tr_py=""
3706
+ [ -f "$tr_file" ] && _tr_py="$(_loki_snapshot_py_tool)" || _tr_py=""
3707
+ if [ -n "$_tr_py" ]; then
3672
3708
  local _tr_status
3673
- _tr_status=$(_TR_FILE="$tr_file" python3 -E -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
3709
+ _tr_status=$(_TR_FILE="$tr_file" "$_tr_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
3674
3710
  import json, os, sys
3675
3711
  try:
3676
3712
  with open(os.environ['_TR_FILE']) as f:
@@ -3727,14 +3763,25 @@ else:
3727
3763
  # Cases 6 and 7, with Case 8 as the positive control proving this
3728
3764
  # can still reach COMPLETE when every queue really is empty.
3729
3765
  local unfinished=0
3730
- local _q _qcount
3766
+ local _q _qcount _q_py=""
3731
3767
  for _q in pending in-progress blocked; do
3732
3768
  [ -f "$loki_dir/queue/${_q}.json" ] || continue
3733
- _qcount=$(_QUEUE_FILE="$loki_dir/queue/${_q}.json" python3 -E -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]; import json, os
3769
+ # Same -I -S interpreter as the test-result readers: a user-site
3770
+ # .pth must not be able to make this count read 0.
3771
+ [ -n "$_q_py" ] || _q_py="$(_loki_snapshot_py_tool)" || _q_py=""
3772
+ _qcount=""
3773
+ [ -n "$_q_py" ] && _qcount=$(_QUEUE_FILE="$loki_dir/queue/${_q}.json" "$_q_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]; import json, os
3734
3774
  d = json.load(open(os.environ['_QUEUE_FILE']))
3735
- print(len(d.get('tasks', d) if isinstance(d, dict) else d))" 2>/dev/null || echo "0")
3736
- # Guard against a non-numeric read (malformed file, python absent).
3737
- case "$_qcount" in ''|*[!0-9]*) _qcount=0 ;; esac
3775
+ print(len(d.get('tasks', d) if isinstance(d, dict) else d))" 2>/dev/null)
3776
+ # Fail closed: a present queue file whose count cannot be read
3777
+ # (malformed file, no interpreter) is not evidence it is empty.
3778
+ case "$_qcount" in
3779
+ ''|*[!0-9]*)
3780
+ blocked="true"
3781
+ reasons="${reasons}${_q} queue count unreadable; "
3782
+ continue
3783
+ ;;
3784
+ esac
3738
3785
  if [ "$_qcount" -gt 0 ]; then
3739
3786
  unfinished=$((unfinished + _qcount))
3740
3787
  blocked="true"
@@ -3969,9 +4016,11 @@ council_devils_advocate_review() {
3969
4016
  # (nothing writes .loki/logs/test-*.log, so an empty glob is the normal case
3970
4017
  # and must never veto a unanimous COMPLETE on its own).
3971
4018
  local tr_file="$loki_dir/quality/test-results.json"
3972
- if [ -f "$tr_file" ]; then
4019
+ local _tr_py=""
4020
+ [ -f "$tr_file" ] && _tr_py="$(_loki_snapshot_py_tool)" || _tr_py=""
4021
+ if [ -n "$_tr_py" ]; then
3973
4022
  local _tr_status
3974
- _tr_status=$(_TR_FILE="$tr_file" python3 -E -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
4023
+ _tr_status=$(_TR_FILE="$tr_file" "$_tr_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
3975
4024
  import json, os, sys
3976
4025
  try:
3977
4026
  with open(os.environ['_TR_FILE']) as f:
@@ -3994,6 +4043,8 @@ else:
3994
4043
  ((issues_found++))
3995
4044
  issue_details="${issue_details}structured test results red (pass==false); "
3996
4045
  fi
4046
+ elif [ -f "$tr_file" ]; then
4047
+ ((issues_found++)); issue_details="${issue_details}test results unreadable (no isolated python3); "
3997
4048
  fi
3998
4049
  # Additional source: any legacy test log that shows no pass indicator is a red
3999
4050
  # signal. Missing logs are NOT counted (this path is not written by the runner).
@@ -4009,8 +4060,18 @@ else:
4009
4060
  # Skeptical check 2: Are there still failing tasks in the queue?
4010
4061
  if [ -f "$loki_dir/queue/failed.json" ]; then
4011
4062
  local failed_count
4012
- failed_count=$(_QUEUE_FILE="$loki_dir/queue/failed.json" python3 -E -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]; import json, os; print(len(json.load(open(os.environ['_QUEUE_FILE']))))" 2>/dev/null || echo "0")
4013
- if [ "$failed_count" -gt 0 ]; then
4063
+ local failed_count="" _fq_py=""
4064
+ _fq_py="$(_loki_snapshot_py_tool)" || _fq_py=""
4065
+ [ -n "$_fq_py" ] && failed_count=$(_QUEUE_FILE="$loki_dir/queue/failed.json" "$_fq_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]; import json, os; print(len(json.load(open(os.environ['_QUEUE_FILE']))))" 2>/dev/null)
4066
+ # Fail closed: an unreadable failed-queue count (malformed, no
4067
+ # interpreter) vetoes instead of reading as zero.
4068
+ case "$failed_count" in
4069
+ ''|*[!0-9]*)
4070
+ ((issues_found++))
4071
+ issue_details="${issue_details}failed queue count unreadable; "
4072
+ ;;
4073
+ esac
4074
+ if [ "${failed_count:-0}" -gt 0 ] 2>/dev/null; then
4014
4075
  ((issues_found++))
4015
4076
  issue_details="${issue_details}$failed_count tasks in failed queue; "
4016
4077
  fi
@@ -4492,8 +4553,11 @@ _council_convergence_evidence_green() {
4492
4553
  # Affirmative test-green is REQUIRED: a real runner that passed. A missing
4493
4554
  # file or runner=="none" (no suite) is NOT affirmative evidence -> not green.
4494
4555
  [ -f "$tr_file" ] || return 1
4556
+ # -I -S interpreter (see _loki_snapshot_py_tool); none resolvable -> not green.
4557
+ local _cv_py
4558
+ _cv_py="$(_loki_snapshot_py_tool)" || return 1
4495
4559
  local tr_state
4496
- tr_state=$(_TR_FILE="$tr_file" python3 -E -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
4560
+ tr_state=$(_TR_FILE="$tr_file" "$_cv_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
4497
4561
  import json, os, sys
4498
4562
  try:
4499
4563
  with open(os.environ['_TR_FILE']) as f:
@@ -4516,7 +4580,7 @@ print('yes' if (runner != 'none' and passed is True
4516
4580
  local results_file="${TARGET_DIR:-.}/.loki/checklist/verification-results.json"
4517
4581
  if [ -f "$results_file" ]; then
4518
4582
  local cl_state
4519
- cl_state=$(_RESULTS_FILE="$results_file" python3 -E -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
4583
+ cl_state=$(_RESULTS_FILE="$results_file" "$_cv_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
4520
4584
  import json, os, sys
4521
4585
  try:
4522
4586
  with open(os.environ['_RESULTS_FILE']) as f:
package/autonomy/loki CHANGED
@@ -33945,8 +33945,10 @@ except Exception:
33945
33945
  # Only scan added lines (starting with "+") to avoid flagging deleted code
33946
33946
  while IFS= read -r match; do
33947
33947
  [ -z "$match" ] && continue
33948
- local ml
33949
- ml=$(echo "$match" | cut -d: -f1)
33948
+ # grep -n emits "N:content"; take the number before the first
33949
+ # colon without forking echo+cut per line (matters at finding
33950
+ # counts in the thousands+).
33951
+ local ml="${match%%:*}"
33950
33952
  _ci_add_finding "diff" "$ml" "$sev" "$cat" "$finding" "$suggestion"
33951
33953
  done < <(printf '%s\n' "$diff_content" \
33952
33954
  | grep -n '^+' \
@@ -34089,8 +34091,11 @@ TEST_SUGGEST_PY
34089
34091
  # --- Tally findings ---
34090
34092
  local count_critical=0 count_high=0 count_medium=0 count_low=0 count_info=0
34091
34093
  for f in "${findings[@]+"${findings[@]}"}"; do
34092
- local sev
34093
- sev=$(echo "$f" | cut -d'|' -f3)
34094
+ # "file|line|sev|cat|finding|suggestion" (see _ci_add_finding above).
34095
+ # No echo+cut fork per finding -- this loop runs once per finding and
34096
+ # dominated the runtime at the fixture's old ARG_MAX-scale sizes.
34097
+ local sev _tally_file _tally_line _tally_cat _tally_finding _tally_suggestion
34098
+ IFS='|' read -r _tally_file _tally_line sev _tally_cat _tally_finding _tally_suggestion <<< "$f"
34094
34099
  case "$sev" in
34095
34100
  CRITICAL) count_critical=$((count_critical + 1)) ;;
34096
34101
  HIGH) count_high=$((count_high + 1)) ;;
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "9.57.0"
10
+ __version__ = "9.59.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
package/docs/v10/BOARD.md CHANGED
@@ -65,14 +65,14 @@ set is declared to avoid overlap with those and with each other.
65
65
  | S-46 | BACKLOG 115: status inference presents a guess as a state | web-app/server.py (`_infer_session_status` only), cockpit/useCockpitState.ts (completed fallback + checklist scoping) | MEDIUM | red: a paused/failed run older than 5 min reads "completed"; green: an explicit unknown/stale status is possible and surfaced | released@2026-09-27T15:00Z | File set disjoint from any moat scanner or run.sh trust-core path. Merged 18013159, 11/11 reconfirmed on main. Terminal statuses trusted regardless of file age; a stale active-looking status or a bare source file now falls through to an honest 'unknown' instead of guessed 'completed'. Also fixed the checklist-scoping bug (a historical session no longer shows the globally-running project's live checklist). Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
66
66
  | S-47 | BACKLOG 116: key misreads hide or zero real data (GitHubPRsPanel statusCheckRollup/state case, EvidenceReceiptPanel cost_usd/files_changed keys, memory-browser camelCase mismatch) | web-app/src/components/GitHubPRsPanel.tsx, web-app/src/components/EvidenceReceiptPanel.tsx, dashboard-ui/components/loki-memory-browser.js, dashboard-ui/components/loki-overview.js | MEDIUM | red: each named field misreads and shows a false empty/zero; green: each reads the real emitted key, one test per site | released@2026-09-27T15:00Z | EvidenceReceiptPanel.tsx already touched by GF-5 (merged) for a different concern (classify()); this slice is the cost_usd/files_changed key-path only, disjoint lines. Merged 11a3e7fb (auto-merged onto loki-overview.js cleanly, different function), 34/34 reconfirmed on main; builder verified every key against the real writer/API source, not the backlog description alone. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
67
67
  | S-48 | BACKLOG 128: stale docs assert a `{"runner":"none","pass":true}` writer shape that does not exist (D20 already corrected the real shape) | references/core-workflow.md, docs/VERIFIED-COMPLETION-PLAN.md, tests/test-council-devils-advocate.sh, tests/test-da-veto.sh, tests/test-council-convergence-floor.sh | LOW | docs describe the real writer shape (`pass:"inconclusive"` string); the 3 named test fixtures use the real shape instead of a synthetic `pass:true` | released@2026-09-27T10:58Z | STALE DUPLICATE, already fixed: builder found all 5 target locations already correct (commit c06b50ee, "S-33"), verified directly against `enforce_test_coverage` in run.sh and re-ran all 3 test suites clean (6/6, 9/9, 40/40). No new commit needed. Marking released since the underlying fix already shipped in main. One non-blocking follow-up noted: `test-council-convergence-floor.sh` NEGATIVE-B passes even without the `runner!='none'` guard, since `pass:"inconclusive"` alone already fails `pass===True` -- candidate for a future assertion-hardening slice, not filed as a new backlog item yet. |
68
- | S-49 | BACKLOG 134: completion-council.sh's 3 test-result readers use bare PATH python3 -E -c with no -I/-S/fixed-path resolution (same bypass class BACKLOG 129 already closed for run.sh) | autonomy/completion-council.sh (council_heuristic_review, council_evaluate_member, council_devils_advocate_review only), new moat case planting a user-site .pth | HIGH | red: a planted .pth flips a genuinely-failing test-results.json to CONFIRMED_COMPLETE; green: routed through a resolved-interpreter helper matching `_loki_snapshot_py_tool`'s pattern, .pth has no effect | review@2026-09-27T15:46Z | Disjoint from S-18/BACKLOG 149's run.sh GH-token work and from S-17's vote-text assertions (already merged, different lines in the same file -- verify no line-range collision before editing). Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 6b276aaf (wf_29a75a9c-652-1): 3 readers via _loki_snapshot_py_tool -I -S, new P2 case red pre-fix and green after, mutation red. 2 HIGH reviewers dispatched 15:16. Reviewer B APPROVE; reviewer A CONCERN (reproduced): queue readers at ~3769/~4050 still use bare python3 -E, so a .pth flips the verdict. Rework sent 15:28. Rework 17eeb940+8ce7f1b1: queue and convergence readers isolated and fail closed, no-interpreter veto, byte-identity test. Re-review (HIGH) dispatched 15:40. |
68
+ | S-49 | BACKLOG 134: completion-council.sh's 3 test-result readers use bare PATH python3 -E -c with no -I/-S/fixed-path resolution (same bypass class BACKLOG 129 already closed for run.sh) | autonomy/completion-council.sh (council_heuristic_review, council_evaluate_member, council_devils_advocate_review only), new moat case planting a user-site .pth | HIGH | red: a planted .pth flips a genuinely-failing test-results.json to CONFIRMED_COMPLETE; green: routed through a resolved-interpreter helper matching `_loki_snapshot_py_tool`'s pattern, .pth has no effect | merged@2026-09-27T16:01Z | Disjoint from S-18/BACKLOG 149's run.sh GH-token work and from S-17's vote-text assertions (already merged, different lines in the same file -- verify no line-range collision before editing). Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 6b276aaf (wf_29a75a9c-652-1): 3 readers via _loki_snapshot_py_tool -I -S, new P2 case red pre-fix and green after, mutation red. 2 HIGH reviewers dispatched 15:16. Reviewer B APPROVE; reviewer A CONCERN (reproduced): queue readers at ~3769/~4050 still use bare python3 -E, so a .pth flips the verdict. Rework sent 15:28. Rework 17eeb940+8ce7f1b1: queue and convergence readers isolated and fail closed, no-interpreter veto, byte-identity test. Re-review (HIGH) dispatched 15:40. Round 2: HIGH reviewer A APPROVE (the .pth attack fails on every reader in the 3 functions; 7 of 8 mutations red; M3b checklist leg is a test gap, fix holds on a direct probe). Reviewer B approved round 1. Train 4. Follow-up: council readers outside the 3 functions (~1425, ~1564, ~4215-4245). Cherry-picked onto main for train 4 (3 commits); D27 checks green except tests/test-select-tests.sh, which hit the 60s cap (rc=124; CI runs it in full; reviewer ran 38/38). |
69
69
  | S-50 | BACKLOG 137: S-02's dynamic-repo-root fix breaks P7's own sed-workaround for the same bug | tests/moat/p7-no-fabricated-data.sh (lines 477-501, the client-route helper invocation only) | MEDIUM | red: P7.webapp-client-routes-exist fails post-S-02 because the sed target no longer exists; green: helpers run in place or receive the root explicitly, case passes again | ready@2026-09-27T10:50Z | NARROW SCOPE WARNING: this file is the most contended file this session (S-27/S-29/S-30/S-31/S-32 lineage). Builder must rebase onto main AFTER the S-29 merge-pass lands and touch ONLY the named 477-501 invocation block, nothing else in the file. Coordinate with Captain before starting if S-29's merge is still in flight. |
70
70
  | S-51 | BACKLOG 117: stale or false static copy (version numbers, license, Gemini support, gate counts, invented testimonial) | web-app/src/components/WhatsNew.tsx (actual path, not pages/ as originally listed), web-app/src/components/Footer.tsx, web-app/src/pages/SettingsPage.tsx, web-app/src/pages/ComparePage.tsx, web-app/src/components/BenefitCards.tsx, web-app/src/components/TrustedBy.tsx | LOW | grep confirms each named false claim is gone (version bumped to current, MIT replaced with BUSL-1.1, Gemini removed, gate count corrected to 8, invented testimonial line removed) | released@2026-09-27T15:00Z | Merged 7ad546a7, clean cherry-pick, direct Captain review (LOW tier, pure copy diff, verified against VERSION/LICENSE/CLAUDE.md source of truth). TrustedBy.tsx reviewed and left unchanged (no fabricated content found). BenefitCards.tsx's Gemini removal also dropped it from Multi-AI copy (not just the gate count). Follow-ups flagged, not filed as new backlog yet: SettingsPage.tsx build date still hardcoded (2026-03-24, out of scope for a copy-only slice since deriving it live is a logic change); PROVIDERS array still lists Gemini as active (behavior, not copy); DocsSidebar.tsx/ContextualHelp.tsx/CostEstimator.tsx/ProjectWorkspace.tsx/SystemSettingsPage.tsx also reference Gemini or stale gate counts, out of this slice's file set; WhatsNew.tsx/Footer.tsx versions are not in CLAUDE.md's release version-bump checklist so will drift stale again next release. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
71
71
  | S-52 | BACKLOG 122: receipt field `first_result_verified_patch` is True for any first code change, naming a verification that did not run | autonomy/lib/proof-generator.py (the named field only) | LOW | field is renamed additively (`first_result_kind` already exists) or set only from an actually-verified result, with a test; no consumer of the old field silently breaks | released@2026-09-27T15:00Z | Direct fix chosen over additive rename (advisor-caught: a new field would duplicate first_result_kind byte-for-byte, since no producer anywhere ever sets this field True). Full consumer survey done; found and fixed a real bug the fix would otherwise have caused (loki-overview.js's "Plan only" label, repointed at first_result_kind). Merged 0380ad6f, 21/21 + 11/11 reconfirmed on main. Direct Captain review (LOW tier). Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
72
72
  | S-53 | BACKLOG 138 fix direction: trusted git push/PR calls should ignore agent-controlled hooks/config (planted pre-push hook, credential.helper, core.fsmonitor, core.sshCommand can see GH_TOKEN during Loki's own trusted push) | autonomy/run.sh (create_session_pr / the trusted push call sites only, not the withhold function itself which S-18/BACKLOG-149's rework owns) | HIGH | red: a planted `.git/hooks/pre-push` sees GH_TOKEN during Loki's trusted push (reproduce reviewer 3's GF-3 finding); green: the trusted push explicitly neutralizes hooksPath/credential.helper/sshCommand for that one invocation, hook sees nothing | ready@2026-09-27T10:58Z | Sequencing note: wait for S-18/BACKLOG 149's rework to land first (touches nearby withhold/re-grant code in the same file); rebase onto that before starting to avoid a conflict. Do not touch the withhold function itself, only the push-call invocation. |
73
- | S-54 | BACKLOG 139: shipped composite GitHub Actions put GH_TOKEN in the SAME step as the agent invocation (issue-to-pr) | .github/actions/issue-to-pr/action.yml, root action.yml | HIGH | the issue content is fetched in an earlier, separate step that does not hold the token; the agent-invoking step receives only a file, never a step with GH_TOKEN in its env | review@2026-09-27T15:46Z | NOTE: S-41 (merged 8a714c73) may have already partially addressed this exact split for .github/actions/issue-to-pr/action.yml -- builder must read S-41's actual merged diff first via `git show 8a714c73` and confirm what remains before starting, to avoid redundant or conflicting work. If S-41 already fully closed this, report back as a stale duplicate rather than editing. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 9c64533b: the fix was already in S-41 (8a714c73), so it adds only a step-level test (17 failures on pre-S-41 files, 0 now, 2 mutations red). HIGH reviewer dispatched 15:16. CONCERN: hand-listed action files; detection misses npx loki-mode@ver, path or variable calls and scripts. Rework sent 15:28. Rework 2b81483d: globbed actions, wider detector; it blanked tokens in 3 production steps (incl. Run Quality Review). HIGH re-review is checking whether that breaks PR commenting. |
73
+ | S-54 | BACKLOG 139: shipped composite GitHub Actions put GH_TOKEN in the SAME step as the agent invocation (issue-to-pr) | .github/actions/issue-to-pr/action.yml, root action.yml | HIGH | the issue content is fetched in an earlier, separate step that does not hold the token; the agent-invoking step receives only a file, never a step with GH_TOKEN in its env | building@2026-09-27T15:59Z | NOTE: S-41 (merged 8a714c73) may have already partially addressed this exact split for .github/actions/issue-to-pr/action.yml -- builder must read S-41's actual merged diff first via `git show 8a714c73` and confirm what remains before starting, to avoid redundant or conflicting work. If S-41 already fully closed this, report back as a stale duplicate rather than editing. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 9c64533b: the fix was already in S-41 (8a714c73), so it adds only a step-level test (17 failures on pre-S-41 files, 0 now, 2 mutations red). HIGH reviewer dispatched 15:16. CONCERN: hand-listed action files; detection misses npx loki-mode@ver, path or variable calls and scripts. Rework sent 15:28. Rework 2b81483d: globbed actions, wider detector; it blanked tokens in 3 production steps (incl. Run Quality Review). HIGH re-review is checking whether that breaks PR commenting. Re-review CONCERN: the narrowed separator lost if/while/timeout/env/pipe/subshell detection (reproduced). The token blanking is fine (the 3 steps never used a token; no CEO decision needed). Rework sent 15:59. |
74
74
  | S-55 | Found by S-44's reviewer 1: `.github/workflows/test.yml`'s `Tests` workflow has no `paths-ignore`, so every docs-only `docs/v10/**` push (BOARD.md/BACKLOG.md/PROGRESS.md status bookkeeping) retriggers the full sharded suite and cancels any in-flight run via `cancel-in-progress` -- confirmed as the majority cause of this session's own repeated shard-2 "hangs" (self-inflicted, not a product bug) | .github/workflows/test.yml (paths-ignore only) | LOW | red: a docs/v10/BOARD.md-only push currently retriggers Tests and can cancel an in-flight run; green: a docs/v10/**-only push (and ideally other pure-doc paths) does not trigger Tests, verified by pushing a real docs-only commit and confirming no new Tests run starts | parked@2026-09-27T14:21Z | High value, low risk: directly stops the self-cancellation pattern this session hit repeatedly (including twice from the Captain's own BOARD.md bookkeeping pushes this turn). Do not broaden paths-ignore beyond doc-only globs -- must not accidentally skip Tests on a real code change that happens to touch a doc in the same commit (paths-ignore is all-or-nothing per push: verify no historical commit mixed docs/v10/** with code changes in the same push before landing, or scope the exclusion pattern more conservatively if any did). Built 0ec9c251, never merged. Parked: superseded by S-80 (main runs no longer cancel) and it conflicts with verdict reuse (S-84): a docs-only commit with no Tests run leaves a later release nothing to reuse. |
75
- | S-56 | BACKLOG 119 follow-through / BACKLOG 33: `cleanup_expired_rotating_keys` has no caller (the fix in cycle 4 correctly rejects an expired rotating key at validate_token time, but the cleanup function that would purge them from storage is dead code) | dashboard/api_keys.py (add a caller only, e.g. a periodic sweep or an on-validate-failure purge; do not touch dashboard/auth.py's already-fixed validate_token), its test | LOW | red: an expired rotating key stays in storage indefinitely (confirmed via a test that seeds one, advances time past rotation_expires_at, and checks it is still listed); green: cleanup_expired_rotating_keys is actually invoked on some real trigger and the key is purged | merged@2026-09-27T14:21Z | Read the BACKLOG 119 fix (dashboard/auth.py validate_token) first to avoid duplicating or conflicting with it -- this slice only wires up the already-written cleanup function, does not touch the security-relevant rejection logic. Also found and fixed a latent crash (bare datetime.fromisoformat instead of the fail-closed _deadline_passed helper validate_token already uses -- would have 500'd on any bad timestamp once wired live). Merged e3c27c6d, 32/32 reconfirmed on main. Correction: this row was stale at building@. Merged as e3c27c6d (ancestor of main, verified with git merge-base --is-ancestor), shipped in v9.56.0. |
75
+ | S-56 | BACKLOG 119 follow-through / BACKLOG 33: `cleanup_expired_rotating_keys` has no caller (the fix in cycle 4 correctly rejects an expired rotating key at validate_token time, but the cleanup function that would purge them from storage is dead code) | dashboard/api_keys.py (add a caller only, e.g. a periodic sweep or an on-validate-failure purge; do not touch dashboard/auth.py's already-fixed validate_token), its test | LOW | red: an expired rotating key stays in storage indefinitely (confirmed via a test that seeds one, advances time past rotation_expires_at, and checks it is still listed); green: cleanup_expired_rotating_keys is actually invoked on some real trigger and the key is purged | released@2026-09-27T15:53Z | Read the BACKLOG 119 fix (dashboard/auth.py validate_token) first to avoid duplicating or conflicting with it -- this slice only wires up the already-written cleanup function, does not touch the security-relevant rejection logic. Also found and fixed a latent crash (bare datetime.fromisoformat instead of the fail-closed _deadline_passed helper validate_token already uses -- would have 500'd on any bad timestamp once wired live). Merged e3c27c6d, 32/32 reconfirmed on main. Correction: this row was stale at building@. Merged as e3c27c6d (ancestor of main, verified with git merge-base --is-ancestor), shipped in v9.56.0. Correction: e3c27c6d is an ancestor of v9.56.0 (git log v9.56.0 shows it at 07:45), so it shipped in v9.56.0, not train 2. |
76
76
  | S-57 | BACKLOG 120: two dashboard-ui test files do not test the shipped code (`ui-components.test.js` asserts hand-copied stale logic and is never run by any test runner; `loki-overview-issue-journey.node.test.mjs` fails 0/5 and is registered nowhere) | dashboard-ui/tests/ui-components.test.js, dashboard-ui/tests/loki-overview-issue-journey.node.test.mjs, tests/run-all-tests.sh (registration only, coordinate: S-44 also touched this file -- rebase onto post-S-44-merge main first) | MEDIUM | ui-components.test.js imports and asserts against the REAL shipped functions (formatGateTime, summarizeGates, formatRunDuration), not hand-copied logic; the journey test is fixed (sets `_data.connected` per the known cause) and passes 5/5; both are registered in a real runner and actually execute in CI | released@2026-09-27T15:00Z | Merged 21a539df, 23/23 + 6/6 reconfirmed on main. Found the real drift: shipped formatGateTime/summarizeGates/formatRunDuration had all changed behavior since the hand-copied test was written; rewrote to dynamically import the real functions after a DOM stub (needed since the components extend LokiElement at class-definition time). Journey test's 6 (not 5) failing cases all traced to one root cause (_data.connected never set), fixed once in the shared mounted() helper. Both registered in run-all-tests.sh, additive alongside S-44's timeout wrapping. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
77
77
  | S-58 | BACKLOG 114 (scoped subset): false-empty-on-failed-fetch in the cockpit -- `cockpit/useCockpitState.ts` swallows git-status and checkpoint failures, so ChangeReview.tsx says "The working tree is clean" and RiskPanel.tsx says "No checkpoints were recorded" on a FAILED fetch, not a real empty result | cockpit/useCockpitState.ts (the git status / checkpoint fetch error handling only), web-app/src/components/ChangeReview.tsx, web-app/src/components/RiskPanel.tsx | MEDIUM | red: a failed git-status/checkpoint fetch (simulate a 500 or network error) renders the same copy as a genuine empty result; green: a failed fetch renders "Could not load..." distinctly from a genuine empty state, one test per component | released@2026-09-27T15:00Z | Merged 14d80a54, 22/22 (11 pre-existing S-46 + 11 new) reconfirmed on main, no conflict with S-46's earlier changes to the same file. Verified against the real server responses first (a genuine empty case returns HTTP 200, never a rejection) before implementing, confirming the fix's premise; a `settle()` helper now distinguishes resolved-empty from rejected. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
78
78
  | S-59 | BACKLOG 71: `evidence-gate-details` `tests.ok` stays true for an inconclusive test axis -- `surface_evidence_gate_details` prints `tests_ok=True` next to `tests_inconclusive` | autonomy/lib/proof-generator.py or wherever surface_evidence_gate_details lives (grep first), its test | LOW | red: an inconclusive test axis (zero-test or no-runner) still shows tests_ok=True in evidence-gate-details.json; green: tests_ok is false or absent when the axis is inconclusive, with a test | released@2026-09-27T15:00Z | Real bug, correctly located: `surface_evidence_gate_details` lives in autonomy/run.sh:13966 (not proof-generator.py, which is a different already-correct consumer). Reused the existing 'inconclusive dominates ok' convention already established in proof-generator.py's _classify_func_axis rather than inventing a new pattern. Merged 72afa3e9, 15/15 reconfirmed on main. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
@@ -83,36 +83,42 @@ set is declared to avoid overlap with those and with each other.
83
83
  | S-64 | BACKLOG 90: `LOKI_BRANCH_PROTECTION=false` on a leftover `loki/session-*` branch returns before any snapshot is taken, then `commit_session_changes` commits using the EARLIER session's stale snapshot, so a file the user made since is committed and lost from disk on checkout of the base | autonomy/run.sh (the branch-protection-off early-return path and commit_session_changes' snapshot-freshness check only), its test | HIGH | red: with LOKI_BRANCH_PROTECTION=false and a leftover loki/session-* branch, a file created by the user AFTER that branch was minted gets swept into the session commit and lost on a later base checkout; green: commit_session_changes refuses to commit (or re-snapshots) when no snapshot was taken THIS run | released@2026-09-27T13:18Z | STALE DUPLICATE, already fixed and released: commit d298ca60 (2026-09-26) already implements exactly this fix (the _LOKI_SNAPSHOT_THIS_RUN gate in commit_session_changes), confirmed an ancestor of origin/main. Builder verified thoroughly: ran the full 81/81 test-branch-lifecycle.sh suite (including the exact BACKLOG-90 regression test), mutation-tested the gate itself to confirm it's non-vacuous, swept for un-gated sibling commit sites (none found), and confirmed no Bun-route duplicate implementation exists. No code change needed. |
84
84
  | S-65 | BACKLOG 116 remainder: `dashboard/server.py`'s `gh pr view` (PR detail endpoint) doesn't request `statusCheckRollup` or several other fields S-47 already fixed the READER side for, so the detail-view status-checks section stays empty even after S-47's merge | dashboard/server.py (the gh pr view field-list only), its test | LOW | red: the PR detail endpoint's gh invocation omits statusCheckRollup from its --json field list, so the already-fixed frontend reader has nothing to read; green: the field list includes statusCheckRollup (and any other fields S-47's fix now expects), a real PR detail response includes it | released@2026-09-27T15:00Z | Merged 52b4dda0, reconfirmed on main. Builder correctly found the file set in the original ticket was wrong (dashboard/server.py has no PR routes; the real site is web-app/server.py's github_get_pr) and fixed there instead, disclosing the correction rather than silently working around it. Added statusCheckRollup + 4 other missing fields matching the sibling list endpoint's already-correct field set. Also flagged, not fixed (out of scope): 2 duplicate function definitions in web-app/server.py. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
85
85
  | S-66 | BACKLOG 116 remainder: `dashboard/server.py`'s PR list endpoint likely has the same missing-field gap as the detail endpoint (S-65) -- verify and fix if so | dashboard/server.py (the gh pr list field-list only, if distinct from the view endpoint), its test | LOW | red: the PR list gh invocation also omits fields S-47's reader fix expects (if confirmed distinct from S-65's site); green: fixed, with a test | released@2026-09-27T13:14Z | Investigated thoroughly, no fix needed: the PR list endpoint (github_list_prs) is a genuinely separate call site from S-65's fix target, but its field list already includes all 13 fields the frontend list view actually reads (enumerated against PRCard and the GitHubPR TypeScript interface directly). This gap never existed on the list side. Marked released since no code change was needed; nothing new to ship. |
86
- | S-67 | BACKLOG 88: old git (< 2.18) receipt attribution -- a failed mint snapshot deletes the list, so the receipt names every user untracked file as the run's work | autonomy/lib/workspace_diff.py or wherever the mint-snapshot failure path lives (grep first), its test | LOW | red: on a git < 2.18 (or simulated failure of the mint snapshot), the receipt attributes every pre-existing untracked file to the session's own work; green: an `ls-files --others --exclude-standard` fallback list is kept for the receipt even when the primary snapshot mechanism fails, names and counts only (the commit path still fails closed) | merged@2026-09-27T15:34Z | Check the git version floor already established elsewhere in this session's work (S-18's rework added a similar floor check for GIT_CONFIG_COUNT) for the established pattern to follow. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built ae200f0c. Reviewer dispatched 15:16. 1/1 APPROVE: red to green confirmed; -z handles spaces and newlines, mutation red. Cherry-picked 8f2d58b0. |
87
- | S-68 | BACKLOG 102: `.loki/state/agent-committed-user-files.z` has no reader outside the lifecycle test and is never cleared by a later session with no hits | autonomy/run.sh (the file's write site and lifecycle only), its test | LOW | red: the file persists indefinitely across sessions even when a later session finds no agent-committed user files; green: it's read and correctly cleared/rotated when appropriate, with a test proving the lifecycle | merged@2026-09-27T15:34Z | Low-risk housekeeping; read BACKLOG 108's reader requirement first (create_session_pr is supposed to read this file per that item) to confirm this isn't already partially addressed. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 963dcca5. Reviewer dispatched 15:16. 1/1 APPROVE. Cherry-picked f626cfb4. |
86
+ | S-67 | BACKLOG 88: old git (< 2.18) receipt attribution -- a failed mint snapshot deletes the list, so the receipt names every user untracked file as the run's work | autonomy/lib/workspace_diff.py or wherever the mint-snapshot failure path lives (grep first), its test | LOW | red: on a git < 2.18 (or simulated failure of the mint snapshot), the receipt attributes every pre-existing untracked file to the session's own work; green: an `ls-files --others --exclude-standard` fallback list is kept for the receipt even when the primary snapshot mechanism fails, names and counts only (the commit path still fails closed) | merged@2026-09-27T15:54Z | Check the git version floor already established elsewhere in this session's work (S-18's rework added a similar floor check for GIT_CONFIG_COUNT) for the established pattern to follow. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built ae200f0c. Reviewer dispatched 15:16. 1/1 APPROVE: red to green confirmed; -z handles spaces and newlines, mutation red. Cherry-picked 8f2d58b0. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
87
+ | S-68 | BACKLOG 102: `.loki/state/agent-committed-user-files.z` has no reader outside the lifecycle test and is never cleared by a later session with no hits | autonomy/run.sh (the file's write site and lifecycle only), its test | LOW | red: the file persists indefinitely across sessions even when a later session finds no agent-committed user files; green: it's read and correctly cleared/rotated when appropriate, with a test proving the lifecycle | merged@2026-09-27T15:54Z | Low-risk housekeeping; read BACKLOG 108's reader requirement first (create_session_pr is supposed to read this file per that item) to confirm this isn't already partially addressed. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 963dcca5. Reviewer dispatched 15:16. 1/1 APPROVE. Cherry-picked f626cfb4. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
88
88
  | S-69 | Founder directive D25 part 3: `.github/workflows/test.yml`'s `cancel-in-progress` must apply ONLY to pull_request events, never to a push to main -- this is the direct fix for the 10-of-12-cancelled-runs pattern this session repeatedly hit and disclosed piecemeal | .github/workflows/test.yml (the concurrency block only) | MEDIUM | red: a push to main while a prior main-triggered Tests run is still in progress currently cancels it; green: a push to main never cancels an in-progress main-triggered run, while two PRs against the same branch still correctly cancel each other's stale run; verified with one real push per the founder's own instruction | parked@2026-09-27T14:13Z | Founder-specified fix shape: `cancel-in-progress: ${{ github.event_name == 'pull_request' }}`. Verify the exact syntax is valid GitHub Actions expression syntax before landing, and that the concurrency GROUP key doesn't also need adjusting (a shared group across push and PR could still cause unwanted interaction even with cancel-in-progress scoped). Superseded by S-80 (CEO Part C item 2: per-SHA group key in all 3 gating workflows, not just cancel-in-progress in test.yml). Commit 8cfa1eae not merged. |
89
89
  | S-70 | Founder directive D25 part 4: measure real per-shard Tests durations, then rebalance/increase sharding (4 to 8, or whatever balances best) so a Tests run completes in under 15 minutes; record before/after numbers in METRICS.md | .github/workflows/test.yml (shard count/matrix), tests/run-all-tests.sh (shard-assignment logic if it needs updating for a new shard count), METRICS.md (new before/after entry) | MEDIUM | red: current Tests run takes 26-34 minutes per the founder's own measurement; green: a real Tests run on the new shard count completes under 15 minutes, with the actual before/after wall-clock numbers recorded in METRICS.md, not estimated | parked@2026-09-27T14:13Z | This is what raises the release-train ceiling toward 60/day per the founder's own framing -- get real per-shard timing data first (do not guess a shard count), and confirm the shard-index partition still covers 100% of the suite (index-based, as documented elsewhere in this repo's release notes) after rebalancing. Superseded by S-81 (CEO Part C item 4: duration-balanced 8 shards). Its measured timing data (c6000cb9) feeds S-81. |
90
- | S-71 | Founder directive D25 part 5: `scripts/v10-pulse.sh` blind spots -- add CI_CANCELLED_STREAK (3+ consecutive cancelled Tests runs on main), MERGED_UNRELEASED (45+ min even when main CI reads UNKNOWN), and fix main-CI-UNKNOWN currently suppressing other violations that should fire independently | scripts/v10-pulse.sh, tests/test-v10-pulse.sh (fixture tests for each new violation) | LOW | red: 3+ consecutive cancelled runs, or a 45+ min unreleased-merge age, currently produce no violation when main CI reads UNKNOWN; green: both fire correctly, and a fixture test proves neither is suppressed by an UNKNOWN main-CI reading, nor does it suppress any OTHER already-existing violation | merged@2026-09-27T14:58Z | Read scripts/v10-pulse.sh's current UNKNOWN-handling logic first to find exactly where/why it currently suppresses other checks before fixing -- this is likely one shared early-return or short-circuit, not three separate bugs. Built b8f43d08. 2 Tech Lead reviewers dispatched 14:09 (stacked with S-75). Reviewer 1/2 APPROVE (12 new tests fail pre-fix, 0 at 5c9a16f2). 2/2 APPROVE (both Tech Leads; mutation-tested). Merges alone at b8f43d08 in train 2; S-75 stacked on it is rejected separately. Train 2: cherry-picked onto main as 59118705. |
90
+ | S-71 | Founder directive D25 part 5: `scripts/v10-pulse.sh` blind spots -- add CI_CANCELLED_STREAK (3+ consecutive cancelled Tests runs on main), MERGED_UNRELEASED (45+ min even when main CI reads UNKNOWN), and fix main-CI-UNKNOWN currently suppressing other violations that should fire independently | scripts/v10-pulse.sh, tests/test-v10-pulse.sh (fixture tests for each new violation) | LOW | red: 3+ consecutive cancelled runs, or a 45+ min unreleased-merge age, currently produce no violation when main CI reads UNKNOWN; green: both fire correctly, and a fixture test proves neither is suppressed by an UNKNOWN main-CI reading, nor does it suppress any OTHER already-existing violation | merged@2026-09-27T15:54Z | Read scripts/v10-pulse.sh's current UNKNOWN-handling logic first to find exactly where/why it currently suppresses other checks before fixing -- this is likely one shared early-return or short-circuit, not three separate bugs. Built b8f43d08. 2 Tech Lead reviewers dispatched 14:09 (stacked with S-75). Reviewer 1/2 APPROVE (12 new tests fail pre-fix, 0 at 5c9a16f2). 2/2 APPROVE (both Tech Leads; mutation-tested). Merges alone at b8f43d08 in train 2; S-75 stacked on it is rejected separately. Train 2: cherry-picked onto main as 59118705. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
91
91
  | S-72 | Founder directive D25 part 6: `.claude/worktrees` holds 112 worktrees (~20GB) -- remove every worktree whose slice is merged/released/rejected/parked on BOARD.md after confirming its branch is fully merged (or its work is otherwise recorded), keep at most 10, and add a pulse violation for more than 15 worktrees | scripts/v10-pulse.sh (new violation only), a cleanup pass (not a code file -- direct git worktree operations, done carefully and verified per-worktree, not a blanket rm) | LOW | red: 112 worktrees exist today, no pulse violation fires above 15; green: worktree count is at or below 10 after a verified cleanup pass, and the new pulse violation fires correctly above 15 | released@2026-09-27T14:31Z | HIGH CARE REQUIRED: verify each worktree's branch tip is actually an ancestor of main (or its work is otherwise fully captured in a merged commit) via `git merge-base --is-ancestor` before removing it with `git worktree remove` -- never blanket-delete by directory listing alone, and never remove a worktree currently in active use by a running agent (cross-check against ListAgents' active subagents first). Cleanup done, no repo change: 64 worktrees removed with plain git worktree remove (no --force), 23G -> 11G (du -sh before/after), 30 merged branches deleted with branch -d. 71 remain; 10 not reachable safely: 27 hold commits not on main (git cherry + lines), 21 live or approved, 7 uncommitted edits, 4 created under 2h ago, 3 locked by this session. Pulse violation split to S-94. Classification: scratchpad/s72-classification.tsv. |
92
- | S-73 | Founder directive D26 guard 1: PreToolUse Bash guard hook (scripts/v10-guard.sh, wired via LOCAL-ONLY .claude/settings.local.json, never committed) blocking pkill/killall by name, git push --force/-f or reset --hard on main, git add -A/git add ., a BOARD.md commit dropping existing rows (the D18 incident), rm -rf outside safe roots, VERSION writes outside scripts/release.sh | scripts/v10-guard.sh (new), a fixture test file, .claude/settings.local.json (LOCAL ONLY -- must not be committed to the repo) | HIGH | red: each named dangerous pattern is currently allowed; green: each is blocked with exit 2 and a clear message, with one blocked-case and one allowed-case fixture per rule | merged@2026-09-27T15:34Z | CRITICAL: .claude/settings.local.json is local machine config, never a repo file -- if it's gitignored confirm that; if not, add it to .gitignore as part of this slice, and never stage or commit it. The guard script itself (scripts/v10-guard.sh) IS a normal repo file and goes through review. Read the actual PreToolUse hook JSON contract (stdin format, exit-code semantics) from Claude Code's own hook documentation before implementing -- do not guess the interface. Built and committed 47179bb3 (25/25 fixtures). 2 opus HIGH reviewers dispatched 14:09. REJECT (HIGH reviewer 2): 6 reproduced gaps (git add -A never implemented; commit -am and git rm of BOARD.md pass rule 3; git -C checks the wrong repo; --force-with-lease=; xargs kill). Rework dispatched 14:17 from 47179bb3. Rework eb534e96: all 12 gaps from both HIGH reviews addressed (timeout/nice/sudo wrappers, shell keywords, RULE6 git add -A, -am, git rm/mv, git -C resolution, --force-with-lease=, xargs/bash -c unwrapping, VERSION by path, rm -rf of a root, heredoc false block, fail-closed on crash). 78/78, 14 mutations killed. Back with both original HIGH reviewers 14:51. Round 3 sent 14:57. Reviewer 1 REJECT (heredoc strip deletes the rest of the opener line: git commit -F - <<EOF && git push --force returns 0; rc was 2 at 47179bb3) and reviewer 2 CONCERN (kill redirect tokens read as PIDs; $(cat f)/xargs < f not source-checked; if/while/until missing from keywords). Round 3 fixes hold, but both HIGH reviewers REJECT: an unterminated heredoc marker (here-string <<<, a grep for "<<EOF", arithmetic with a space, # comments, CRLF, EOF-X) drops the rest of the command. Round 4 sent 15:16. Round 4 75bfa51d: 2/2 HIGH APPROVE (both reran every round-3 repro; about 190 commands diffed, 0 regressions, 0 false blocks). Cherry-picked db51311a..e9170698. Installed as a local PreToolUse hook in .claude/settings.local.json at 15:33; smoke test: pkill -f, push --force, git add -A and echo > VERSION rc=2; git status and kill PID 2>/dev/null rc=0. Follow-ups (non-blocking): quote state carried across lines; exact terminator whitespace. |
93
- | S-74 | Founder directive D26 guard 2: no subagents for trivial orchestration (commit messages, git status/clean checks, BOARD.md row updates, version/CI checks) -- these go through the orchestrator directly or a new scripts/v10-ops.sh, never a dispatched Agent call | scripts/v10-ops.sh (new, subcommands for the named trivial operations), docs/v10/CONTROL.md or SWARM.md (a short binding rule addition, if line budget allows) | MEDIUM | red: this session dispatched full subagents that spent 22 minutes/229k tokens writing a commit message and 16 minutes/182k tokens checking a clean tree (cited by the founder as observed incidents); green: the same operations are one-line orchestrator actions or v10-ops.sh subcommand calls, no Agent dispatch | merged@2026-09-27T15:34Z | This is a PROCESS rule as much as a script -- the script gives the orchestrator a fast path, but the actual fix is behavioral (the Captain/PO/orchestrator role must stop dispatching subagents for these). Keep the script itself minimal: thin wrappers around git/gh one-liners, not a framework. Built and committed 94b8d3a6 (16/16 tests). 2 Tech Lead reviewers dispatched 14:09. CONCERN (reviewer 2): clean-check prints CLEAN and exits 0 when git status itself fails (reproduced). Rework dispatched 14:19 from 94b8d3a6. Round 2 d44a56d0: token allow-list, CRLF-preserving, atomic temp+replace, disk re-read with restore on mismatch (mutation-tested), clean-check exit 2 on git failure; 31/31. 2 re-reviewers dispatched 14:28. Re-review 1: all 6 prior blockers closed; 2 regressions from the atomic write (mode 644 -> 600, symlink replaced instead of written through). Round 3 sent 14:36. Round 3 e5dc3d28: re-reviewer 1 APPROVE; re-reviewer 2 CONCERN (fails closed): header-index lookup refuses the 30 real rows that have one fewer cell than their header. Round 4 sent 14:49 with a full-board sweep test. Round 4 9f8389b9: fallback cell locator for short or long rows, 79-row real-BOARD sweep, 48/48, mutation 34 red. Tech Lead re-review dispatched 14:56. Round 5 4ba139f6 (position rule). 1/1 APPROVE: independent 103-row sweep, 101 flips and 2 safe refusals (S-29, S-36), 0 wrong-cell writes; 52/52 under both shells. Cherry-picked 9ff5e41e..f1a26981. tests/test-v10-ops.sh 52/52 on main. |
94
- | S-75 | Founder directive D26 guard 3: per-role agent time budgets (LOW 15m, MEDIUM 30m, reviewer 30m, HIGH adversarial 60m, any shell command 10m under timeout, background long ones with a recorded PID) plus a new AGENT_OVER_BUDGET pulse violation | scripts/v10-pulse.sh (new violation), docs/v10/CONTROL.md or SWARM.md (the budget table itself, if line budget allows -- otherwise a new short reference doc), a fixture test | MEDIUM | red: an agent exceeding its tier's budget currently produces no violation and no automatic stop; green: AGENT_OVER_BUDGET fires when an agent's recorded runtime exceeds its tier's budget, with a fixture proving it (a synthetic over-budget record fires, an under-budget one does not) | merged@2026-09-27T14:58Z | Coordinate file-set with S-71 (already merged locally, held for train 2) which also touches scripts/v10-pulse.sh -- rebase onto that commit first to avoid a conflict, since S-71 already restructured how violations are detected and named. Built 5c9a16f2, stacked on S-71 (must merge with or after it). 45/45 tests. HIGH-builder budget set to 60 min by symmetry, needs CEO confirmation. Reviewed together with S-71. Reviewer 1/2 APPROVE (S-75 delta isolated: 7 tests fail on S-71-only script). REJECT (reviewer 2): a malformed building@ timestamp (e.g. 2026-99-99T99:99Z) crashes the whole pulse (unguarded parse_time_value -> PULSE ERROR, no status block). Rework dispatched 14:21. Rework 339cd6aa: parse_time_value returns None on an impossible date; bad rows report UNKNOWN in both loops; 48/48 under 3 shells; T22/T23 go red without the guard. Re-reviewer dispatched 14:27. Re-review REJECT: guard only catches a bad month (calendar.timegm), so T24:61, day 99 and Feb 30 pass as parsed (false green or fabricated 300480-min violation). Round 2 sent to the same engineer 14:35. Round 2 f285c138: full-tuple datetime validation, T25-T28 (8 assertions) red on round 1, 56/56 under 3 shells. Back with the original re-reviewer 14:40. APPROVED: re-reviewer re-ran all 5 malformed-timestamp repros on f285c138, all UNKNOWN, no fabricated violation; 8 new assertions red on round 1; other callers fall back safely. Reviewer 1 approved the unchanged code. Merge the stack b8f43d08 (S-71) + 5c9a16f2 + 339cd6aa + f285c138 in train 2. HIGH-builder budget still assumed 60 min pending CEO. Train 2: cherry-picked as c16d875f/ceacd964/03c78213. Live in the main checkout pulse (flagged S-87/S-91 at 14:56). |
95
- | S-76 | Founder directive D26 guards 4-5: evidence-or-it-did-not-happen (a BOARD.md/PROGRESS.md/commit claim of verified/green/fixed/confirmed/no-fix-needed must cite the exact command and its exit code or output line) plus the new UNEVIDENCED_CLAIM pulse check over the last 20 doc changes, plus backfilling docs/v10/GUARDS.md with the 6 named incidents (D14/D15 pkill, D18 BOARD.md-emptied, CI self-cancellation/D25, pulse UNKNOWN-suppression/S-71, the hung worktree classifier, over-budget trivial agents) | docs/v10/GUARDS.md (new file), scripts/v10-pulse.sh (UNEVIDENCED_CLAIM check), a fixture test | MEDIUM | red: none of the 6 named incidents has a GUARDS.md entry today, and no pulse check flags an evidence-free claim; green: all 6 incidents have a 4-field GUARDS.md entry (incident, root cause with evidence, the guard, the test proving it fires) and each incident's guard+test genuinely already exists (cite them, do not invent new ones just to close the entry), plus UNEVIDENCED_CLAIM fires on a fixture claim with no cited command/exit-code | review@2026-09-27T15:46Z | For each of the 6 incidents, the guard it names must ALREADY EXIST (S-71's pulse fix, D25's cancel-in-progress fix once S-69 lands, this slice's own new evidence check, the worktree-classifier lesson likely just needs a documented timeout+batch-approach guard rather than new code) -- do not fabricate a guard reference that doesn't correspond to real code. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 30ed0f19 (GUARDS.md + UNEVIDENCED_CLAIM). Reviewer dispatched 15:16. REJECT: GUARDS.md claims the S-16 mode guard is merged (254c3c85 is only on a side branch); the regex counts a bare "exit" or a date as evidence. Rework sent 15:28. Rework 17bb5573: S-16 marked PENDING after merge-base checks; stricter evidence regex. Re-review dispatched 15:40. |
96
- | S-77 | Founder directive D26 guard 6: small, fresh subagent context -- a slice card of at most 60 lines (goal, file set, Wall checks, commands to run, budget) instead of full conversation history when dispatching a builder/reviewer | docs/v10/SWARM.md or CONTROL.md (the slice-card template and the binding rule that the orchestrator dispatches from it, not from raw history) | LOW | the swarm's own dispatch documentation names a concrete 60-line-max slice-card template and states the orchestrator keeps only CONTROL.md/pulse-block/BOARD.md in its own live view, deferring everything else to on-demand file reads | review@2026-09-27T15:46Z | This is primarily a documentation/process slice (the Agent tool's own dispatch prompts already tend toward focused briefs in this session's practice, per BOARD.md's own file-set-per-slice convention) -- codify the existing good practice into a named template rather than inventing new mechanics. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built c437ea4a. Reviewer dispatched 15:16. CONCERN: test not registered in run-all-tests.sh. Rework sent 15:28. Rework 06648ac1: test registered. Re-review dispatched 15:40. |
97
- | S-78 | CEO Part B: docs/v10/OPERATING-MODEL.md (verbatim), CLAUDE.md and AGENTS.md trimmed to <=150 lines with the @ import first, the rest moved to docs/dev/*.md with a removal ledger | docs/v10/OPERATING-MODEL.md, CLAUDE.md, AGENTS.md, docs/dev/*.md | LOW | wc -l limits met; cmp of OPERATING-MODEL.md against the CEO text is 0; every removed rule listed with where it moved or why it was deleted | merged@2026-09-27T14:58Z | Engineer dispatched 14:09 (sonnet). Built 330bcafb: CLAUDE.md 756->150 lines, OPERATING-MODEL.md cmp-identical, 7 doc/regression suites pass. AGENTS.md is gitignored (local-only), so a 74-line draft waits for review before being applied to the main checkout; the live AGENTS.md still instructs pkill -f and rm -rf /tmp/loki-* (D14/D15 patterns). Reviewer dispatched 14:26. 1/1 on the commit: 45 MUST/NEVER rules traced to a home, 99 pointers resolve, no contradiction survives, cmp identical, 4 regression suites pass. The one blocker was in the uncommitted AGENTS.md draft (evidence rule relied on the @ import, unverified for non-Claude tools); fixed by adding it as literal text plus the harness invariants, then applied to the local-only AGENTS.md (542 -> 80 lines, live pkill -f and git add -A instructions gone; backup in TMPDIR). Commit 330bcafb ships in train 2. Train 2: e481bab5. CLAUDE.md conflict was only main's v9.56.0 version line; S-78 text taken (no test reads that line: grep of tests/, scripts/ and workflows found 0). |
98
- | S-79 | CEO Part C item 1: size the argmax fixture on Linux to max(4000, 2*131072/72) (MAX_ARG_STRLEN, not ARG_MAX), replace per-item subshells in autonomy/loki, drop the 2400s override, full-scale fixture to nightly | tests/test-ci-json-argmax.sh, autonomy/loki (two sites), tests/run-all-tests.sh (override only) | HIGH | at the parent of b842a332, on Linux, the smaller fixture still exits rc=126; passes on the fixed tree | approved@2026-09-27T15:49Z | Engineer dispatched 14:09 (sonnet). Saves 17-23 min per Tests run. Built and committed 81c52fcb: Linux argmax suite 383 ms (was about 22 min); required Wall check reproduced (b842a332^ = ef218d74: small fixture still exits 126; fixed tree passes 3/3). Full scale weekly in integrity-audit.yml. Override removal is in the same commit. 2 opus HIGH reviewers dispatched 14:49 (incl. 64K-page arm64 MAX_ARG_STRLEN check). Merge before or with S-81. Reviewer 2 APPROVE, reviewer 1 CONCERN (hardcoded 4K-page ceiling; the weekly run behind continue-on-error). Round 2 sent 15:12: restore the full-scale fixture (4-9s now). Round 2 28e6b605: full fixture restored everywhere, knob and workflow change reverted, fork removal kept; docker arm64: pre-fix exit 126 (326s), post-fix 3/3 in 7.1s. Re-review by reviewer 1 dispatched 15:40. Round 2 28e6b605: reviewer 1 APPROVE (pre-fix exit 126 at 329s; post-fix 3/3 in 4.3s, and 8.9s at ARG_MAX 4 MiB; diff vs c31cb1e8 touches only loki and run-all-tests.sh). Reviewer 2 approved round 1 and recommended exactly this change. 2/2. Ships with S-81 in train 3. |
99
- | S-80 | CEO Part C item 2: per-SHA concurrency group on push, cancel only on PRs, in test.yml, bun-parity.yml, security-audit.yml | the three concurrency blocks | MEDIUM | a push to main never cancels or supersedes a main run; PRs still cancel their own stale runs; verified by one real push | merged@2026-09-27T14:58Z | Built 3bd01787. 2 Tech Lead reviewers dispatched 14:12. Supersedes S-69. 2/2 APPROVE (Tech Leads). Follow-up, non-blocking: a weekly cron and a release push landing on the same SHA would queue, not cancel; strictly narrower than before. Ships in train 2; Release Manager verifies with one real push. Train 2: d5d6bfa7. The train 2 push is its one real-push verification. |
100
- | S-81 | CEO Part C item 4: 8 shards balanced by measured duration, checked-in duration table, 5 heavy suites in distinct shards | test.yml shell-tests matrix, run-all-tests.sh shard selection, tests/shard-durations table, tests/test-shard-coverage.sh, docs/v10/METRICS.md | MEDIUM | test-shard-coverage.sh proves every suite runs exactly once at n=8; projected max shard reported | approved@2026-09-27T14:46Z | Engineer dispatched 14:09 (sonnet). Supersedes S-70. Built dd08f821: duration table for all 496 suites (one CI run), greedy longest-first packing, LOKI_TEST_LIST mode, 17/17 coverage tests incl. n=8. Projected max shard 197s vs 451s today. Reviewers told to recompute the "all 8 exactly 197s" claim themselves. 2 dispatched 14:33. 2/2 APPROVE. Reviewer 1 recomputed the 8 shard totals from the table: 197s each, true; exactly-once at n=4 and n=8; deterministic across bash 5, bash 3.2 and mawk. The 197s assumes S-79 shrinks argmax (real today 1186s, which would sit alone in one shard at about 19.8 min); a second CI sample moves the max shard to 240s. MERGE ORDER: S-79 and S-81 in the same train, S-79 first. Follow-ups: no drift detector for the duration table; T4 recomputes a static count instead of driving the runner. |
101
- | S-82 | CEO Part C item 6: timeout-minutes 10 on shell, python, node, bun jobs in test.yml | test.yml job-level timeout-minutes only | LOW | every listed job has timeout-minutes 10 | approved@2026-09-27T14:18Z | Built 22e1bd4d. Reviewer dispatched 14:12. MERGE HELD until S-79 and S-81 land and a real run shows every shell shard under 10 min. 1/1 APPROVE. Data: shards 0 and 2 run up to 30 min today; python/bun/node max 5m21s. MERGE HELD until S-79 and S-81 land and a real run shows every shell shard under 10 min. |
102
- | S-83 | CEO Part C item 7: update-homebrew needs only [release] | release.yml update-homebrew needs line | LOW | job reads only needs.release outputs | approved@2026-09-27T14:14Z | Built 53251c1b in 40s. Reviewer dispatched 14:11. release.yml change ships in its own train (D22). 1/1 APPROVE (Tech Lead): only needs.release outputs used; Homebrew asset is the hash-verified GitHub Release tarball, so npm/Docker failures cannot break it. Advisory: channels may now differ in version briefly. Ships in its own train (D22). |
103
- | S-84 | CEO Part C items 3+5: release verdict reuse (required-ci accepts the parent's success when parent..release touches only version-bump files; select the completed successful run, not head -1 at :143; update the promise comment at :89-93) and drop needs: gate from required-ci plus gate's duplicate pytest | release.yml required-ci and gate jobs only | HIGH | a VERSION-only bump on a verified parent releases without a new Tests run; any non-allowlisted file in parent..release forces a full verdict; loki-ts/dist changes qualify only if the diff is version-string-only | building@2026-09-27T15:46Z | Loki-approved. release.yml: ships in its own train (D22). Built 6f71e7d1 (harness drove the real required-ci script: 5/5 cases; builder caught its own debugId substring bug). Only 2 of the last 8 release commits would qualify (the rest also touched docs/wiki/docs-v10; v9.56.0 dist had stale-build rename noise). Release process must therefore make the bump commit pure, on a dist-fresh verified parent. 2 opus HIGH reviewers dispatched 14:47. Own train (D22). HIGH reviewer 2 REJECT: only dist/loki.js is content-checked; package.json files[] drop, import-time code in mcp/__init__.py and a Dockerfile RUN line all released on the parent verdict even with the release SHA Tests red. Goal case (0.33s) and all 13 error paths fail closed are verified. Round 2 sent 14:53: version-only compare on every allowlisted file, and a red run at the release SHA always fails. HIGH reviewer 1 also REJECT with the same 2 findings plus loki.js.map; addendum sent with their 18-scenario harness. Round 2 c22503dd: every allowlisted file content-checked (CHANGELOG exempt), red run at release SHA fails, .map dropped, 4 wiki/docs files added; 18-scenario harness matches. Builder claims reuse rarely fires (non-deterministic loki.js); contradicts my earlier determinism check, so reviewers must measure it. 2 HIGH re-reviewers dispatched 15:12. Round 2: reviewer A REJECT (line splitting hides \r/U+2028 swaps; a Dockerfile USER line dropped, the image runs as root, confirmed by docker build), reviewer B CONCERN (cancelled counted as failure). Both measured the build as deterministic; reuse fails because of the map debugId plus a stale parent dist. Round 3 sent 15:14. Round 3 55abbebd+f14b2f6e: raw-byte compare, fixed decision order, map JSON compare, semver, --raw modes, CHANGELOG insert-only; 25/25; real b651b98d replay ELIGIBLE with a fresh parent dist. 2 HIGH re-reviewers dispatched 15:34. Round 3 verified by both HIGH reviewers (code), but the tests never drive the real poll loop: reordering branches keeps 25/25 green (reproduced). Round 4 (tests only, plus a leading-zero semver fix) sent 15:43. |
104
- | S-85 | CEO Part C item 8: Docker arm64 on a native ubuntu-24.04-arm runner instead of QEMU (release.yml:665-687); no gha cache in publish jobs (:54-56) | release.yml publish-docker job and cache config | MEDIUM | multi-arch image still published for amd64 and arm64; publish jobs restore no cache | approved@2026-09-27T14:35Z | release.yml: own train (D22). Different region from S-84 and S-83. Built and committed d8f98328 (native amd64/arm64 by digest plus manifest merge job, qemu removed, cache-binary false; provenance 11/11, SBOM 9/9, P9 4/4). 2 reviewers dispatched 14:29. Own train (D22). 2/2 APPROVE. Manifest-digest technique checked against a real multi-arch index (alpine:latest): sha256 of imagetools --raw equals the registry index digest. Missing arch fails closed (needs + count guard). Non-blocking: stale comment in publish-npm names publish-docker for the dist assert (now in publish-docker-build). Only a real release proves the arm64 runner and timing. |
105
- | S-86 | CEO Part C item 9: pytest -n auto (test.yml:73), all 4 Python versions still required; fix any order-dependent tests it exposes | test.yml python job, the tests it exposes | MEDIUM | python job wall time drops; 4 versions still required; no test skipped to get green | merged@2026-09-27T14:58Z | Built 0b82b63e: python suite 261.9s serial -> 79-82s with -n auto (14 cores, 3 runs, identical counts 3469/22); one racy test given a private TMPDIR, assertion unchanged. 2 reviewers dispatched 14:37 (asked to verify at -n 4, the CI core count). 2/2 APPROVE: assertion mutation-tested (still red when the tool leaks a scratch dir); no other shared-tempdir hazards; -n 4 x3 deterministic (109-121s on 14 cores). Follow-ups: local-ci/pre-push still serial; pytest-xdist unpinned (matches file convention). Train 2: 6c35aac0. |
106
- | S-87 | CEO Part C item 10a: remove built-in waits (review-tail stub blocks on a FIFO and drop the 4x scale at :388-390; mock vectors setup in test-e2e-features.sh:344; go-gate stub sleep 60 to 2s; test-dashboard-multiproject.sh:160 poll instead of sleep 25; pytest-gate fixture sleep 10 to 1) | the named test files only | MEDIUM | each named suite is faster, measured before and after; none weakened (mutation check per change) | merged@2026-09-27T15:34Z | OVER BUDGET 14:56 (33.5 min vs 30): told to stop and commit; the rest gets re-sliced. Committed 7935ed68 (worktree-agent-a6e52768add598d70) at 38 min, over budget: 4 of 5 waits cut (go-cargo 24.9s to 12.9s, pytest-gate 13.3s to 2.0s, multiproject 27.3s to 16.9s, e2e 26.6s to 22.2s; each mutation-checked). Remainder re-sliced as S-95. MEDIUM review (with a load-flake check) dispatched 15:05. 1/1 APPROVE: 4 mutations red; 12 runs under 4-way CPU load, 0 flakes. Cherry-picked 11c9df0c. |
92
+ | S-73 | Founder directive D26 guard 1: PreToolUse Bash guard hook (scripts/v10-guard.sh, wired via LOCAL-ONLY .claude/settings.local.json, never committed) blocking pkill/killall by name, git push --force/-f or reset --hard on main, git add -A/git add ., a BOARD.md commit dropping existing rows (the D18 incident), rm -rf outside safe roots, VERSION writes outside scripts/release.sh | scripts/v10-guard.sh (new), a fixture test file, .claude/settings.local.json (LOCAL ONLY -- must not be committed to the repo) | HIGH | red: each named dangerous pattern is currently allowed; green: each is blocked with exit 2 and a clear message, with one blocked-case and one allowed-case fixture per rule | merged@2026-09-27T15:54Z | CRITICAL: .claude/settings.local.json is local machine config, never a repo file -- if it's gitignored confirm that; if not, add it to .gitignore as part of this slice, and never stage or commit it. The guard script itself (scripts/v10-guard.sh) IS a normal repo file and goes through review. Read the actual PreToolUse hook JSON contract (stdin format, exit-code semantics) from Claude Code's own hook documentation before implementing -- do not guess the interface. Built and committed 47179bb3 (25/25 fixtures). 2 opus HIGH reviewers dispatched 14:09. REJECT (HIGH reviewer 2): 6 reproduced gaps (git add -A never implemented; commit -am and git rm of BOARD.md pass rule 3; git -C checks the wrong repo; --force-with-lease=; xargs kill). Rework dispatched 14:17 from 47179bb3. Rework eb534e96: all 12 gaps from both HIGH reviews addressed (timeout/nice/sudo wrappers, shell keywords, RULE6 git add -A, -am, git rm/mv, git -C resolution, --force-with-lease=, xargs/bash -c unwrapping, VERSION by path, rm -rf of a root, heredoc false block, fail-closed on crash). 78/78, 14 mutations killed. Back with both original HIGH reviewers 14:51. Round 3 sent 14:57. Reviewer 1 REJECT (heredoc strip deletes the rest of the opener line: git commit -F - <<EOF && git push --force returns 0; rc was 2 at 47179bb3) and reviewer 2 CONCERN (kill redirect tokens read as PIDs; $(cat f)/xargs < f not source-checked; if/while/until missing from keywords). Round 3 fixes hold, but both HIGH reviewers REJECT: an unterminated heredoc marker (here-string <<<, a grep for "<<EOF", arithmetic with a space, # comments, CRLF, EOF-X) drops the rest of the command. Round 4 sent 15:16. Round 4 75bfa51d: 2/2 HIGH APPROVE (both reran every round-3 repro; about 190 commands diffed, 0 regressions, 0 false blocks). Cherry-picked db51311a..e9170698. Installed as a local PreToolUse hook in .claude/settings.local.json at 15:33; smoke test: pkill -f, push --force, git add -A and echo > VERSION rc=2; git status and kill PID 2>/dev/null rc=0. Follow-ups (non-blocking): quote state carried across lines; exact terminator whitespace. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
93
+ | S-74 | Founder directive D26 guard 2: no subagents for trivial orchestration (commit messages, git status/clean checks, BOARD.md row updates, version/CI checks) -- these go through the orchestrator directly or a new scripts/v10-ops.sh, never a dispatched Agent call | scripts/v10-ops.sh (new, subcommands for the named trivial operations), docs/v10/CONTROL.md or SWARM.md (a short binding rule addition, if line budget allows) | MEDIUM | red: this session dispatched full subagents that spent 22 minutes/229k tokens writing a commit message and 16 minutes/182k tokens checking a clean tree (cited by the founder as observed incidents); green: the same operations are one-line orchestrator actions or v10-ops.sh subcommand calls, no Agent dispatch | merged@2026-09-27T15:54Z | This is a PROCESS rule as much as a script -- the script gives the orchestrator a fast path, but the actual fix is behavioral (the Captain/PO/orchestrator role must stop dispatching subagents for these). Keep the script itself minimal: thin wrappers around git/gh one-liners, not a framework. Built and committed 94b8d3a6 (16/16 tests). 2 Tech Lead reviewers dispatched 14:09. CONCERN (reviewer 2): clean-check prints CLEAN and exits 0 when git status itself fails (reproduced). Rework dispatched 14:19 from 94b8d3a6. Round 2 d44a56d0: token allow-list, CRLF-preserving, atomic temp+replace, disk re-read with restore on mismatch (mutation-tested), clean-check exit 2 on git failure; 31/31. 2 re-reviewers dispatched 14:28. Re-review 1: all 6 prior blockers closed; 2 regressions from the atomic write (mode 644 -> 600, symlink replaced instead of written through). Round 3 sent 14:36. Round 3 e5dc3d28: re-reviewer 1 APPROVE; re-reviewer 2 CONCERN (fails closed): header-index lookup refuses the 30 real rows that have one fewer cell than their header. Round 4 sent 14:49 with a full-board sweep test. Round 4 9f8389b9: fallback cell locator for short or long rows, 79-row real-BOARD sweep, 48/48, mutation 34 red. Tech Lead re-review dispatched 14:56. Round 5 4ba139f6 (position rule). 1/1 APPROVE: independent 103-row sweep, 101 flips and 2 safe refusals (S-29, S-36), 0 wrong-cell writes; 52/52 under both shells. Cherry-picked 9ff5e41e..f1a26981. tests/test-v10-ops.sh 52/52 on main. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
94
+ | S-75 | Founder directive D26 guard 3: per-role agent time budgets (LOW 15m, MEDIUM 30m, reviewer 30m, HIGH adversarial 60m, any shell command 10m under timeout, background long ones with a recorded PID) plus a new AGENT_OVER_BUDGET pulse violation | scripts/v10-pulse.sh (new violation), docs/v10/CONTROL.md or SWARM.md (the budget table itself, if line budget allows -- otherwise a new short reference doc), a fixture test | MEDIUM | red: an agent exceeding its tier's budget currently produces no violation and no automatic stop; green: AGENT_OVER_BUDGET fires when an agent's recorded runtime exceeds its tier's budget, with a fixture proving it (a synthetic over-budget record fires, an under-budget one does not) | merged@2026-09-27T15:54Z | Coordinate file-set with S-71 (already merged locally, held for train 2) which also touches scripts/v10-pulse.sh -- rebase onto that commit first to avoid a conflict, since S-71 already restructured how violations are detected and named. Built 5c9a16f2, stacked on S-71 (must merge with or after it). 45/45 tests. HIGH-builder budget set to 60 min by symmetry, needs CEO confirmation. Reviewed together with S-71. Reviewer 1/2 APPROVE (S-75 delta isolated: 7 tests fail on S-71-only script). REJECT (reviewer 2): a malformed building@ timestamp (e.g. 2026-99-99T99:99Z) crashes the whole pulse (unguarded parse_time_value -> PULSE ERROR, no status block). Rework dispatched 14:21. Rework 339cd6aa: parse_time_value returns None on an impossible date; bad rows report UNKNOWN in both loops; 48/48 under 3 shells; T22/T23 go red without the guard. Re-reviewer dispatched 14:27. Re-review REJECT: guard only catches a bad month (calendar.timegm), so T24:61, day 99 and Feb 30 pass as parsed (false green or fabricated 300480-min violation). Round 2 sent to the same engineer 14:35. Round 2 f285c138: full-tuple datetime validation, T25-T28 (8 assertions) red on round 1, 56/56 under 3 shells. Back with the original re-reviewer 14:40. APPROVED: re-reviewer re-ran all 5 malformed-timestamp repros on f285c138, all UNKNOWN, no fabricated violation; 8 new assertions red on round 1; other callers fall back safely. Reviewer 1 approved the unchanged code. Merge the stack b8f43d08 (S-71) + 5c9a16f2 + 339cd6aa + f285c138 in train 2. HIGH-builder budget still assumed 60 min pending CEO. Train 2: cherry-picked as c16d875f/ceacd964/03c78213. Live in the main checkout pulse (flagged S-87/S-91 at 14:56). In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
95
+ | S-76 | Founder directive D26 guards 4-5: evidence-or-it-did-not-happen (a BOARD.md/PROGRESS.md/commit claim of verified/green/fixed/confirmed/no-fix-needed must cite the exact command and its exit code or output line) plus the new UNEVIDENCED_CLAIM pulse check over the last 20 doc changes, plus backfilling docs/v10/GUARDS.md with the 6 named incidents (D14/D15 pkill, D18 BOARD.md-emptied, CI self-cancellation/D25, pulse UNKNOWN-suppression/S-71, the hung worktree classifier, over-budget trivial agents) | docs/v10/GUARDS.md (new file), scripts/v10-pulse.sh (UNEVIDENCED_CLAIM check), a fixture test | MEDIUM | red: none of the 6 named incidents has a GUARDS.md entry today, and no pulse check flags an evidence-free claim; green: all 6 incidents have a 4-field GUARDS.md entry (incident, root cause with evidence, the guard, the test proving it fires) and each incident's guard+test genuinely already exists (cite them, do not invent new ones just to close the entry), plus UNEVIDENCED_CLAIM fires on a fixture claim with no cited command/exit-code | building@2026-09-27T15:59Z | For each of the 6 incidents, the guard it names must ALREADY EXIST (S-71's pulse fix, D25's cancel-in-progress fix once S-69 lands, this slice's own new evidence check, the worktree-classifier lesson likely just needs a documented timeout+batch-approach guard rather than new code) -- do not fabricate a guard reference that doesn't correspond to real code. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 30ed0f19 (GUARDS.md + UNEVIDENCED_CLAIM). Reviewer dispatched 15:16. REJECT: GUARDS.md claims the S-16 mode guard is merged (254c3c85 is only on a side branch); the regex counts a bare "exit" or a date as evidence. Rework sent 15:28. Rework 17bb5573: S-16 marked PENDING after merge-base checks; stricter evidence regex. Re-review dispatched 15:40. Re-review REJECT: stale PENDING for cherry-picked S-74/S-73; branch conflicts with the merged pulse changes. Rework sent 15:59: rebase, judge by patch-equivalence, add today's 5 incidents. |
96
+ | S-77 | Founder directive D26 guard 6: small, fresh subagent context -- a slice card of at most 60 lines (goal, file set, Wall checks, commands to run, budget) instead of full conversation history when dispatching a builder/reviewer | docs/v10/SWARM.md or CONTROL.md (the slice-card template and the binding rule that the orchestrator dispatches from it, not from raw history) | LOW | the swarm's own dispatch documentation names a concrete 60-line-max slice-card template and states the orchestrator keeps only CONTROL.md/pulse-block/BOARD.md in its own live view, deferring everything else to on-demand file reads | merged@2026-09-27T16:01Z | This is primarily a documentation/process slice (the Agent tool's own dispatch prompts already tend toward focused briefs in this session's practice, per BOARD.md's own file-set-per-slice convention) -- codify the existing good practice into a named template rather than inventing new mechanics. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built c437ea4a. Reviewer dispatched 15:16. CONCERN: test not registered in run-all-tests.sh. Rework sent 15:28. Rework 06648ac1: test registered. Re-review dispatched 15:40. 1/1 APPROVE. Train 4. Cherry-picked onto main for train 4 (2 commits); D27 checks green except tests/test-select-tests.sh, which hit the 60s cap (rc=124; CI runs it in full; reviewer ran 38/38). |
97
+ | S-78 | CEO Part B: docs/v10/OPERATING-MODEL.md (verbatim), CLAUDE.md and AGENTS.md trimmed to <=150 lines with the @ import first, the rest moved to docs/dev/*.md with a removal ledger | docs/v10/OPERATING-MODEL.md, CLAUDE.md, AGENTS.md, docs/dev/*.md | LOW | wc -l limits met; cmp of OPERATING-MODEL.md against the CEO text is 0; every removed rule listed with where it moved or why it was deleted | merged@2026-09-27T15:54Z | Engineer dispatched 14:09 (sonnet). Built 330bcafb: CLAUDE.md 756->150 lines, OPERATING-MODEL.md cmp-identical, 7 doc/regression suites pass. AGENTS.md is gitignored (local-only), so a 74-line draft waits for review before being applied to the main checkout; the live AGENTS.md still instructs pkill -f and rm -rf /tmp/loki-* (D14/D15 patterns). Reviewer dispatched 14:26. 1/1 on the commit: 45 MUST/NEVER rules traced to a home, 99 pointers resolve, no contradiction survives, cmp identical, 4 regression suites pass. The one blocker was in the uncommitted AGENTS.md draft (evidence rule relied on the @ import, unverified for non-Claude tools); fixed by adding it as literal text plus the harness invariants, then applied to the local-only AGENTS.md (542 -> 80 lines, live pkill -f and git add -A instructions gone; backup in TMPDIR). Commit 330bcafb ships in train 2. Train 2: e481bab5. CLAUDE.md conflict was only main's v9.56.0 version line; S-78 text taken (no test reads that line: grep of tests/, scripts/ and workflows found 0). In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
98
+ | S-79 | CEO Part C item 1: size the argmax fixture on Linux to max(4000, 2*131072/72) (MAX_ARG_STRLEN, not ARG_MAX), replace per-item subshells in autonomy/loki, drop the 2400s override, full-scale fixture to nightly | tests/test-ci-json-argmax.sh, autonomy/loki (two sites), tests/run-all-tests.sh (override only) | HIGH | at the parent of b842a332, on Linux, the smaller fixture still exits rc=126; passes on the fixed tree | merged@2026-09-27T15:55Z | Engineer dispatched 14:09 (sonnet). Saves 17-23 min per Tests run. Built and committed 81c52fcb: Linux argmax suite 383 ms (was about 22 min); required Wall check reproduced (b842a332^ = ef218d74: small fixture still exits 126; fixed tree passes 3/3). Full scale weekly in integrity-audit.yml. Override removal is in the same commit. 2 opus HIGH reviewers dispatched 14:49 (incl. 64K-page arm64 MAX_ARG_STRLEN check). Merge before or with S-81. Reviewer 2 APPROVE, reviewer 1 CONCERN (hardcoded 4K-page ceiling; the weekly run behind continue-on-error). Round 2 sent 15:12: restore the full-scale fixture (4-9s now). Round 2 28e6b605: full fixture restored everywhere, knob and workflow change reverted, fork removal kept; docker arm64: pre-fix exit 126 (326s), post-fix 3/3 in 7.1s. Re-review by reviewer 1 dispatched 15:40. Round 2 28e6b605: reviewer 1 APPROVE (pre-fix exit 126 at 329s; post-fix 3/3 in 4.3s, and 8.9s at ARG_MAX 4 MiB; diff vs c31cb1e8 touches only loki and run-all-tests.sh). Reviewer 2 approved round 1 and recommended exactly this change. 2/2. Ships with S-81 in train 3. Train 3: caf4bc2f+469db2b4, release commit c2eccb21 (v9.58.0) pushed 15:53:26Z, ls-remote verified; released when publish-npm succeeds. |
99
+ | S-80 | CEO Part C item 2: per-SHA concurrency group on push, cancel only on PRs, in test.yml, bun-parity.yml, security-audit.yml | the three concurrency blocks | MEDIUM | a push to main never cancels or supersedes a main run; PRs still cancel their own stale runs; verified by one real push | merged@2026-09-27T15:54Z | Built 3bd01787. 2 Tech Lead reviewers dispatched 14:12. Supersedes S-69. 2/2 APPROVE (Tech Leads). Follow-up, non-blocking: a weekly cron and a release push landing on the same SHA would queue, not cancel; strictly narrower than before. Ships in train 2; Release Manager verifies with one real push. Train 2: d5d6bfa7. The train 2 push is its one real-push verification. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
100
+ | S-81 | CEO Part C item 4: 8 shards balanced by measured duration, checked-in duration table, 5 heavy suites in distinct shards | test.yml shell-tests matrix, run-all-tests.sh shard selection, tests/shard-durations table, tests/test-shard-coverage.sh, docs/v10/METRICS.md | MEDIUM | test-shard-coverage.sh proves every suite runs exactly once at n=8; projected max shard reported | merged@2026-09-27T15:55Z | Engineer dispatched 14:09 (sonnet). Supersedes S-70. Built dd08f821: duration table for all 496 suites (one CI run), greedy longest-first packing, LOKI_TEST_LIST mode, 17/17 coverage tests incl. n=8. Projected max shard 197s vs 451s today. Reviewers told to recompute the "all 8 exactly 197s" claim themselves. 2 dispatched 14:33. 2/2 APPROVE. Reviewer 1 recomputed the 8 shard totals from the table: 197s each, true; exactly-once at n=4 and n=8; deterministic across bash 5, bash 3.2 and mawk. The 197s assumes S-79 shrinks argmax (real today 1186s, which would sit alone in one shard at about 19.8 min); a second CI sample moves the max shard to 240s. MERGE ORDER: S-79 and S-81 in the same train, S-79 first. Follow-ups: no drift detector for the duration table; T4 recomputes a static count instead of driving the runner. Train 3: 46fdd376, release commit c2eccb21 (v9.58.0) pushed 15:53:26Z, ls-remote verified; released when publish-npm succeeds. |
101
+ | S-82 | CEO Part C item 6: timeout-minutes 10 on shell, python, node, bun jobs in test.yml | test.yml job-level timeout-minutes only | LOW | every listed job has timeout-minutes 10 | merged@2026-09-27T15:55Z | Built 22e1bd4d. Reviewer dispatched 14:12. MERGE HELD until S-79 and S-81 land and a real run shows every shell shard under 10 min. 1/1 APPROVE. Data: shards 0 and 2 run up to 30 min today; python/bun/node max 5m21s. MERGE HELD until S-79 and S-81 land and a real run shows every shell shard under 10 min. Train 3: babfd814 (conflict with S-81 comments resolved by keeping both), release commit c2eccb21 (v9.58.0) pushed 15:53:26Z, ls-remote verified; released when publish-npm succeeds. |
102
+ | S-83 | CEO Part C item 7: update-homebrew needs only [release] | release.yml update-homebrew needs line | LOW | job reads only needs.release outputs | merged@2026-09-27T15:55Z | Built 53251c1b in 40s. Reviewer dispatched 14:11. release.yml change ships in its own train (D22). 1/1 APPROVE (Tech Lead): only needs.release outputs used; Homebrew asset is the hash-verified GitHub Release tarball, so npm/Docker failures cannot break it. Advisory: channels may now differ in version briefly. Ships in its own train (D22). Train 3: 754d0d99, release commit c2eccb21 (v9.58.0) pushed 15:53:26Z, ls-remote verified; released when publish-npm succeeds. |
103
+ | S-84 | CEO Part C items 3+5: release verdict reuse (required-ci accepts the parent's success when parent..release touches only version-bump files; select the completed successful run, not head -1 at :143; update the promise comment at :89-93) and drop needs: gate from required-ci plus gate's duplicate pytest | release.yml required-ci and gate jobs only | HIGH | a VERSION-only bump on a verified parent releases without a new Tests run; any non-allowlisted file in parent..release forces a full verdict; loki-ts/dist changes qualify only if the diff is version-string-only | review@2026-09-27T15:53Z | Loki-approved. release.yml: ships in its own train (D22). Built 6f71e7d1 (harness drove the real required-ci script: 5/5 cases; builder caught its own debugId substring bug). Only 2 of the last 8 release commits would qualify (the rest also touched docs/wiki/docs-v10; v9.56.0 dist had stale-build rename noise). Release process must therefore make the bump commit pure, on a dist-fresh verified parent. 2 opus HIGH reviewers dispatched 14:47. Own train (D22). HIGH reviewer 2 REJECT: only dist/loki.js is content-checked; package.json files[] drop, import-time code in mcp/__init__.py and a Dockerfile RUN line all released on the parent verdict even with the release SHA Tests red. Goal case (0.33s) and all 13 error paths fail closed are verified. Round 2 sent 14:53: version-only compare on every allowlisted file, and a red run at the release SHA always fails. HIGH reviewer 1 also REJECT with the same 2 findings plus loki.js.map; addendum sent with their 18-scenario harness. Round 2 c22503dd: every allowlisted file content-checked (CHANGELOG exempt), red run at release SHA fails, .map dropped, 4 wiki/docs files added; 18-scenario harness matches. Builder claims reuse rarely fires (non-deterministic loki.js); contradicts my earlier determinism check, so reviewers must measure it. 2 HIGH re-reviewers dispatched 15:12. Round 2: reviewer A REJECT (line splitting hides \r/U+2028 swaps; a Dockerfile USER line dropped, the image runs as root, confirmed by docker build), reviewer B CONCERN (cancelled counted as failure). Both measured the build as deterministic; reuse fails because of the map debugId plus a stale parent dist. Round 3 sent 15:14. Round 3 55abbebd+f14b2f6e: raw-byte compare, fixed decision order, map JSON compare, semver, --raw modes, CHANGELOG insert-only; 25/25; real b651b98d replay ELIGIBLE with a fresh parent dist. 2 HIGH re-reviewers dispatched 15:34. Round 3 verified by both HIGH reviewers (code), but the tests never drive the real poll loop: reordering branches keeps 25/25 green (reproduced). Round 4 (tests only, plus a leading-zero semver fix) sent 15:43. Round 4 fdf36a33: tests drive the real poll loop (6 scenarios); both reviewer mutants now go red; one test each for map, --raw, NUL and trailer; semver rejects leading zeros; 28/28; b651b98d replay still ELIGIBLE. |
104
+ | S-85 | CEO Part C item 8: Docker arm64 on a native ubuntu-24.04-arm runner instead of QEMU (release.yml:665-687); no gha cache in publish jobs (:54-56) | release.yml publish-docker job and cache config | MEDIUM | multi-arch image still published for amd64 and arm64; publish jobs restore no cache | merged@2026-09-27T15:55Z | release.yml: own train (D22). Different region from S-84 and S-83. Built and committed d8f98328 (native amd64/arm64 by digest plus manifest merge job, qemu removed, cache-binary false; provenance 11/11, SBOM 9/9, P9 4/4). 2 reviewers dispatched 14:29. Own train (D22). 2/2 APPROVE. Manifest-digest technique checked against a real multi-arch index (alpine:latest): sha256 of imagetools --raw equals the registry index digest. Missing arch fails closed (needs + count guard). Non-blocking: stale comment in publish-npm names publish-docker for the dist assert (now in publish-docker-build). Only a real release proves the arm64 runner and timing. Train 3: 3a372c8e, release commit c2eccb21 (v9.58.0) pushed 15:53:26Z, ls-remote verified; released when publish-npm succeeds. |
105
+ | S-86 | CEO Part C item 9: pytest -n auto (test.yml:73), all 4 Python versions still required; fix any order-dependent tests it exposes | test.yml python job, the tests it exposes | MEDIUM | python job wall time drops; 4 versions still required; no test skipped to get green | merged@2026-09-27T15:54Z | Built 0b82b63e: python suite 261.9s serial -> 79-82s with -n auto (14 cores, 3 runs, identical counts 3469/22); one racy test given a private TMPDIR, assertion unchanged. 2 reviewers dispatched 14:37 (asked to verify at -n 4, the CI core count). 2/2 APPROVE: assertion mutation-tested (still red when the tool leaks a scratch dir); no other shared-tempdir hazards; -n 4 x3 deterministic (109-121s on 14 cores). Follow-ups: local-ci/pre-push still serial; pytest-xdist unpinned (matches file convention). Train 2: 6c35aac0. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
106
+ | S-87 | CEO Part C item 10a: remove built-in waits (review-tail stub blocks on a FIFO and drop the 4x scale at :388-390; mock vectors setup in test-e2e-features.sh:344; go-gate stub sleep 60 to 2s; test-dashboard-multiproject.sh:160 poll instead of sleep 25; pytest-gate fixture sleep 10 to 1) | the named test files only | MEDIUM | each named suite is faster, measured before and after; none weakened (mutation check per change) | merged@2026-09-27T15:54Z | OVER BUDGET 14:56 (33.5 min vs 30): told to stop and commit; the rest gets re-sliced. Committed 7935ed68 (worktree-agent-a6e52768add598d70) at 38 min, over budget: 4 of 5 waits cut (go-cargo 24.9s to 12.9s, pytest-gate 13.3s to 2.0s, multiproject 27.3s to 16.9s, e2e 26.6s to 22.2s; each mutation-checked). Remainder re-sliced as S-95. MEDIUM review (with a load-flake check) dispatched 15:05. 1/1 APPROVE: 4 mutations red; 12 runs under 4-way CPU load, 0 flakes. Cherry-picked 11c9df0c. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
107
107
  | S-88 | CEO Part C item 10b: parallelize moat P6, P9 and P5 internally | tests/moat/p6-*.sh, p9-rule-of-two.sh, p5-*.sh | MEDIUM | moat suite wall time drops; same cases, same verdicts | ready@2026-09-27T14:21Z | SEQUENCING: p9-rule-of-two.sh is in S-18's file set; start only after S-18 merges. |
108
- | S-89 | CEO Part C item 11: pip and bun install caches on read-only test jobs | test.yml cache steps only | LOW | cache hits on a second run; never on a job holding a write token or publish secret (P9) | review@2026-09-27T15:46Z | Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built e85a23d4. Reviewer dispatched 15:16. CONCERN: the fixed -A5 permissions window misses a late contents: write. Rework sent 15:28 (parse the YAML). Rework ab32c540: YAML-parsed permissions, 15/15. Re-review dispatched 15:40. |
109
- | S-90 | CEO Part C item 12: move bun=latest, macOS bun and hyperfine to nightly (test.yml:303-312, 390-400) | test.yml those jobs, a nightly workflow | LOW | those jobs no longer run on push; they run on a nightly schedule | merged@2026-09-27T14:58Z | Built 93482497 (bun matrix 4->1 on push; nightly.yml runs the other 3 plus hyperfine). Reviewer dispatched 14:24. 1/1 APPROVE. Non-blocking: release gate now requires only the pinned bun combo (intended); hyperfine no longer runs for the pinned combo anywhere; macOS-only branches in a few test files now run nightly only. Train 2: 329882d7. |
110
- | S-91 | CEO Part C item 13: Tier A diff-selected tests with selection rules R0-R7; anything touching tests/lib, run-all-tests.sh, package.json, requirements*, loki-ts/dist, VERSION or .github/workflows runs everything | a selection script, a Tier A workflow or job | MEDIUM | Tier A finishes in 2 min or less on a typical change; the run-everything triggers are honored; Tier A never replaces Tier B | review@2026-09-27T15:46Z | OVER BUDGET 14:56 (33.5 min vs 30): told to stop and commit; the rest gets re-sliced. Committed cb8ddb07 at 39 min (over budget): R0-R7 done, 29/29, P9 4/4 on tier-a.yml, real-commit runs 25s/86s/5s. Remainder re-sliced as S-96. Tech Lead review dispatched 15:08. REJECT: the .py-suffixed grep misses Python imports (workspace_diff.py, fast_verify.py moat miss); R5 targets tests/dashboard only; no dashboard-ui rule. Round 2 sent 15:23. Round 2 52a58e86: import needles, dashboard-ui rule, node match kind; 38/38. Re-review dispatched 15:46. |
108
+ | S-89 | CEO Part C item 11: pip and bun install caches on read-only test jobs | test.yml cache steps only | LOW | cache hits on a second run; never on a job holding a write token or publish secret (P9) | merged@2026-09-27T16:01Z | Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built e85a23d4. Reviewer dispatched 15:16. CONCERN: the fixed -A5 permissions window misses a late contents: write. Rework sent 15:28 (parse the YAML). Rework ab32c540: YAML-parsed permissions, 15/15. Re-review dispatched 15:40. 1/1 APPROVE. Train 4. Cherry-picked onto main for train 4 (2 commits); D27 checks green except tests/test-select-tests.sh, which hit the 60s cap (rc=124; CI runs it in full; reviewer ran 38/38). |
109
+ | S-90 | CEO Part C item 12: move bun=latest, macOS bun and hyperfine to nightly (test.yml:303-312, 390-400) | test.yml those jobs, a nightly workflow | LOW | those jobs no longer run on push; they run on a nightly schedule | merged@2026-09-27T15:54Z | Built 93482497 (bun matrix 4->1 on push; nightly.yml runs the other 3 plus hyperfine). Reviewer dispatched 14:24. 1/1 APPROVE. Non-blocking: release gate now requires only the pinned bun combo (intended); hyperfine no longer runs for the pinned combo anywhere; macOS-only branches in a few test files now run nightly only. Train 2: 329882d7. In train 2 release commit 5332bfc3 (v9.57.0), pushed 15:50:12Z (ls-remote verified); becomes released when publish-npm succeeds (D27). Correction 15:54: I had marked this released too early. |
110
+ | S-91 | CEO Part C item 13: Tier A diff-selected tests with selection rules R0-R7; anything touching tests/lib, run-all-tests.sh, package.json, requirements*, loki-ts/dist, VERSION or .github/workflows runs everything | a selection script, a Tier A workflow or job | MEDIUM | Tier A finishes in 2 min or less on a typical change; the run-everything triggers are honored; Tier A never replaces Tier B | merged@2026-09-27T16:01Z | OVER BUDGET 14:56 (33.5 min vs 30): told to stop and commit; the rest gets re-sliced. Committed cb8ddb07 at 39 min (over budget): R0-R7 done, 29/29, P9 4/4 on tier-a.yml, real-commit runs 25s/86s/5s. Remainder re-sliced as S-96. Tech Lead review dispatched 15:08. REJECT: the .py-suffixed grep misses Python imports (workspace_diff.py, fast_verify.py moat miss); R5 targets tests/dashboard only; no dashboard-ui rule. Round 2 sent 15:23. Round 2 52a58e86: import needles, dashboard-ui rule, node match kind; 38/38. Re-review dispatched 15:46. Round 2: 1/1 APPROVE (all 4 misses reproduced as fixed, 38/38, shellcheck clean). Advisory: generic stems (base.py) over-select (bloat, not a miss). Train 4. Cherry-picked onto main for train 4 (3ee19595+10090cd3); D27 checks green except tests/test-select-tests.sh, which hit the 60s cap (rc=124; CI runs it in full; reviewer ran 38/38). |
111
111
  | S-92 | CEO Part C item 14: tests/quarantine.txt (suite, owner, expiry max 7 days, issue); quarantined suites still run but do not block; moat and review suites can never be quarantined; no retry loops | tests/quarantine.txt, tests/run-all-tests.sh (quarantine handling) | LOW | a listed suite's failure is reported but non-blocking; an expired or moat entry is rejected | building@2026-09-27T15:49Z | SEQUENCING: after S-79 and S-81 merge (same runner file). Dispatched 15:49 on top of S-79+S-81. |
112
112
  | S-93 | CEO Part C item 15: the 38 unregistered test files: register each or delete it with a reason | tests/run-all-tests.sh registrations, the files | LOW | zero unregistered test files; each deletion has a stated reason | building@2026-09-27T15:49Z | SEQUENCING: after S-79 and S-81 merge (same runner file). Dispatched 15:49 on top of S-79+S-81. |
113
113
  | S-94 | Worktree follow-up: triage the 27 worktrees whose branches hold commits not on main (merge, record in BOARD, or discard with a reason), check the 3 with an uncommitted edit to tests/moat/p7-no-fabricated-data.sh, and add a pulse violation for more than 15 worktrees | scripts/v10-pulse.sh, tests/test-v10-pulse.sh (violation only); triage is a Captain decision per branch | LOW | worktree count at or below 10 with no unmerged work lost; the new violation fires above 15 with a fixture | review-blocked@2026-09-27T15:28Z | SEQUENCING: the pulse change waits for S-75 (same file). Triage list: scratchpad/s72-classification.tsv. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built ad60e018 (WORKTREE_COUNT violation). Reviewer dispatched 15:16; worktree triage still pending (orchestrator). CONCERN: git merge-tree with S-76 (30ed0f19) conflicts in both files (adjacent inserts). Rebase onto S-76 after its rework lands. |
114
114
  | S-95 | S-87 remainder (CEO Part C item 10): tests/test-review-assurance-tail.sh, which did not finish in 120s on its own (rc=124). Replace the DA-block poll with a FIFO wake, check for orphaned sleep 30/120 children, and drop the 4x LOKI_TEST_SHARD scale only if it is proven safe under 4-way contention | tests/test-review-assurance-tail.sh only | MEDIUM | the suite finishes under 60s alone and under 4-way load 3 times in a row; mutating the code under test still turns it red | building@2026-09-27T15:49Z | Known load-flaky (memory: review-assurance-tail). Measure before optimizing. Dispatched 15:49. |
115
115
  | S-96 | S-91 remainder: re-collect the last-10-main-code-commits Tier A selection report (time and selected tests per commit), add bun/pip/npm install caches to tier-a.yml, and set an explicit timeout-minutes on its R0 full-suite path | .github/workflows/tier-a.yml, docs/v10/METRICS.md | LOW | the METRICS.md table shows 10 real commits each under 120s or labeled R0; caches never on a job with write permissions or secrets (P9) | ready@2026-09-27T15:08Z | After S-91 merges. |
116
+ | S-97 | D27 item 2: .githooks/pre-push becomes identity check + bash -n + red-main warning only; remove the pytest run; target under 5s; update docs/dev/release-checklist.md to retire the local-CI mandate | .githooks/pre-push, its test, docs/dev/release-checklist.md | LOW | the hook finishes under 5s on this repo (timed 3x); a wrong identity and a bash -n error still block; no pytest invocation remains | review@2026-09-27T16:08Z | Train 3. Built b72e9ab7: hook runs in 0.78-0.89s (3 runs), 7/7, 4 mutations red. Review dispatched 16:07. |
117
+ | S-98 | D27 items 3 and 6: TRAIN_LATE pulse violation (more than 25 min since the last train push while merged-unreleased commits exist) and a push helper in scripts/v10-ops.sh that pushes without a pipe and succeeds only when git ls-remote equals HEAD | scripts/v10-pulse.sh, scripts/v10-ops.sh, their tests | LOW | fixtures: TRAIN_LATE fires at 26 min with an unreleased merge and not at 24 min or with none; the push helper returns non-zero when the remote does not match | review@2026-09-27T16:08Z | Train 3. Last train push time comes from git reflog or the push log, not the release tag. Built b44571ba: pulse 61/61, ops 60/60, mutations red. Review dispatched 16:07. |
118
+ | S-99 | S-73 follow-ups (non-blocking review findings): carry quote state across lines in strip_heredocs; exact terminator whitespace; a quoted ">" must not hide an rm target; sed -i, cp, tee, perl -pi or python open() writes to BOARD.md chained before a commit set the pending flag | scripts/v10-guard.sh, tests/test-v10-guard.sh | MEDIUM | each reviewer repro becomes a blocked fixture; no everyday command becomes blocked | ready@2026-09-27T15:53Z | Repros in the S-73 round 3 and 4 reviews. |
119
+ | S-100 | S-18 follow-up (reviewer A F1): record the origin URL before the first agent iteration and refuse the trusted push if it changed; also refuse the repo default branch, not just main and master | autonomy/run.sh (trusted push only), a test | HIGH | an agent that repoints origin to another github.com repo gets a refused push with a visible warning | ready@2026-09-27T15:53Z | After S-18 merges. |
120
+ | S-101 | D27 follow-up: scripts/release.sh --bump-only (no prompt, no commit, no push) that bumps every checklist file and rebuilds loki-ts/dist, so the Release Manager no longer edits VERSION with the editor tool | scripts/release.sh, tests/test-release-sh.sh | LOW | --bump-only bumps all 13 files and the dist version and exits without git side effects (asserted via git status and git log in a temp repo) | ready@2026-09-27T16:01Z | Ends the D27 VERSION exception. |
121
+ | S-102 | P0 fix-forward: tests/dashboard/test_build_supervisor.py fails intermittently under pytest -n auto (S-86) with "provider attempt lineage marker is unavailable" (rc 127), raised at autonomy/lib/deadline.py:415. Hit Python 3.11+3.12 on 89e350bd and 3.10+3.11 on c2eccb21 (train 3 Tests run 36331204715, so required-ci failed); identical code passed on 5332bfc3 | autonomy/lib/deadline.py and/or tests/dashboard/test_build_supervisor.py | HIGH | reproduced in Linux docker with -n 4 before the fix; 0 failures over at least 3x the runs after; no assertion loosened, no retry | building@2026-09-27T16:06Z | Dispatched 16:06 (opus). Train 3 failed jobs rerun at 16:05:48Z. |
116
122
  | S-01 | BACKLOG 22: orphaned `sleep 300` resource monitor | autonomy/run.sh (resource-monitor spawn/cleanup only, not kill_provider_child), one new test | MEDIUM | red: `loki start` then interrupt leaves a `sleep 300` orphan (ps shows it after exit); green: it is reaped or never orphaned, by PID recorded at spawn | released@2026-09-27T01:33Z | 027ec085 confirmed ancestor of origin/main; note corrected, was stale. |
117
123
  | S-02 | BACKLOG 23: client-route test hardcodes main checkout path | tests/lib-extract-client-paths.mjs, tests/lib-match-client-routes.py, tests/test-verify-client-routes.sh | LOW | released@2026-09-27T15:00Z | 1/1 APPROVE: reviewer reproduced from 2 independent real worktrees, mutation-tested both new WORKTREE GUARD assertions independently, confirmed the disclosed BACKLOG 137 interaction (P7's own sed-workaround breaking) is expected, not a surprise. **Merged to main.** S-39/BACKLOG 137 now unblocked -- but note: this merge makes `tests/moat/run.sh` read a live FAIL on `P7.webapp-client-routes-exist` until S-39 lands (an unlisted-in-pending.txt regression per the ratchet), since P7's own copy-and-sed workaround can no longer find the literal it targets. S-39 should be prioritized promptly to close this gap, or a temporary pending.txt entry considered if S-39 doesn't land quickly. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
118
124
  | S-03 | BACKLOG 25: `loki ci --report` "Argument list too long" on a large diff | autonomy/loki (cmd_ci report path only), one new test | MEDIUM | red: a synthetic diff over the ARG_MAX threshold, exit 126; green: same diff passed via stdin/file, exit 0 | rejected@2026-09-27T01:33Z | Real crash site (export-based ARG_MAX) misdiagnosed and left unfixed; sent back to builder. |
@@ -128,9 +134,9 @@ set is declared to avoid overlap with those and with each other.
128
134
  | S-13 | BACKLOG 48: same-class D7 hardening -- `loki outcomes canary verify`, `proof share`/`show` heredocs, empty PATH component | autonomy/loki (the specific inline python3 sites named), its test | MEDIUM | released@2026-09-27T15:00Z | Merged bb99e675, pushed; quorum topped up 2/2 APPROVE. RED leg pins a literal parent SHA (reachability-checked, SKIP on a shallow clone) instead of HEAD, confirmed to survive being merged. `loki_verify_tool_path()` rejects any non-absolute PATH entry. Quorum reviewer confirmed all 5 hardened call sites fail closed (hard error), never silently fall back to a bare unscoped python3. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
129
135
  | S-14 | BACKLOG 49: verifier stdout still says `ok: true` on ABSENT/NOT CHECKED while exit is 1/2 | autonomy/loki (`cmd_proof verify)` case), its test | MEDIUM | released@2026-09-27T15:00Z | `TAMPERED if attestation=="FAILED" or hash_ok is False`; both JSON and `--human` share one verdict computation. Unanimous 2/2 APPROVE. Reviewer 2 independently isolated the hash-only-corrupted case (a scenario reviewer 1's body-mutation approach couldn't architecturally produce, since receipt_sha256 and verification.hash are the same canonical digest by design) and confirmed the `or`'s hash_ok disjunct correctly fires TAMPERED there too. Both reviewers confirmed the `--human` residual gap is real but non-blocking (exit code and stderr both correct; a pure-stdout consumer sees a weaker finding). f8deb64a. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
130
136
  | S-15 | BACKLOG 50: remote NOT CHECKED returns 0, local returns 2 -- align or document | the remote verifier path, docs/exit-codes.md | LOW | either the remote exit is changed to 2 with a test proving it, or docs/exit-codes.md documents the divergence explicitly with the reason; pick one and record which in the commit message | released@2026-09-27T15:00Z | Main since 34198ff4. Round 1 found the first draft factually wrong (missed the nopubkey unconditional-exit-1 override), corrected and re-approved. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
131
- | S-16 | BACKLOG 22 follow-on / housekeeping: `scripts/release.sh` only bumps 3 of the 14 files CLAUDE.md's release checklist requires (VERSION, package.json, vscode-extension/package.json) | scripts/release.sh | MEDIUM | review@2026-09-27T15:46Z | Rebased to ed565709. Fixed the subshell-scoping bug (`--dry-run`/`--help` silently broken) and switched to anchored per-format sed slots for drift-safety. 2/2 reviewers ran independently: reviewer 1 (Opus) APPROVE, one non-blocking permissions note. Reviewer 2 (Sonnet) CONCERN, blocking: `apply_sed()`'s mktemp+mv silently narrows all 14 touched files from 644 to 600 on every release (mktemp always creates 600, mv keeps the temp file's mode, not the original's) -- confirmed live on a real bump run. Fix (`cp -p` after mktemp, before the sed redirect) dispatched as rework; full verification suite (subshell fix, drift-detection, idempotency, fail-closed assert, plus the new permissions check) to be re-run before a fresh quorum. Correction: stale building@ with no live agent and no commit of its own (git log --all --grep found none). Re-cut to ready. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 9bf1ddda (test-release-sh.sh). Reviewer dispatched 15:16 to verify the mode-preservation fix. CONCERN: tests/test-release-sh.sh is never invoked. Rework sent 15:28 (register it). Rework 12f2d681: test registered (mode fix 59fcbaa3 is in the branch). Re-review dispatched 15:40. |
137
+ | S-16 | BACKLOG 22 follow-on / housekeeping: `scripts/release.sh` only bumps 3 of the 14 files CLAUDE.md's release checklist requires (VERSION, package.json, vscode-extension/package.json) | scripts/release.sh | MEDIUM | merged@2026-09-27T16:01Z | Rebased to ed565709. Fixed the subshell-scoping bug (`--dry-run`/`--help` silently broken) and switched to anchored per-format sed slots for drift-safety. 2/2 reviewers ran independently: reviewer 1 (Opus) APPROVE, one non-blocking permissions note. Reviewer 2 (Sonnet) CONCERN, blocking: `apply_sed()`'s mktemp+mv silently narrows all 14 touched files from 644 to 600 on every release (mktemp always creates 600, mv keeps the temp file's mode, not the original's) -- confirmed live on a real bump run. Fix (`cp -p` after mktemp, before the sed redirect) dispatched as rework; full verification suite (subshell fix, drift-detection, idempotency, fail-closed assert, plus the new permissions check) to be re-run before a fresh quorum. Correction: stale building@ with no live agent and no commit of its own (git log --all --grep found none). Re-cut to ready. Dispatched 15:00 (workflow wf_29a75a9c-652, slice-card brief). Built 9bf1ddda (test-release-sh.sh). Reviewer dispatched 15:16 to verify the mode-preservation fix. CONCERN: tests/test-release-sh.sh is never invoked. Rework sent 15:28 (register it). Rework 12f2d681: test registered (mode fix 59fcbaa3 is in the branch). Re-review dispatched 15:40. 1/1 APPROVE (test registered; mode fix 59fcbaa3 in the branch). Train 4: this ends the D27 VERSION editor-tool exception. Cherry-picked onto main for train 4 (4 commits); D27 checks green except tests/test-select-tests.sh, which hit the 60s cap (rc=124; CI runs it in full; reviewer ran 38/38). |
132
138
  | S-17 | BACKLOG 43: council/runner test-result readers (`completion-council.sh`'s 4 `python3 -c` sites) can be shadowed by a committed `hashlib.py`/`json.py` in the agent's own repo (cwd module shadowing, honest-verdict, HIGH) | autonomy/completion-council.sh (the 4 named `python3 -c` test-results readers only, not the S-07 vote site), its test | HIGH | released@2026-09-27T15:00Z | Rebased onto current main in worktree-agent-a5422f6f162d3dd2f (now at 23025a09), NOT yet merged -- HIGH tier requires 3+1 unanimous APPROVE (D12/D13) before merge, which has not run against this diff. Found the CODE fix already shipped in 3bc40efb (all 43 inline python3 sites in this file, including all 4 named readers, already have the D7 pattern) -- this slice is test-coverage-only, no production code changed. Extended tests/moat/p2-honest-verdict.sh's existing P2.council-readers-not-shadowed case to drive the 3 previously-untested readers (council_heuristic_review, council_evaluate_member, council_devils_advocate_review) via mutation-cycle verification (stripped each site's filter, confirmed shadow fired/verdict flipped, restored, reconfirmed clean). No cases.txt/pending.txt change (no new case ID). Solo-verified (not a substitute for quorum): moat matches main (3 of 9, no rule failed), shellcheck clean (one pre-existing unrelated SC2034 warning). Full 4-reviewer HIGH-tier quorum: 3/4 APPROVE, 1/4 CONCERN -- NOT unanimous, rework needed. Reviewer 3 (Opus) found a real, more severe, OUT-OF-SCOPE bypass in the SAME file's reader hardening (bucket-(b) per D21, not a veto on this test-coverage-only slice): the readers' `python3 -E -c` calls have no `-I`/`-S`/fixed-path resolution, lighter than BACKLOG 129's equivalent fix; reproduced live via a planted `.pth` flipping a genuinely-failing verdict to CONFIRMED_COMPLETE. Filed as BACKLOG 134. Reviewer 4 (Sonnet) CONCERN, blocking, reproduced live: the 3 new call sites (`tests/moat/p2-honest-verdict.sh:802-804`) discard rc/stdout and rely only on a shared marker file staying empty -- a typo'd function name (verified: renaming `council_heuristic_review` to a nonexistent name) still reports PASS, since a nonexistent-function call never touches the marker either. Fix: assert rc/vote-text differs between the fail and green legs for each of the 3 new calls (reviewer already confirmed each site's distinct fail/green vote text: council_heuristic_review -> VOTE:REJECT/VOTE:APPROVE; council_evaluate_member -> CONTINUE.../COMPLETE...; council_devils_advocate_review -> OVERRIDE_CONTINUE/CONFIRMED_COMPLETE). Same reviewer also found a 6th reader, `council_managed_should_stop` (already D7-hardened, gated behind 3 default-false experimental flags, not currently exploitable) not covered by this case -- filed as BACKLOG 135, non-blocking follow-up, not part of this rework. Rework commit 4df70b2d (rebased): `council_call_args` now captures and asserts each call's actual vote-text output against the pattern the file's pre-existing 2-reader loop already uses, matching each site's real fail-leg/green-leg strings verified against completion-council.sh source directly. Also fixed a subtler fixture-contamination bug found during rework: `council_heuristic_review`'s original fixture file independently drove REJECT via a separate grep check regardless of whether the D7-guarded reader even ran; switched to a neutral empty file so the assertion depends only on the reader under test. Typo-reproduction re-verified for all 3 sites (before: silent PASS; after: loud FAIL naming the broken site). Full mutation cycle re-verified. Fresh 4-reviewer HIGH-tier quorum: reviewer 1/4 (Opus) APPROVE, independently confirmed the typo test both ways (renamed the test's call AND separately renamed the production function), verified every expected vote string against completion-council.sh source directly, independently reproduced that the neutral-fixture fix is genuinely necessary (the original evidence file's own text content drives REJECT via the role's own grep check, masking a stripped D7 filter at the heuristic reader specifically), and ran the full mutation cycle per-site and combined. No blocking findings. Reviewer 3/4 (Opus) also APPROVE: confirmed the vote-text comparison is exact-match (not a fooled-by-prefix risk), found one genuine limit (the devil's-advocate result-WRITER site at completion-council.sh:4058 is uncaptured stdout, so a decoy vote there could theoretically slip past the vote-text check alone -- but confirmed via its own mutation that the marker check independently catches this case, proving the two checks are complementary, not redundant), reproduced the typo test on 2 of 3 sites, and confirmed the case's own claims stay within what it actually tests (does not overclaim coverage of the separate BACKLOG 134 gap). Flagged (and I confirmed clean, transient) a worktree hygiene note: other review agents' own scratch scripts briefly appeared in this shared worktree's tests/moat/ directory during concurrent review, causing a spurious DUPLICATE SCRIPT moat failure for one reviewer -- verified via git status that nothing persisted or got tracked. Reviewer 4/4 (Sonnet, the original round-1 CONCERN-finder re-checking their own finding) also APPROVE: reproduced the original bug fresh against the pre-rework file for all 3 functions (confirmed the gap was not limited to just one), confirmed all 3 typos now fail loudly against the reworked file, and went further than the original ask by testing a stronger variant not explicitly claimed by the commit -- swapping one function's definition to occupy ANOTHER's name (wrong-function-called-instead-of-missing) -- and confirmed the assertion catches that too, since all 3 sites' vote vocabularies are pairwise distinct. Reviewer 2/4 (Sonnet) also APPROVE: confirmed the 6th-reader scope from BACKLOG 135 still holds and is unchanged, found a real but genuinely out-of-scope bypass (a stub function redefined inside the test harness's own subshell can fool the vote-text check -- requires editing the test itself, not achievable via the actual PYTHONPATH-shadow threat model this test defends against), and caught its own FIRST mutation-cycle attempt was too weak (only proved the marker fired, not the vote-text check specifically) -- consulted the advisor, redid it per-site, and confirmed all 3 vote-text brackets genuinely fire on their own, not just the marker. **Unanimous 4/4 APPROVE. Merged to main (410046ce).** Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
133
- | S-18 | BACKLOG 149 (escalated from BACKLOG 44, real production gap not just test-coverage): the GH CLI's own `hosts.yml` credential file is completely unprotected by Rule-of-Two's env-var withhold -- an injected prompt can just run real `gh`, which resolves credentials from `$HOME` regardless of the withheld env vars | autonomy/run.sh (the withhold call site), loki-ts/src/runner/github_token.ts (Bun equivalent), tests/moat/p9-rule-of-two.sh | HIGH | review@2026-09-27T15:40Z | **ROUND 2 REJECT (new reviewer), a different and more severe bypass than round 1**: round 1's fix (env sentinel + GH_CONFIG_DIR + git credential.helper reset via GIT_CONFIG_COUNT/KEY/VALUE) correctly closes both round-1-confirmed bypasses -- verified independently by BOTH round-2 reviewers (one APPROVE, one REJECT, but the REJECT's own verification of the round-1 fixes is itself a clean pass). The REJECT is for a THIRD, previously undisclosed and unhandled vector: **SSH transport is completely unhandled**. Every round-1 mechanism (token sentinel, GH_CONFIG_DIR, credential.helper reset) covers only HTTPS-based git/gh credential resolution -- none touch SSH_AUTH_SOCK, GIT_SSH_COMMAND, core.sshCommand, or ~/.ssh. Reproduced live, read-only: `ssh-add -l` exits 0 (the provider session's live, untouched $HOME/env always has a reachable SSH agent by the fix's own design), and `ssh -T git@github.com` completes full auth as the real account, completely unaffected by every round-1 mitigation. An agent can simply `git remote set-url origin git@github.com:<owner>/<repo>.git && git push` and authenticate exactly as an unrestricted session -- simpler than the already-disclosed `gh auth token -u <username>` gap (no username lookup, no keychain tooling, just the operator's normal always-on ssh-agent). The P9 test's own PASS assertion text is unqualified and false for this case. Fix path per the reviewer: extend the existing capture/apply/restore/depth-counter architecture (already correct for the HTTPS vars) to also sentinel SSH_AUTH_SOCK and override GIT_SSH_COMMAND/core.sshCommand for the withheld session -- a bounded addition to an already-sound design, not a redesign. Two secondary non-blocking findings folded into the rework: (1) `env -u GH_TOKEN -u GITHUB_TOKEN gh auth token` is an undisclosed sibling of the already-disclosed username-lookup gap, needs naming; (2) the Bun/TS route has no git-version floor check the bash route already added, silently no-oping the credential.helper reset on git < 2.31 with no operator signal. Per the founder's D25 directive, this does NOT block the current release train -- the vulnerability this slice fixes (both round-1 and this newly-found SSH gap) is already live in published v9.55.0 either way; S-18 ships in its own train the moment its review reaches unanimous APPROVE. Rework dispatched. Round 3 built 3af8cdac (stack 6e38257c, 94f8b26a, b8c0eccf): SSH_AUTH_SOCK unset and GIT_SSH_COMMAND=false in the withheld window with a trusted re-grant, Bun git-floor warning, P9 SSH tier mutation-tested. Disclosed residual: direct ssh -i <key>. 2 opus HIGH reviewers dispatched 14:26. Ships in its own train once unanimous (D25). Round 3 HIGH reviewer 1: SSH blocker CLOSED (synthetic agent), re-grant/depth/login shells verified; REJECT for a new reproduced Bun-route bypass: 5 bare git spawns (council.ts:178,:190,:401, prd_reuse.ts:161-175) inherit the startup env with real GH_TOKEN/SSH_AUTH_SOCK, so an agent core.fsmonitor script runs with operator creds. Round 4 sent 14:43. Round 4 9cb330cb: Bun start-env spawn leak, nested run fails closed, SSH re-grant test. 2 HIGH reviewers dispatched 15:05. Round 4 closed all round-3 findings, but both HIGH reviewers REJECT: the trusted push in on_run_complete (default on) and create_session_pr runs an agent-planted pre-push hook or core.sshCommand with every withheld channel restored (reproduced with synthetic creds). Round 5 sent 15:16: push from a Loki-owned temp repo. CEO decision pending: a Loki-spawned nested run fails closed and cannot push. Round 5 7d5b7fb4: the push goes from a fresh private repo (origin validated as github.com), gh runs from / with GH_REPO; new 38-check test and P9 planted scenario with a positive control; mutation red. Incidents during the build: a glob rm in the shared scratchpad root and 4 synthetic insteadOf lines written to the real ~/.gitconfig (removed; grep of ~/.gitconfig for loki-test/insteadOf found none at 15:40). 2 HIGH reviewers dispatched 15:40. |
139
+ | S-18 | BACKLOG 149 (escalated from BACKLOG 44, real production gap not just test-coverage): the GH CLI's own `hosts.yml` credential file is completely unprotected by Rule-of-Two's env-var withhold -- an injected prompt can just run real `gh`, which resolves credentials from `$HOME` regardless of the withheld env vars | autonomy/run.sh (the withhold call site), loki-ts/src/runner/github_token.ts (Bun equivalent), tests/moat/p9-rule-of-two.sh | HIGH | review@2026-09-27T16:08Z | **ROUND 2 REJECT (new reviewer), a different and more severe bypass than round 1**: round 1's fix (env sentinel + GH_CONFIG_DIR + git credential.helper reset via GIT_CONFIG_COUNT/KEY/VALUE) correctly closes both round-1-confirmed bypasses -- verified independently by BOTH round-2 reviewers (one APPROVE, one REJECT, but the REJECT's own verification of the round-1 fixes is itself a clean pass). The REJECT is for a THIRD, previously undisclosed and unhandled vector: **SSH transport is completely unhandled**. Every round-1 mechanism (token sentinel, GH_CONFIG_DIR, credential.helper reset) covers only HTTPS-based git/gh credential resolution -- none touch SSH_AUTH_SOCK, GIT_SSH_COMMAND, core.sshCommand, or ~/.ssh. Reproduced live, read-only: `ssh-add -l` exits 0 (the provider session's live, untouched $HOME/env always has a reachable SSH agent by the fix's own design), and `ssh -T git@github.com` completes full auth as the real account, completely unaffected by every round-1 mitigation. An agent can simply `git remote set-url origin git@github.com:<owner>/<repo>.git && git push` and authenticate exactly as an unrestricted session -- simpler than the already-disclosed `gh auth token -u <username>` gap (no username lookup, no keychain tooling, just the operator's normal always-on ssh-agent). The P9 test's own PASS assertion text is unqualified and false for this case. Fix path per the reviewer: extend the existing capture/apply/restore/depth-counter architecture (already correct for the HTTPS vars) to also sentinel SSH_AUTH_SOCK and override GIT_SSH_COMMAND/core.sshCommand for the withheld session -- a bounded addition to an already-sound design, not a redesign. Two secondary non-blocking findings folded into the rework: (1) `env -u GH_TOKEN -u GITHUB_TOKEN gh auth token` is an undisclosed sibling of the already-disclosed username-lookup gap, needs naming; (2) the Bun/TS route has no git-version floor check the bash route already added, silently no-oping the credential.helper reset on git < 2.31 with no operator signal. Per the founder's D25 directive, this does NOT block the current release train -- the vulnerability this slice fixes (both round-1 and this newly-found SSH gap) is already live in published v9.55.0 either way; S-18 ships in its own train the moment its review reaches unanimous APPROVE. Rework dispatched. Round 3 built 3af8cdac (stack 6e38257c, 94f8b26a, b8c0eccf): SSH_AUTH_SOCK unset and GIT_SSH_COMMAND=false in the withheld window with a trusted re-grant, Bun git-floor warning, P9 SSH tier mutation-tested. Disclosed residual: direct ssh -i <key>. 2 opus HIGH reviewers dispatched 14:26. Ships in its own train once unanimous (D25). Round 3 HIGH reviewer 1: SSH blocker CLOSED (synthetic agent), re-grant/depth/login shells verified; REJECT for a new reproduced Bun-route bypass: 5 bare git spawns (council.ts:178,:190,:401, prd_reuse.ts:161-175) inherit the startup env with real GH_TOKEN/SSH_AUTH_SOCK, so an agent core.fsmonitor script runs with operator creds. Round 4 sent 14:43. Round 4 9cb330cb: Bun start-env spawn leak, nested run fails closed, SSH re-grant test. 2 HIGH reviewers dispatched 15:05. Round 4 closed all round-3 findings, but both HIGH reviewers REJECT: the trusted push in on_run_complete (default on) and create_session_pr runs an agent-planted pre-push hook or core.sshCommand with every withheld channel restored (reproduced with synthetic creds). Round 5 sent 15:16: push from a Loki-owned temp repo. CEO decision pending: a Loki-spawned nested run fails closed and cannot push. Round 5 7d5b7fb4: the push goes from a fresh private repo (origin validated as github.com), gh runs from / with GH_REPO; new 38-check test and P9 planted scenario with a positive control; mutation red. Incidents during the build: a glob rm in the shared scratchpad root and 4 synthetic insteadOf lines written to the real ~/.gitconfig (removed; grep of ~/.gitconfig for loki-test/insteadOf found none at 15:40). 2 HIGH reviewers dispatched 15:40. Round 5: reviewer A APPROVE (no plant saw a credential; fetch runs withheld; URL validator rejects 14 attack forms); reviewer B CONCERN: proof-check.sh:208 `gh repo view` ignores GH_REPO from /, so the opt-in check-run never posts (reproduced). Round 6 sent 15:51 with shallow, alias and warning fixes. Round 6 e84f1136: proof-check uses _loki_trusted_repo, explicit --repo, --update-shallow, visible refusals, case-insensitive host, origin pinned (F1). trusted-push 54/54, P9 4/4. HIGH re-review dispatched 16:01. |
134
140
  | S-41 | BACKLOG 139 (top-ranked GF-3 follow-up): shipped composite GitHub Actions (`.github/actions/issue-to-pr/action.yml`, root `action.yml`) still put `GH_TOKEN` in the SAME step as the agent invocation -- an agent can read another process's environment via `/proc/<ppid>/environ` on hosted Linux regardless of its own env | .github/actions/issue-to-pr/action.yml, root action.yml | MEDIUM | released@2026-09-27T15:00Z | Root action.yml was already correct (3 agent-invoking steps never had GH_TOKEN); the real violation was `.github/actions/issue-to-pr/action.yml`'s single fetch-and-run step, split into a token-holding fetch step (writes a PRD file via `gh issue view`) and a token-free `loki start <file>` step (file-mode). Also blanks all 4 token vars on root action.yml's 3 agent steps as defense-in-depth. Unanimous 2/2 APPROVE, both fully independent and adversarial (output-tampering, argument-injection, PRD-content-leak angles all checked clean). One non-blocking follow-up filed: an owner/repo parsing divergence on refs with extra slashes (cosmetic, `gh` rejects either way). 8a714c73 (cherry-picked onto local main, pending push). Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
135
141
  | S-42 | BACKLOG 140: `docs/environment-variables.md`'s `LOKI_ALLOW_AGENT_GITHUB_TOKEN` description is imprecise -- says tokens are "used only by Loki's own push and PR steps," but omits the Bun route (drops the token outright, no push/PR step), the `gh` CLI and proof-check.sh re-grant sites, and the 2 enterprise-token variables entirely | docs/environment-variables.md | LOW | released@2026-09-27T15:00Z | Docs-only, single table cell, skip-rule per SDLC fleet pattern (no review quorum needed). All 4 re-grant sites and both routes' behavior grep-verified against actual source before writing. 32d914f0 (cherry-picked onto local main, pending push). Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
136
142
  | S-43 | BACKLOG 133: `scripts/v10-pulse.sh`'s `PULSE_MOAT_RESULT` file isn't tied to the commit it was measured against -- a stale capture from an earlier commit would read as clean today | scripts/v10-pulse.sh (moat-regression check only), its test | LOW | released@2026-09-27T15:00Z | Sidecar-file convention (`<PULSE_MOAT_RESULT>.sha`, written before the suite runs) compared against `$PULSE_MAIN_REF`'s resolved HEAD; 3 fail-closed paths all demote to UNKNOWN, never false-clean. Rejected a read-time env-var alternative that would have reintroduced the exact bug. 1/1 APPROVE with 2 non-blocking wording nits, one applied directly (the no-provenance error message now says "BEFORE running the suite" instead of the more ambiguous "alongside"). 34/34 tests pass on bash and real bash 3.2.57. 7dd23bcb. Shipped in v9.56.0 (merged before the 14:16:24Z release push of b651b98d; on npm 15:00:06Z). |
@@ -195,3 +195,17 @@ One entry per decision: context, choice, why, how to reverse. Newest last.
195
195
  - Choice: build guards 1-3 now as slices through the normal review process (per the founder's own instruction: "each as a slice through normal review"), without interrupting or pushing anything onto the currently-frozen release train (train 1 remains frozen at `c31cb1e8` until Tests/Bun Parity/Security Audit are all green there). Guards 4-7 are process/documentation changes that also queue as slices, cut alongside 1-3, but are not blocking the reply the founder asked for (the founder's own instruction is to reply once guards 1-3 are live).
196
196
  - Why: this directive is itself direct, textbook evidence for guard 5's own thesis -- this session has repeatedly found and disclosed the SAME classes of near-miss (an agent spending disproportionate time/tokens on trivial work, a stuck background classification process, a BOARD.md edit that could have dropped rows, self-inflicted CI cancellation) as one-off narrative corrections in PROGRESS.md's drift audits, without ever converting any of them into a structural guard that makes the mistake mechanically non-repeatable. The founder's framing ("prompts drift; guards do not") is the correct fix for a pattern this session's own turn-102/120/132/138/144 audits kept re-discovering piecemeal.
197
197
  - Reverse: continue relying on per-turn discipline and drift-audit self-correction without codifying guards. Not chosen -- directly countermanded, and this session's own history is the founder's strongest evidence against it.
198
+
199
+ ## D27. 2026-09-27: Loki directive -- speed: a train every 20 minutes, CI replaces local gates, trains pipelined
200
+
201
+ - Context: train 2 sat unpushed for more than 89 minutes. The local-ci fast tier took more than 10 minutes under swarm load (90 of 173 checks at the 600s cap, EXIT=124), and the pre-push hook's serial pytest alone exceeded 10 minutes. The first push attempt was killed by its own timeout. The second reported success through a pipe while `git ls-remote` still showed b651b98d. Loki directed: push train 2 now with PRE_PUSH_SKIP=1 (Loki-authorized; CI is the gate), never pipe git push, and verify each push by ls-remote.
202
+ - Choice (Loki approved):
203
+ 1. The "Local CI Before Every Push" mandate (2026-07-31) is retired. Before a push, run only syntax checks (`bash -n`, `py_compile`) plus the slice's own tests, capped at 60 seconds. GitHub CI (Tier B) is the release gate.
204
+ 2. `.githooks/pre-push` becomes identity check + `bash -n` + red-main warning only (no pytest), target under 5 seconds; a LOW slice in train 3 (S-97).
205
+ 3. A push is done only when `git ls-remote origin refs/heads/main` equals `git rev-parse HEAD`; recorded as evidence and enforced by a guard (S-98).
206
+ 4. Train 3 carries the release-speed fixes first as each is approved (Part C items 1-7: S-79, S-80, S-84, S-81, the needs:gate removal inside S-84, S-82, S-83); other approved slices ride along but never delay a train.
207
+ 5. Trains are pipelined: a train counts as released when publish-npm succeeds. The next train opens immediately, without waiting for npm's availability lag, Docker or Homebrew; train N+1 can run Tier B while train N publishes.
208
+ 6. Cadence: push a train every 20 minutes whenever at least one merged slice exists, even a single one. New pulse violation TRAIN_LATE: more than 25 minutes since the last train push while merged-unreleased commits exist (S-98).
209
+ - Release Manager exception, disclosed: until S-16 makes `scripts/release.sh` bump every checklist file without prompting or pushing, the Release Manager bumps VERSION with the editor tool (not a shell command) as part of the release commit. v9.57.0 (5332bfc3) was bumped this way. This is the only permitted VERSION write outside release.sh, and it ends when S-16 merges.
210
+ - Evidence: train 2 push, `PRE_PUSH_SKIP=1 git push origin main` at 15:48:18Z: rc=0; `git rev-parse HEAD` = `git ls-remote origin refs/heads/main` = 89e350bd641a4b659faa405587a9db5bb7bcc201. Release push at 15:50:09Z: rc=0; both = 5332bfc35ccf801bdf8c67bff9fc07537ac4240f. The per-SHA concurrency from S-80 held on its first real push: Tests on 89e350bd kept running (`in_progress`) after 5332bfc3 was pushed.
211
+ - Reverse: keep local CI as a precondition. Not chosen: it cannot finish inside the 10-minute command cap under swarm load, and it duplicates GitHub CI.
@@ -123,3 +123,36 @@ ratchet-ordering deadlock: GF-4 cannot promote `P4.three-setups-resolve`
123
123
  out of pending.txt until S-19's stricter pass bar is also in the tree, or
124
124
  the merge itself becomes a ratchet regression. S-19 promoted to the
125
125
  critical path and reviewed immediately.
126
+
127
+ ## CI: Tests workflow shell-tests sharding (S-81, supersedes S-70)
128
+
129
+ S-70 resharded shell-tests 4 -> 8 with a plain `idx % n` split and measured
130
+ the real per-suite cost of doing so (see its commit, c6000cb9). S-81 keeps
131
+ n=8 but replaces the index split with a deterministic greedy longest-first
132
+ (LPT) bin-packing over measured per-suite durations, so the 8 shards are
133
+ balanced by real cost instead of by coincidence of registration order.
134
+
135
+ BEFORE numbers are measured from a real completed GitHub Actions run. AFTER
136
+ numbers are **projected from measured per-suite durations**, not measured --
137
+ this sandbox cannot trigger a real push, so nothing below claims to be a live
138
+ CI wall clock for the new packing until a real run confirms it.
139
+
140
+ | Metric | Value | Method |
141
+ |---|---|---|
142
+ | BEFORE: measured per-suite durations, source | real run 36319020918 (2026-09-27), 4 shard job logs, S-44's START/END lines | `gh run view --job <id> --log`, grepped for `END: ` |
143
+ | BEFORE: suites with a real measured row | 496 of 496 registered suites (100%; no suite needed the default) | exact-name join against `tests/run-all-tests.sh`'s `run_test` registrations |
144
+ | Deliberate override, not a measurement | `test-ci-json-argmax.sh` measured 1186s in that run; a parallel BACKLOG 25 slice is shrinking its fixture, so `tests/shard-durations.tsv` assumes ~60s post-fix and uses 60, not 1186 | documented in the table's own header comment |
145
+ | BEFORE (projected from measured per-suite durations): max shard at n=4, plain `idx % 4` (current main, pre-S-70/S-81) | 451s test-time (~7.5min) | `idx % 4` replay against `tests/shard-durations.tsv`'s same measured rows (single source run, n=1) |
146
+ | BEFORE (projected from measured per-suite durations): max shard at n=8, plain `idx % 8` (S-70's scheme) | 346s test-time (~5.8min) | same replay, `idx % 8` -- matches S-70's own commit projection (c6000cb9) of "~346s (~5.8min)" once argmax is fixed, cross-checked independently here |
147
+ | AFTER (projected from measured per-suite durations): max shard at n=8, LPT-packed (S-81, this change) | 197s test-time (~3.3min) -- every shard lands at exactly 197s, because 1576s total / 8 divides evenly against this suite set | replay of the actual `tests/run-all-tests.sh` LPT logic via `LOKI_TEST_SHARD=i/8 LOKI_TEST_LIST=1`, summed against `tests/shard-durations.tsv` |
148
+ | AFTER (projected from measured per-suite durations): max shard including ~29s job setup (checkout/pip/npm/bun, per S-70's measurement) | about 226s (about 3.8min), under the 6-minute target | same table + S-70's measured setup overhead |
149
+ | Named suites land in 5 distinct shards (required) | trust-core-detect -> shard 1, review-assurance-tail -> shard 0, e2e-features -> shard 4, shellcheck -> shard 2, no-unreachable -> shard 3 | same LPT replay |
150
+ | Partition proof | `tests/test-shard-coverage.sh`: n in [2, 3, 4, 6, 8] each give union=496, 0 duplicates, 0 missing, no empty shard -- driven through the real runtime path (`LOKI_TEST_LIST=1`), not a second copy of the packing algorithm | `bash tests/test-shard-coverage.sh` |
151
+
152
+ **What still needs real-CI verification** (cannot be done from this sandbox):
153
+ an actual GitHub Actions run of the updated `test.yml` at n=8 with the new
154
+ packing, to confirm the real wall clock lands near the ~3.8min projection
155
+ rather than something job-setup variance or GitHub's own queueing missed, and
156
+ that `test-ci-json-argmax.sh` actually lands near 60s once its BACKLOG 25
157
+ fixture fix ships (until then this table's 60s row is an assumption, not a
158
+ measurement, as stated in `tests/shard-durations.tsv`'s own header).
@@ -1197,3 +1197,14 @@ sample.
1197
1197
  - Near-miss, false green: the train 2 background push "completed (exit code 0)", but `git ls-remote origin refs/heads/main` still returned b651b98d. `timeout 600 git push ... | tail -5` reported tail's status, not the push's. The first attempt had already been killed by a 120s timeout during the pre-push hook's serial pytest. Correction: after every push, compare `git ls-remote` with `git rev-parse HEAD`; never trust a piped exit code. GUARDS.md candidate (S-76 follow-up): a push helper in scripts/v10-ops.sh that asserts remote == local.
1198
1198
  - The local-ci fast tier took more than 10 min under swarm load (90 of 173 checks at 600s, EXIT=124). Its one real failure (CLAUDE.md tool count after S-78) was fixed in 6f1e68eb. This conflicts with the 10-min command cap; S-91 (Tier A) is the planned replacement. Until then, release on GitHub CI plus the packaging checks run by hand (npm pack contents: 938 entries, 9/9 required).
1199
1199
  - Standing drift check: slices finished inside workflows leave BOARD rows at building until the orchestrator reads the result, which fed 5 false AGENT_OVER_BUDGET hits at 15:15. Read workflow results the same turn they complete.
1200
+
1201
+ ## Trains 2 and 3 under D27 (2026-09-27)
1202
+ | Time (UTC) | Event | Evidence |
1203
+ |---|---|---|
1204
+ | 15:36 | Train 2 assembled (12 slices); third push attempt still in the pre-push serial pytest | push-train2 log |
1205
+ | 15:47 | Loki directive D27 received; background push stopped (TaskStop bw1fgezxd) | |
1206
+ | 15:48:18-21 | Train 2 pushed: PRE_PUSH_SKIP=1 git push origin main, rc=0 | HEAD = ls-remote = 89e350bd641a4b659faa405587a9db5bb7bcc201 |
1207
+ | 15:50:09-12 | Train 2 release commit v9.57.0 pushed, rc=0 | HEAD = ls-remote = 5332bfc35ccf801bdf8c67bff9fc07537ac4240f |
1208
+ | 15:50 | S-80 verified on a real push: Tests on 89e350bd stayed in_progress after 5332bfc3 landed | gh run list |
1209
+ | 15:53:23-26 | Train 3 (S-79, S-81, S-82, S-83, S-85) plus release commit v9.58.0 pushed, rc=0 | HEAD = ls-remote = c2eccb21c1273b27a385aa785fedee3269c4f605 |
1210
+ - Next: each train counts as released when its publish-npm job succeeds. Train 4 opens now: S-84 (Part C items 3 and 5) once approved, plus S-97 and S-98 (D27 hook and cadence guard). Target push by 16:13 (20-minute cadence).
package/docs/v10/SWARM.md CHANGED
@@ -223,6 +223,30 @@ queue. Never unpublishes or deletes a version.
223
223
  agent-authored slices never combine untrusted input, an agent, and push
224
224
  rights either.
225
225
 
226
+ ## Slice card template
227
+
228
+ Binding: the orchestrator dispatches every builder and reviewer from a slice
229
+ card below, never from conversation history or its own recall of prior
230
+ turns -- the card is the sole source of scope for that agent. While the
231
+ swarm runs, the orchestrator's own live view stays limited to
232
+ `docs/v10/CONTROL.md`, the pulse block, and `docs/v10/BOARD.md`; it does not
233
+ re-derive scope from scrollback.
234
+
235
+ <!-- SLICE-CARD-TEMPLATE:START -->
236
+ Slice: S-<id>
237
+ Goal: <one sentence, the acceptance condition for this slice>
238
+ Tier: LOW | MEDIUM | HIGH
239
+ Model: <pinned model, e.g. opus, sonnet -- never inherited, see D13>
240
+ Budget: <minutes>
241
+ Files:
242
+ - <path>
243
+ - <path>
244
+ Wall checks:
245
+ - <command or assertion that must pass before review>
246
+ Commands:
247
+ - <exact shell command(s) the builder runs>
248
+ <!-- SLICE-CARD-TEMPLATE:END -->
249
+
226
250
  ## Metrics (appended to METRICS.md after every wave)
227
251
 
228
252
  - releases in the last 24h
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var ct=Object.create;var{getPrototypeOf:lt,defineProperty:OG,getOwnPropertyNames:it}=Object;var at=Object.prototype.hasOwnProperty;function ot($){return this[$]}var st,nt,rt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?st??=new WeakMap:nt??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?ct(lt($)):{};let J=X||!$||!$.__esModule?OG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of it($))if(!at.call(J,q))OG(J,q,{get:ot.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var Zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var tt=($)=>$;function et($,X){this[$]=tt.bind(null,X)}var B1=($,X)=>{for(var Q in X)OG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:et.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var HR={};B1(HR,{lokiDir:()=>h0,homeLokiDir:()=>GQ,findRepoRootForVersion:()=>LG,REPO_ROOT:()=>O1});import{resolve as h2,dirname as jG}from"path";import{fileURLToPath as $e}from"url";import{existsSync as Kq}from"fs";import{homedir as Xe}from"os";function Qe(){let $=UR;for(let X=0;X<6;X++){if(Kq(h2($,"VERSION"))&&Kq(h2($,"autonomy/run.sh")))return $;let Q=jG($);if(Q===$)break;$=Q}return h2(UR,"..","..","..")}function LG($){let X=$;for(let Q=0;Q<6;Q++){if(Kq(h2(X,"VERSION"))&&Kq(h2(X,"autonomy/run.sh")))return X;let z=jG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function GQ(){return h2(Xe(),".loki")}var UR,O1;var k1=s(()=>{UR=jG($e(import.meta.url));O1=Qe()});import{readFileSync as ze}from"fs";import{resolve as Ze,dirname as Ke}from"path";import{fileURLToPath as Je}from"url";function L9(){if(t3!==null)return t3;let $="9.57.0";if(typeof $==="string"&&$.length>0)return t3=$,t3;try{let X=Ke(Je(import.meta.url)),Q=LG(X);t3=ze(Ze(Q,"VERSION"),"utf-8").trim()}catch{t3="unknown"}return t3}var t3=null;var Jq=s(()=>{k1()});var jR={};B1(jR,{runOrThrow:()=>Te,run:()=>X1,readStreamCapped:()=>qq,commandVersion:()=>Fe,commandExists:()=>g5,ShellError:()=>CG,MAX_STDOUT_BYTES:()=>OR});async function qq($,X=OR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function X1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([qq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Te($,X={}){let Q=await X1($,X);if(Q.exitCode!==0)throw new CG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=De($),Q=await X1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function De($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function Fe($,X="--version"){if(!await g5($))return null;let z=await X1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var OR=16777216,CG;var y8=s(()=>{CG=class CG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return Re?"":$}var Re,p0,$5,Z1,y61,A1,f1,m5,n;var e7=s(()=>{Re=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),Z1=g2("\x1B[1;33m"),y61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),n=g2("\x1B[0m")});import{existsSync as fe}from"fs";async function Z2(){if(BQ!==void 0)return BQ;let $="/opt/homebrew/bin/python3.12";if(fe($))return BQ=$,$;let X=await g5("python3.12");if(X)return BQ=X,X;let Q=await g5("python3");return BQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return X1([Q,"-c",$],X)}var BQ;var m2=s(()=>{y8()});var hR={};B1(hR,{runStatus:()=>J00});import{existsSync as u5,readFileSync as T9,readdirSync as xR,statSync as kR}from"fs";import{resolve as A5,basename as re}from"path";import{homedir as te}from"os";function SR($){let X=Math.trunc($);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function yR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Yq/X);if(Z>Yq)Z=Yq;let K=Yq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=Z1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=SR($),Y=SR(X);return` ${f1}${Q}${n} ${J}[${q}]${n} ${z}% (${V} / ${Y})`}async function $00(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${n}
2
+ var ct=Object.create;var{getPrototypeOf:lt,defineProperty:OG,getOwnPropertyNames:it}=Object;var at=Object.prototype.hasOwnProperty;function ot($){return this[$]}var st,nt,rt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?st??=new WeakMap:nt??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?ct(lt($)):{};let J=X||!$||!$.__esModule?OG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of it($))if(!at.call(J,q))OG(J,q,{get:ot.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var Zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var tt=($)=>$;function et($,X){this[$]=tt.bind(null,X)}var B1=($,X)=>{for(var Q in X)OG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:et.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var HR={};B1(HR,{lokiDir:()=>h0,homeLokiDir:()=>GQ,findRepoRootForVersion:()=>LG,REPO_ROOT:()=>O1});import{resolve as h2,dirname as jG}from"path";import{fileURLToPath as $e}from"url";import{existsSync as Kq}from"fs";import{homedir as Xe}from"os";function Qe(){let $=UR;for(let X=0;X<6;X++){if(Kq(h2($,"VERSION"))&&Kq(h2($,"autonomy/run.sh")))return $;let Q=jG($);if(Q===$)break;$=Q}return h2(UR,"..","..","..")}function LG($){let X=$;for(let Q=0;Q<6;Q++){if(Kq(h2(X,"VERSION"))&&Kq(h2(X,"autonomy/run.sh")))return X;let z=jG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function GQ(){return h2(Xe(),".loki")}var UR,O1;var k1=s(()=>{UR=jG($e(import.meta.url));O1=Qe()});import{readFileSync as ze}from"fs";import{resolve as Ze,dirname as Ke}from"path";import{fileURLToPath as Je}from"url";function L9(){if(t3!==null)return t3;let $="9.59.0";if(typeof $==="string"&&$.length>0)return t3=$,t3;try{let X=Ke(Je(import.meta.url)),Q=LG(X);t3=ze(Ze(Q,"VERSION"),"utf-8").trim()}catch{t3="unknown"}return t3}var t3=null;var Jq=s(()=>{k1()});var jR={};B1(jR,{runOrThrow:()=>Te,run:()=>X1,readStreamCapped:()=>qq,commandVersion:()=>Fe,commandExists:()=>g5,ShellError:()=>CG,MAX_STDOUT_BYTES:()=>OR});async function qq($,X=OR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function X1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([qq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Te($,X={}){let Q=await X1($,X);if(Q.exitCode!==0)throw new CG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=De($),Q=await X1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function De($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function Fe($,X="--version"){if(!await g5($))return null;let z=await X1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var OR=16777216,CG;var y8=s(()=>{CG=class CG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return Re?"":$}var Re,p0,$5,Z1,y61,A1,f1,m5,n;var e7=s(()=>{Re=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),Z1=g2("\x1B[1;33m"),y61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),n=g2("\x1B[0m")});import{existsSync as fe}from"fs";async function Z2(){if(BQ!==void 0)return BQ;let $="/opt/homebrew/bin/python3.12";if(fe($))return BQ=$,$;let X=await g5("python3.12");if(X)return BQ=X,X;let Q=await g5("python3");return BQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return X1([Q,"-c",$],X)}var BQ;var m2=s(()=>{y8()});var hR={};B1(hR,{runStatus:()=>J00});import{existsSync as u5,readFileSync as T9,readdirSync as xR,statSync as kR}from"fs";import{resolve as A5,basename as re}from"path";import{homedir as te}from"os";function SR($){let X=Math.trunc($);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function yR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Yq/X);if(Z>Yq)Z=Yq;let K=Yq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=Z1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=SR($),Y=SR(X);return` ${f1}${Q}${n} ${J}[${q}]${n} ${z}% (${V} / ${Y})`}async function $00(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${n}
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)
@@ -1349,4 +1349,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1349
1349
  `),2}case"start":{let{runStart:z}=await Promise.resolve().then(() => (ut(),mt));return z(Q)}default:return process.stderr.write(`Unknown command: ${X}
1350
1350
  `),process.stderr.write(dt),2}}PR();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var N61=await B61(Bun.argv.slice(2));process.exit(N61);
1351
1351
 
1352
- //# debugId=F76B88433C16492BF932B310B0E94366
1352
+ //# debugId=BE3483EEAD4BDCD4D66273ADAB0A2BC0
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '9.57.0'
78
+ __version__ = '9.59.0'
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": "9.57.0",
4
+ "version": "9.59.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, opencode).",
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": "9.57.0",
5
+ "version": "9.59.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",