loki-mode 11.74.2 → 11.75.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SKILL.md +2 -3
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +10 -10
- package/autonomy/council-v2.sh +4 -4
- package/autonomy/grill.sh +1 -1
- package/autonomy/lib/browser-open.sh +1 -1
- package/autonomy/lib/chat.sh +1 -1
- package/autonomy/lib/cockpit-render.sh +1 -1
- package/autonomy/lib/done-recognition.sh +4 -4
- package/autonomy/lib/efficiency_cost.py +1 -1
- package/autonomy/lib/engine10-push.sh +2 -2
- package/autonomy/lib/prd-enrich.sh +1 -1
- package/autonomy/lib/proof-check.sh +1 -1
- package/autonomy/lib/proof-generator.py +3 -3
- package/autonomy/lib/proof-pr.sh +1 -1
- package/autonomy/lib/skill-link-heal.sh +1 -1
- package/autonomy/lib/voter-agents.sh +1 -1
- package/autonomy/loki +26 -37
- package/autonomy/prd-checklist.sh +1 -1
- package/autonomy/receipt_jwt.py +1 -1
- package/autonomy/run.sh +28 -28
- package/autonomy/trigger-schedule.py +6 -414
- package/autonomy/tui.sh +1 -1
- package/autonomy/verify.sh +4 -4
- package/completions/loki.bash +0 -5
- package/dashboard/__init__.py +1 -1
- package/docs/ARCHITECTURE.md +1 -2
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/COMPONENTS.md +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/ROUTING-EVIDENCE.md +1 -2
- package/docs/V10-RESEARCH.md +1 -1
- package/docs/dev/architecture-reference.md +3 -7
- package/docs/dev/project-structure.md +1 -4
- package/docs/dev/release-checklist.md +13 -31
- package/docs/research-2026-07/OBSERVABILITY-TOOL-SPEC.md +1 -1
- package/docs/v11/MARKET-SLICES.md +1 -1
- package/docs/v11/REGISTRIES.md +1 -1
- package/docs/v11/ROUTER-1.md +2 -2
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/skills/extending.md +2 -3
- package/skills/factory-operations.md +11 -18
- package/skills/release-cadence.md +2 -2
- package/web-app/dist/assets/{AdminPage-jkjilfbl.js → AdminPage-xKgtIhk2.js} +1 -1
- package/web-app/dist/assets/{Avatar-bd5JWkSJ.js → Avatar-Q14AHox9.js} +1 -1
- package/web-app/dist/assets/{Badge-DVRit2Xw.js → Badge-yk5M-lip.js} +1 -1
- package/web-app/dist/assets/{Button-BNAB-pVZ.js → Button-ynznk0At.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-DpG29h_w.js → CockpitPage-C8B765za.js} +1 -1
- package/web-app/dist/assets/{ComparePage-Dasg5V41.js → ComparePage-DqzmWKnf.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-DJojse6b.js → ErrorBoundary-DdBPd_Lq.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-Blxw7_u1.js → EvidenceReceiptPanel-jb4Tp38b.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-BBvSFj80.js → GitHubIssuesPanel-xJQiLcxs.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-BCIKeSBg.js → GitHubPRsPanel-84W3Dhk_.js} +1 -1
- package/web-app/dist/assets/{HomePage-C7d__oNG.js → HomePage-St1Xl2tu.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BXljL3yY.js → LoginPage-jRt0NUHb.js} +1 -1
- package/web-app/dist/assets/{MagicPage-Xjkts_yE.js → MagicPage-CQLFe7LR.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-kQvVp-j2.js → MetricsPage-zzX9eTC5.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-D-4ZlEaO.js → NotFoundPage-DDu023SY.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-DfzR5RXP.js → ProjectPage-D60UURaH.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DFDsWzkV.js → ProjectsPage-DqVsKxlt.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-CLPIoYT3.js → SettingsPage-G7pPKvo0.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-PZKkyGeY.js → ShowcasePage-CrU_aKgS.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-BsBlcnzo.js → SystemSettingsPage-ClKGQFsB.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-BSjgNhoU.js → TeamsPage-B530vSNe.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-W2KeAzbg.js → TemplatesPage-COmv_Cqd.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-BjroTiCC.js → TerminalOutput-CufG9YzF.js} +1 -1
- package/web-app/dist/assets/{activity-BXZ5YYes.js → activity-Ct1Updr6.js} +1 -1
- package/web-app/dist/assets/{bell-D1urp5pa.js → bell-rcvhQuGr.js} +1 -1
- package/web-app/dist/assets/{bot-Chedf2bF.js → bot-DzeqEI-b.js} +1 -1
- package/web-app/dist/assets/{check-DhaCuOCV.js → check-zacnZHyC.js} +1 -1
- package/web-app/dist/assets/{chevron-left-D7BpQyA6.js → chevron-left-Lxnkl8Qg.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DnGvQ_AQ.js → circle-alert-DoqSdQdB.js} +1 -1
- package/web-app/dist/assets/{clock-CqfxWhHM.js → clock-B7bJcVIC.js} +1 -1
- package/web-app/dist/assets/{cloud-BKxp5AWB.js → cloud-DdA9Bu6q.js} +1 -1
- package/web-app/dist/assets/{code-xml-BDQ6GBtt.js → code-xml-CAZEZ9T8.js} +1 -1
- package/web-app/dist/assets/{database-BmpEx1y_.js → database-Da2GkEIN.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-DYNY9R_C.js → dollar-sign-DjDiG-37.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-BJi0IBFd.js → file-code-corner-CSL-Jij-.js} +1 -1
- package/web-app/dist/assets/{file-plus-Dqyb2bcQ.js → file-plus-02DRA8pV.js} +1 -1
- package/web-app/dist/assets/{globe-DPh4RWfM.js → globe-CGyB90kK.js} +1 -1
- package/web-app/dist/assets/{hammer-B3xnuoA1.js → hammer-BaQB96TG.js} +1 -1
- package/web-app/dist/assets/{index-BpX8DcTr.js → index-lcoI1_Kk.js} +3 -3
- package/web-app/dist/assets/{layers-BRCmyCW8.js → layers-BWJBvoeg.js} +1 -1
- package/web-app/dist/assets/{loader-circle-n07Pky0M.js → loader-circle-CdcI0FeS.js} +1 -1
- package/web-app/dist/assets/{lock-BWqOuQmS.js → lock-BBkw5NJx.js} +1 -1
- package/web-app/dist/assets/{package-BJlB_Z-Z.js → package-BpPRO3LM.js} +1 -1
- package/web-app/dist/assets/{plus-oH17BZu2.js → plus-D7XbyK29.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CPQ476LL.js → refresh-cw-DLG3hyAi.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-7hLvOcnS.js → rotate-ccw-Bqau4R_S.js} +1 -1
- package/web-app/dist/assets/{scroll-text-BCmLauQn.js → scroll-text-BtIL3cNk.js} +1 -1
- package/web-app/dist/assets/{server-DfTfvW84.js → server-tZCmZ4yH.js} +1 -1
- package/web-app/dist/assets/{shield-alert-CaeGRq19.js → shield-alert-K5JXzFKg.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-Dz-l2GPC.js → thumbs-up-CHCVyJdw.js} +1 -1
- package/web-app/dist/assets/{trash-2-B-OsDmSo.js → trash-2-DncNxVq0.js} +1 -1
- package/web-app/dist/assets/{trending-down-CXTFOxzq.js → trending-down-D8Bp0azX.js} +1 -1
- package/web-app/dist/assets/{trending-up-FQEdqr3v.js → trending-up-AixETdKR.js} +1 -1
- package/web-app/dist/assets/{upload-CacWSNTg.js → upload-VCbjFn2H.js} +1 -1
- package/web-app/dist/assets/{usePolling-cIjZ9qd2.js → usePolling-CQvKD2Jj.js} +1 -1
- package/web-app/dist/assets/{user-DAouWFHi.js → user-SK-YguQ6.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/autonomy/company-seats.json +0 -82
- package/docs/COMPETITIVE-INTEL-2026-09.md +0 -444
- package/docs/FOUNDER-DECISIONS-TRUST-SEMANTICS-2026-08-01.md +0 -145
- package/docs/FOUNDER-STATUS-AND-DECISIONS-2026-07-01.md +0 -132
- package/docs/LOKI-10-BUILD-PROMPT.md +0 -350
- package/docs/STRATEGY-2026-2028.md +0 -124
- package/docs/V10-VISION.md +0 -161
- package/docs/v10/ADOPTION-PLAN.md +0 -375
- package/docs/v10/ARCHITECT-CUTS.md +0 -744
- package/docs/v10/BACKLOG.md +0 -231
- package/docs/v10/BOARD.md +0 -1403
- package/docs/v10/CONTROL-PLANE.md +0 -120
- package/docs/v10/CONTROL.md +0 -36
- package/docs/v10/CP-ASK-PLAN.md +0 -85
- package/docs/v10/CP-ENTERPRISE-UI.md +0 -537
- package/docs/v10/D51-PHASE-B.md +0 -120
- package/docs/v10/D61-SPEED.md +0 -105
- package/docs/v10/D63-CARDS.md +0 -354
- package/docs/v10/DECISIONS.md +0 -915
- package/docs/v10/E2E-D65.md +0 -123
- package/docs/v10/ENGINE-LAWS.md +0 -163
- package/docs/v10/ENGINE-MEASURE.md +0 -69
- package/docs/v10/ENGINE.md +0 -743
- package/docs/v10/FAILURE-CLASSES.md +0 -2026
- package/docs/v10/FC21B-FC22-PLAN.md +0 -107
- package/docs/v10/FIRST-RUN-GATE.md +0 -39
- package/docs/v10/FOUNDER-QUEUE.md +0 -29
- package/docs/v10/GAP-RESEARCH-1003.md +0 -32
- package/docs/v10/GUARDS.md +0 -883
- package/docs/v10/L0-WAVE1.md +0 -81
- package/docs/v10/LEGACY-REMOVAL.md +0 -408
- package/docs/v10/MEDIUM-ANALYSIS.md +0 -346
- package/docs/v10/METRICS.md +0 -645
- package/docs/v10/MODERNIZE.md +0 -437
- package/docs/v10/OPENCODE-PARITY-PLAN.md +0 -84
- package/docs/v10/OPERATING-MODEL.md +0 -60
- package/docs/v10/PLAN-V12.md +0 -896
- package/docs/v10/PROGRESS.md +0 -2735
- package/docs/v10/RELEASE-11.md +0 -77
- package/docs/v10/RETRO-2026.md +0 -650
- package/docs/v10/ROADMAP-D63.md +0 -35
- package/docs/v10/SCALE.md +0 -140
- package/docs/v10/SCORECARD-PLAN.md +0 -250
- package/docs/v10/SWARM-PROMPT.md +0 -98
- package/docs/v10/SWARM.md +0 -293
- package/docs/v10/cp-redesign/PARITY.md +0 -188
- package/docs/v10/cp-redesign/legacy/INDEX.md +0 -1167
- package/docs/v10/roadmap-10/01-measurement.md +0 -113
- package/docs/v10/roadmap-10/02-cost.md +0 -75
- package/docs/v10/roadmap-10/03-speed.md +0 -65
- package/docs/v10/roadmap-10/04-first-run.md +0 -61
- package/docs/v10/roadmap-10/05-deploy.md +0 -61
- package/docs/v10/roadmap-10/06-stability.md +0 -64
- package/docs/v10/roadmap-10/07-quality.md +0 -57
- package/docs/v10/roadmap-10/08-proof.md +0 -48
- package/docs/v10/roadmap-10/09-safety.md +0 -74
- package/docs/v10/roadmap-10/README.md +0 -1464
- package/skills/sdlc-fleet.md +0 -170
- package/tools/cockpit/SPEC.md +0 -275
- package/tools/cockpit/index.html +0 -146
- package/tools/cockpit/roi-basis.json +0 -33
- package/tools/cockpit/server.py +0 -946
|
@@ -19,30 +19,18 @@ Supported actions:
|
|
|
19
19
|
run <issue-ref> - Run loki run on an issue/ref
|
|
20
20
|
status - Run loki status
|
|
21
21
|
quality-review - Run loki review
|
|
22
|
-
seat <name> - One headless tick of company seat <name>
|
|
23
|
-
(.claude/agents/<name>.md), see tick_seat()
|
|
24
22
|
|
|
25
23
|
Usage:
|
|
26
24
|
python3 autonomy/trigger-schedule.py [--once] [--dry-run]
|
|
27
|
-
python3 autonomy/trigger-schedule.py daemon [--loop]
|
|
28
|
-
python3 autonomy/trigger-schedule.py tick <seat>
|
|
29
|
-
python3 autonomy/trigger-schedule.py seed | install | uninstall
|
|
30
25
|
"""
|
|
31
26
|
|
|
32
27
|
import argparse
|
|
33
|
-
import fcntl
|
|
34
28
|
import json
|
|
35
29
|
import logging
|
|
36
30
|
import os
|
|
37
|
-
import plistlib
|
|
38
|
-
import re
|
|
39
|
-
import shlex
|
|
40
|
-
import shutil
|
|
41
|
-
import signal
|
|
42
31
|
import subprocess
|
|
43
32
|
import sys
|
|
44
|
-
import
|
|
45
|
-
from datetime import datetime, timedelta, timezone
|
|
33
|
+
from datetime import datetime, timezone
|
|
46
34
|
from pathlib import Path
|
|
47
35
|
|
|
48
36
|
|
|
@@ -195,23 +183,10 @@ def build_loki_args(schedule):
|
|
|
195
183
|
return action.split() + list(args)
|
|
196
184
|
|
|
197
185
|
|
|
198
|
-
def build_seat_command(schedule):
|
|
199
|
-
"""argv that runs one seat tick in a detached child of this script."""
|
|
200
|
-
seat = schedule.get("action", "").split(None, 1)[1].strip()
|
|
201
|
-
return [sys.executable, os.path.abspath(__file__), "tick", seat]
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def is_seat_action(schedule):
|
|
205
|
-
return schedule.get("action", "").split(None, 1)[:1] == ["seat"] and \
|
|
206
|
-
len(schedule.get("action", "").split()) == 2
|
|
207
|
-
|
|
208
|
-
|
|
209
186
|
def run_schedule(schedule, dry_run=False):
|
|
210
187
|
"""Execute a scheduled loki command."""
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
else:
|
|
214
|
-
cmd = ["loki"] + build_loki_args(schedule)
|
|
188
|
+
loki_args = build_loki_args(schedule)
|
|
189
|
+
cmd = ["loki"] + loki_args
|
|
215
190
|
name = schedule.get("name", "unnamed")
|
|
216
191
|
|
|
217
192
|
if dry_run:
|
|
@@ -220,12 +195,10 @@ def run_schedule(schedule, dry_run=False):
|
|
|
220
195
|
|
|
221
196
|
logging.info("Schedule '%s' firing: %s", name, " ".join(cmd))
|
|
222
197
|
try:
|
|
223
|
-
extra = {"start_new_session": True} if is_seat_action(schedule) else {}
|
|
224
198
|
proc = subprocess.Popen(
|
|
225
199
|
cmd,
|
|
226
|
-
stdout=subprocess.
|
|
227
|
-
stderr=subprocess.
|
|
228
|
-
**extra
|
|
200
|
+
stdout=subprocess.PIPE,
|
|
201
|
+
stderr=subprocess.PIPE,
|
|
229
202
|
)
|
|
230
203
|
logging.info("Schedule '%s' started pid=%d", name, proc.pid)
|
|
231
204
|
return True
|
|
@@ -234,351 +207,6 @@ def run_schedule(schedule, dry_run=False):
|
|
|
234
207
|
return False
|
|
235
208
|
|
|
236
209
|
|
|
237
|
-
# ---------------------------------------------------------------------------
|
|
238
|
-
# Company seats: always-on headless ticks of .claude/agents/<name>.md
|
|
239
|
-
# ---------------------------------------------------------------------------
|
|
240
|
-
|
|
241
|
-
SEAT_NAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]*$")
|
|
242
|
-
LEADER_SEATS = ("chief-of-staff", "release-reliability-lead")
|
|
243
|
-
DEFAULT_MAX_LOAD = 28.0
|
|
244
|
-
DEFAULT_SEAT_TIMEOUT_SECS = 1200
|
|
245
|
-
GOVERNOR_CACHE_SECS = 300
|
|
246
|
-
TERM_GRACE_SECS = 15
|
|
247
|
-
LAUNCHAGENT_LABEL = "dev.autonomi.loki-company"
|
|
248
|
-
|
|
249
|
-
SEAT_TICK_PROMPT = (
|
|
250
|
-
"Run one iteration of your standing loop from your agent file. "
|
|
251
|
-
"Read the pulse (bash scripts/v10-pulse.sh) and docs/v10/BOARD.md. "
|
|
252
|
-
"Act only within your decision rights. "
|
|
253
|
-
"Write a short outcome to .loki/company/{seat}/last.json "
|
|
254
|
-
"(fields: ts, seat, outcome, actions, evidence) and append one line to "
|
|
255
|
-
".loki/company/{seat}/log.jsonl. Then stop."
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
def company_root():
|
|
260
|
-
return os.path.realpath(os.environ.get("LOKI_COMPANY_ROOT") or os.getcwd())
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
def _now_iso():
|
|
264
|
-
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def _pid_alive(pid):
|
|
268
|
-
try:
|
|
269
|
-
os.kill(pid, 0)
|
|
270
|
-
except ProcessLookupError:
|
|
271
|
-
return False
|
|
272
|
-
except PermissionError:
|
|
273
|
-
return True
|
|
274
|
-
except OSError:
|
|
275
|
-
return False
|
|
276
|
-
return True
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
def _read_pid(path):
|
|
280
|
-
"""First run of digits on line 1 (the v10-leader / lock format), else None."""
|
|
281
|
-
try:
|
|
282
|
-
with open(path) as f:
|
|
283
|
-
m = re.match(r"\s*(\d+)", f.readline())
|
|
284
|
-
except OSError:
|
|
285
|
-
return None
|
|
286
|
-
return int(m.group(1)) if m else None
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
def acquire_seat_lock(lock_path):
|
|
290
|
-
"""Per-seat flock on the lockfile; our PID is written into it for the cockpit.
|
|
291
|
-
|
|
292
|
-
Returns (file, holder_pid). The lock dies with the process, so a crashed
|
|
293
|
-
tick never leaves a stale lock. Caller closes the file to release.
|
|
294
|
-
"""
|
|
295
|
-
f = open(lock_path, "a+")
|
|
296
|
-
try:
|
|
297
|
-
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
298
|
-
except OSError:
|
|
299
|
-
f.seek(0)
|
|
300
|
-
m = re.match(r"\s*(\d+)", f.readline())
|
|
301
|
-
f.close()
|
|
302
|
-
return None, int(m.group(1)) if m else None
|
|
303
|
-
f.seek(0)
|
|
304
|
-
f.truncate()
|
|
305
|
-
f.write("%d\n" % os.getpid())
|
|
306
|
-
f.flush()
|
|
307
|
-
return f, os.getpid()
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
def governor_max_engineers():
|
|
311
|
-
"""max_engineers_next_hour from the usage governor, or None if unknown.
|
|
312
|
-
|
|
313
|
-
Same source and key the pulse reads (scripts/usage-governor.py --json,
|
|
314
|
-
report["governor"]["max_engineers_next_hour"]). Cached for a few minutes
|
|
315
|
-
so ten seats do not each rescan the transcripts.
|
|
316
|
-
"""
|
|
317
|
-
root = company_root()
|
|
318
|
-
cache = os.path.join(root, ".loki", "company", "governor-cache.json")
|
|
319
|
-
cmd_env = os.environ.get("LOKI_COMPANY_GOVERNOR_CMD") or os.environ.get("PULSE_GOVERNOR_CMD")
|
|
320
|
-
if not cmd_env:
|
|
321
|
-
cmd = [sys.executable, os.path.join(root, "scripts", "usage-governor.py"), "--json"]
|
|
322
|
-
if not os.path.exists(cmd[1]):
|
|
323
|
-
return None
|
|
324
|
-
else:
|
|
325
|
-
cmd = shlex.split(cmd_env)
|
|
326
|
-
try:
|
|
327
|
-
c = json.load(open(cache))
|
|
328
|
-
if cmd_env is None and time.time() - c["at"] < GOVERNOR_CACHE_SECS:
|
|
329
|
-
return c["max"]
|
|
330
|
-
except (OSError, ValueError, KeyError, TypeError):
|
|
331
|
-
pass
|
|
332
|
-
try:
|
|
333
|
-
out = subprocess.run(cmd, cwd=root, capture_output=True, text=True, timeout=90).stdout
|
|
334
|
-
val = (json.loads(out).get("governor") or {}).get("max_engineers_next_hour")
|
|
335
|
-
except (OSError, ValueError, AttributeError, subprocess.TimeoutExpired):
|
|
336
|
-
return None
|
|
337
|
-
if not isinstance(val, int):
|
|
338
|
-
return None
|
|
339
|
-
if cmd_env is None:
|
|
340
|
-
try:
|
|
341
|
-
with open(cache, "w") as f:
|
|
342
|
-
json.dump({"at": time.time(), "max": val}, f)
|
|
343
|
-
except OSError:
|
|
344
|
-
pass
|
|
345
|
-
return val
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
def _append_log(seat_dir, seat, outcome, detail=""):
|
|
349
|
-
entry = {"ts": _now_iso(), "seat": seat, "outcome": outcome}
|
|
350
|
-
if detail:
|
|
351
|
-
entry["detail"] = detail
|
|
352
|
-
with open(os.path.join(seat_dir, "log.jsonl"), "a") as f:
|
|
353
|
-
f.write(json.dumps(entry) + "\n")
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def tick_seat(name):
|
|
357
|
-
"""Run one headless tick of seat `name`. Returns (status, detail).
|
|
358
|
-
|
|
359
|
-
status is "ran", "skipped" or "error". Guards, in order: PAUSE file,
|
|
360
|
-
1-min load average, live v10-leader (chief-of-staff and
|
|
361
|
-
release-reliability-lead only), per-seat PID lock, usage governor.
|
|
362
|
-
"""
|
|
363
|
-
root = company_root()
|
|
364
|
-
if not SEAT_NAME_RE.match(name or "") or \
|
|
365
|
-
not os.path.isfile(os.path.join(root, ".claude", "agents", name + ".md")):
|
|
366
|
-
return "error", "unknown seat: %s" % name
|
|
367
|
-
cdir = os.path.join(root, ".loki", "company")
|
|
368
|
-
seat_dir = os.path.join(cdir, name)
|
|
369
|
-
os.makedirs(seat_dir, exist_ok=True)
|
|
370
|
-
|
|
371
|
-
def skip(why):
|
|
372
|
-
_append_log(seat_dir, name, "skipped", why)
|
|
373
|
-
return "skipped", why
|
|
374
|
-
|
|
375
|
-
if os.path.exists(os.path.join(cdir, "PAUSE")):
|
|
376
|
-
return skip("paused")
|
|
377
|
-
max_load = float(os.environ.get("LOKI_COMPANY_MAX_LOAD") or DEFAULT_MAX_LOAD)
|
|
378
|
-
load1 = os.getloadavg()[0]
|
|
379
|
-
if load1 > max_load:
|
|
380
|
-
return skip("load %.1f above %.1f" % (load1, max_load))
|
|
381
|
-
if name in LEADER_SEATS:
|
|
382
|
-
leader = _read_pid(os.path.join(root, ".loki", "v10-leader"))
|
|
383
|
-
if leader is not None and leader != os.getpid() and _pid_alive(leader):
|
|
384
|
-
return skip("live leader pid %d holds .loki/v10-leader" % leader)
|
|
385
|
-
lock, holder = acquire_seat_lock(os.path.join(seat_dir, "lock"))
|
|
386
|
-
if lock is None:
|
|
387
|
-
return skip("lock held by pid %s" % holder)
|
|
388
|
-
try:
|
|
389
|
-
if governor_max_engineers() == 0:
|
|
390
|
-
return skip("governor max engineers is 0")
|
|
391
|
-
return _run_claude_tick(root, name, seat_dir)
|
|
392
|
-
finally:
|
|
393
|
-
lock.truncate(0)
|
|
394
|
-
lock.close()
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
def _run_claude_tick(root, name, seat_dir):
|
|
398
|
-
timeout = float(os.environ.get("LOKI_SEAT_TIMEOUT_SECS") or DEFAULT_SEAT_TIMEOUT_SECS)
|
|
399
|
-
argv = [os.environ.get("LOKI_CLAUDE_BIN") or "claude", "-p", "--agent", name,
|
|
400
|
-
"--dangerously-skip-permissions", "--output-format", "json",
|
|
401
|
-
SEAT_TICK_PROMPT.format(seat=name)]
|
|
402
|
-
last = os.path.join(seat_dir, "last.json")
|
|
403
|
-
started = time.time()
|
|
404
|
-
try:
|
|
405
|
-
# Own session so a timeout can stop the tick's whole process group
|
|
406
|
-
# (claude spawns children) by the group id we created, never by name.
|
|
407
|
-
with open(os.path.join(seat_dir, "last.out"), "w") as out:
|
|
408
|
-
proc = subprocess.Popen(argv, cwd=root, stdin=subprocess.DEVNULL, stdout=out,
|
|
409
|
-
stderr=subprocess.STDOUT, start_new_session=True)
|
|
410
|
-
except OSError as e:
|
|
411
|
-
_append_log(seat_dir, name, "error", "cannot start claude: %s" % e)
|
|
412
|
-
return "error", "cannot start claude: %s" % e
|
|
413
|
-
outcome, detail = "ok", ""
|
|
414
|
-
try:
|
|
415
|
-
proc.wait(timeout=timeout)
|
|
416
|
-
if proc.returncode != 0:
|
|
417
|
-
outcome, detail = "error", "claude rc=%d" % proc.returncode
|
|
418
|
-
except subprocess.TimeoutExpired:
|
|
419
|
-
# TERM, grace, then ALWAYS KILL the group: a grandchild (bun test)
|
|
420
|
-
# can ignore TERM and outlive the leader.
|
|
421
|
-
try:
|
|
422
|
-
os.killpg(proc.pid, signal.SIGTERM)
|
|
423
|
-
proc.wait(timeout=TERM_GRACE_SECS)
|
|
424
|
-
except (ProcessLookupError, PermissionError, subprocess.TimeoutExpired):
|
|
425
|
-
pass
|
|
426
|
-
try:
|
|
427
|
-
os.killpg(proc.pid, signal.SIGKILL)
|
|
428
|
-
except (ProcessLookupError, PermissionError):
|
|
429
|
-
proc.kill() # not a group leader: at least stop the PID we started
|
|
430
|
-
proc.wait()
|
|
431
|
-
outcome, detail = "timeout", "exceeded %ds" % int(timeout)
|
|
432
|
-
fresh = os.path.exists(last) and os.path.getmtime(last) >= started - 1
|
|
433
|
-
if outcome != "ok" or not fresh:
|
|
434
|
-
if outcome == "ok":
|
|
435
|
-
outcome, detail = "no-report", "seat did not write last.json"
|
|
436
|
-
with open(last, "w") as f:
|
|
437
|
-
json.dump({"ts": _now_iso(), "seat": name, "outcome": outcome,
|
|
438
|
-
"actions": [], "evidence": detail}, f)
|
|
439
|
-
_append_log(seat_dir, name, outcome, detail)
|
|
440
|
-
return "ran", ("%s: %s" % (outcome, detail) if detail else outcome)
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
def default_seat_schedules():
|
|
444
|
-
"""Shipped default cadence for the ten company seats."""
|
|
445
|
-
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "company-seats.json")
|
|
446
|
-
with open(path) as f:
|
|
447
|
-
return json.load(f)
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
def seed_default_schedules():
|
|
451
|
-
"""Add default seat schedules missing from schedules.json. Returns count added."""
|
|
452
|
-
schedules = load_schedules()
|
|
453
|
-
have = {s.get("name") for s in schedules}
|
|
454
|
-
added = 0
|
|
455
|
-
for d in default_seat_schedules():
|
|
456
|
-
if d["name"] not in have:
|
|
457
|
-
schedules.append(dict(d))
|
|
458
|
-
added += 1
|
|
459
|
-
if added:
|
|
460
|
-
save_schedules(schedules)
|
|
461
|
-
return added
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
def _fire_minutes(cron_expr, start, span_minutes):
|
|
465
|
-
t = start
|
|
466
|
-
for _ in range(span_minutes):
|
|
467
|
-
if cron_matches(cron_expr, t):
|
|
468
|
-
yield t
|
|
469
|
-
t += timedelta(minutes=1)
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
def interval_minutes(cron_expr):
|
|
473
|
-
"""Smallest gap in minutes between consecutive fires (None if fewer than 2 in 8 days)."""
|
|
474
|
-
fires = list(_fire_minutes(cron_expr, datetime(2026, 1, 5, 0, 0), 8 * 1440))
|
|
475
|
-
gaps = [int((b - a).total_seconds() // 60) for a, b in zip(fires, fires[1:])]
|
|
476
|
-
return min(gaps) if gaps else None
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
def next_fire(cron_expr, after):
|
|
480
|
-
"""First fire strictly after `after` (naive datetime), within 8 days, else None."""
|
|
481
|
-
start = after.replace(second=0, microsecond=0) + timedelta(minutes=1)
|
|
482
|
-
return next(_fire_minutes(cron_expr, start, 8 * 1440), None)
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
def launchagent_path(home):
|
|
486
|
-
return os.path.join(home, "Library", "LaunchAgents", LAUNCHAGENT_LABEL + ".plist")
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
def write_launchagent(home, repo, loki_bin):
|
|
490
|
-
"""Write the LaunchAgent plist under `home`. Does not load it."""
|
|
491
|
-
logs = os.path.join(home, ".loki", "logs")
|
|
492
|
-
os.makedirs(logs, exist_ok=True)
|
|
493
|
-
plist = {
|
|
494
|
-
"Label": LAUNCHAGENT_LABEL,
|
|
495
|
-
"ProgramArguments": [loki_bin, "trigger", "daemon", "--loop"],
|
|
496
|
-
"WorkingDirectory": repo,
|
|
497
|
-
"EnvironmentVariables": {
|
|
498
|
-
"LOKI_COMPANY_ROOT": repo,
|
|
499
|
-
"PATH": os.environ.get("PATH", "/usr/local/bin:/usr/bin:/bin"),
|
|
500
|
-
"HOME": home,
|
|
501
|
-
},
|
|
502
|
-
"RunAtLoad": True,
|
|
503
|
-
"KeepAlive": True,
|
|
504
|
-
"ThrottleInterval": 30,
|
|
505
|
-
"StandardOutPath": os.path.join(logs, "company.out.log"),
|
|
506
|
-
"StandardErrorPath": os.path.join(logs, "company.err.log"),
|
|
507
|
-
}
|
|
508
|
-
path = launchagent_path(home)
|
|
509
|
-
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
510
|
-
with os.fdopen(os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600), "wb") as f:
|
|
511
|
-
plistlib.dump(plist, f)
|
|
512
|
-
os.chmod(path, 0o600)
|
|
513
|
-
return path
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
def remove_launchagent(home):
|
|
517
|
-
path = launchagent_path(home)
|
|
518
|
-
if not os.path.exists(path):
|
|
519
|
-
return False
|
|
520
|
-
os.unlink(path)
|
|
521
|
-
return True
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
def systemd_unit(repo, loki_bin):
|
|
525
|
-
return "\n".join([
|
|
526
|
-
"[Unit]",
|
|
527
|
-
"Description=Loki Mode company seats (schedule daemon)",
|
|
528
|
-
"",
|
|
529
|
-
"[Service]",
|
|
530
|
-
"WorkingDirectory=%s" % repo,
|
|
531
|
-
"Environment=LOKI_COMPANY_ROOT=%s" % repo,
|
|
532
|
-
"ExecStart=%s trigger daemon --loop" % loki_bin,
|
|
533
|
-
"Restart=always",
|
|
534
|
-
"RestartSec=30",
|
|
535
|
-
"",
|
|
536
|
-
"[Install]",
|
|
537
|
-
"WantedBy=default.target",
|
|
538
|
-
"",
|
|
539
|
-
])
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
def _launchctl(*args):
|
|
543
|
-
return subprocess.run(["launchctl"] + list(args), capture_output=True, text=True)
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
def install_company(no_load=False):
|
|
547
|
-
home = os.path.expanduser("~")
|
|
548
|
-
repo = company_root()
|
|
549
|
-
loki_bin = os.environ.get("LOKI_BIN") or shutil.which("loki") or "loki"
|
|
550
|
-
added = seed_default_schedules()
|
|
551
|
-
print("Seeded %d default seat schedule(s) into .loki/triggers/schedules.json" % added)
|
|
552
|
-
if sys.platform != "darwin":
|
|
553
|
-
print("NOT SUPPORTED: launchd install is macOS only. Equivalent systemd user unit "
|
|
554
|
-
"(~/.config/systemd/user/loki-company.service):\n")
|
|
555
|
-
print(systemd_unit(repo, loki_bin))
|
|
556
|
-
return 0
|
|
557
|
-
path = write_launchagent(home, repo, loki_bin)
|
|
558
|
-
print("Wrote %s" % path)
|
|
559
|
-
if not no_load:
|
|
560
|
-
dom = "gui/%d" % os.getuid()
|
|
561
|
-
_launchctl("bootout", dom, path)
|
|
562
|
-
r = _launchctl("bootstrap", dom, path)
|
|
563
|
-
if r.returncode != 0:
|
|
564
|
-
print("launchctl bootstrap failed: %s" % (r.stderr.strip() or r.stdout.strip()))
|
|
565
|
-
return 1
|
|
566
|
-
print("Loaded %s" % LAUNCHAGENT_LABEL)
|
|
567
|
-
return 0
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
def uninstall_company(no_load=False):
|
|
571
|
-
home = os.path.expanduser("~")
|
|
572
|
-
if sys.platform != "darwin":
|
|
573
|
-
print("NOT SUPPORTED: nothing to remove (launchd is macOS only).")
|
|
574
|
-
return 0
|
|
575
|
-
path = launchagent_path(home)
|
|
576
|
-
if not no_load and os.path.exists(path):
|
|
577
|
-
_launchctl("bootout", "gui/%d" % os.getuid(), path)
|
|
578
|
-
print("Removed %s" % path if remove_launchagent(home) else "Not installed: %s" % path)
|
|
579
|
-
return 0
|
|
580
|
-
|
|
581
|
-
|
|
582
210
|
def log_event(name, action_desc, status):
|
|
583
211
|
"""Log schedule fire to events.log."""
|
|
584
212
|
log_path = get_loki_dir() / "events.log"
|
|
@@ -621,20 +249,6 @@ def run_daemon(dry_run=False, once=False):
|
|
|
621
249
|
return True
|
|
622
250
|
|
|
623
251
|
|
|
624
|
-
def run_daemon_loop(max_passes=None):
|
|
625
|
-
"""Always-on mode: one pass at the top of every minute (launchd KeepAlive)."""
|
|
626
|
-
passes = 0
|
|
627
|
-
while max_passes is None or passes < max_passes:
|
|
628
|
-
try:
|
|
629
|
-
run_daemon(once=True)
|
|
630
|
-
except Exception: # keep the always-on loop alive across a bad pass
|
|
631
|
-
logging.exception("daemon pass failed")
|
|
632
|
-
passes += 1
|
|
633
|
-
if max_passes is not None and passes >= max_passes:
|
|
634
|
-
break
|
|
635
|
-
time.sleep(60 - time.time() % 60)
|
|
636
|
-
|
|
637
|
-
|
|
638
252
|
def list_schedules():
|
|
639
253
|
"""Print all configured schedules."""
|
|
640
254
|
schedules = load_schedules()
|
|
@@ -707,15 +321,6 @@ def main():
|
|
|
707
321
|
# daemon - run one pass
|
|
708
322
|
daemon_parser = subparsers.add_parser("daemon", help="Run one pass of schedule checks")
|
|
709
323
|
daemon_parser.add_argument("--dry-run", action="store_true", help="Preview without running")
|
|
710
|
-
daemon_parser.add_argument("--loop", action="store_true", help="Keep running, one pass per minute")
|
|
711
|
-
daemon_parser.add_argument("--max-passes", type=int, default=None, help=argparse.SUPPRESS)
|
|
712
|
-
|
|
713
|
-
tick_parser = subparsers.add_parser("tick", help="Run one headless tick of a company seat")
|
|
714
|
-
tick_parser.add_argument("seat")
|
|
715
|
-
subparsers.add_parser("seed", help="Add default company-seat schedules")
|
|
716
|
-
for cmd_name in ("install", "uninstall"):
|
|
717
|
-
ip = subparsers.add_parser(cmd_name, help="%s the always-on company LaunchAgent" % cmd_name.capitalize())
|
|
718
|
-
ip.add_argument("--no-load", action="store_true", help="Write/remove the plist without launchctl")
|
|
719
324
|
|
|
720
325
|
# list
|
|
721
326
|
subparsers.add_parser("list", help="List configured schedules")
|
|
@@ -742,20 +347,7 @@ def main():
|
|
|
742
347
|
|
|
743
348
|
if args.command == "daemon" or args.command is None:
|
|
744
349
|
dry_run = getattr(args, "dry_run", False)
|
|
745
|
-
|
|
746
|
-
run_daemon_loop(getattr(args, "max_passes", None))
|
|
747
|
-
else:
|
|
748
|
-
run_daemon(dry_run=dry_run, once=True)
|
|
749
|
-
elif args.command == "tick":
|
|
750
|
-
status, detail = tick_seat(args.seat)
|
|
751
|
-
print("%s: %s" % (status, detail))
|
|
752
|
-
sys.exit(1 if status == "error" else 0)
|
|
753
|
-
elif args.command == "seed":
|
|
754
|
-
print("Added %d default seat schedule(s)" % seed_default_schedules())
|
|
755
|
-
elif args.command == "install":
|
|
756
|
-
sys.exit(install_company(args.no_load))
|
|
757
|
-
elif args.command == "uninstall":
|
|
758
|
-
sys.exit(uninstall_company(args.no_load))
|
|
350
|
+
run_daemon(dry_run=dry_run, once=True)
|
|
759
351
|
elif args.command == "list":
|
|
760
352
|
list_schedules()
|
|
761
353
|
elif args.command == "add":
|
package/autonomy/tui.sh
CHANGED
|
@@ -24,7 +24,7 @@ TUI_HAS_COLOR=false
|
|
|
24
24
|
TUI_COLS=80
|
|
25
25
|
if [ -t 1 ]; then
|
|
26
26
|
TUI_HAS_COLOR=true
|
|
27
|
-
#
|
|
27
|
+
# tput can print 0 or nothing on a pty with no size; fall back to COLUMNS, then 80
|
|
28
28
|
TUI_COLS=$(tput cols 2>/dev/null || true)
|
|
29
29
|
case "$TUI_COLS" in '' | *[!0-9]*) TUI_COLS="${COLUMNS:-80}" ;; esac
|
|
30
30
|
case "$TUI_COLS" in '' | *[!0-9]*) TUI_COLS=80 ;; esac
|
package/autonomy/verify.sh
CHANGED
|
@@ -71,7 +71,7 @@ VERIFY_EXIT_ERROR=3
|
|
|
71
71
|
VERIFY_EXIT_USAGE=64
|
|
72
72
|
VERIFY_SCHEMA_VERSION="1.0"
|
|
73
73
|
|
|
74
|
-
#
|
|
74
|
+
# verify runs from the tree under review (tree="."), and a bare
|
|
75
75
|
# `python3 -c` / `python3 -` puts that cwd first on sys.path, so a planted
|
|
76
76
|
# json.py in the reviewed tree replaced the stdlib module and forged recorded
|
|
77
77
|
# gates (a dependency_audit with high CVEs read as pass). The inline readers
|
|
@@ -3161,7 +3161,7 @@ PYEOF
|
|
|
3161
3161
|
_verify_receipt_digest() {
|
|
3162
3162
|
local rid pj lib rc=0
|
|
3163
3163
|
rid="$(cat .loki/state/last-proof-id.txt 2>/dev/null || true)"
|
|
3164
|
-
#
|
|
3164
|
+
# a missing or unusable run-id pointer must not silently skip the receipt check.
|
|
3165
3165
|
# Proofs on disk with no pointer to one is NOT VERIFIED; a tree that never recorded a
|
|
3166
3166
|
# proof (nothing to verify) says so aloud and does not claim a receipt check.
|
|
3167
3167
|
if [ -z "$rid" ]; then
|
|
@@ -3199,7 +3199,7 @@ sys.exit(0 if ok else 3)" "$lib" "$pj" 2>/dev/null || rc=$?
|
|
|
3199
3199
|
declare -f _deploy_receipt_verdict >/dev/null 2>&1 || { echo "attestation: UNCHECKED (run via loki verify)"; return 1; }
|
|
3200
3200
|
local att
|
|
3201
3201
|
att="$(_deploy_receipt_verdict "$pj")"
|
|
3202
|
-
#
|
|
3202
|
+
# UNSIGNED is never a pass; --allow-unsigned / LOKI_VERIFY_ALLOW_UNSIGNED=1 accepts it, said aloud.
|
|
3203
3203
|
if [ "$att" = "UNSIGNED" ]; then
|
|
3204
3204
|
if [ "${VERIFY_ALLOW_UNSIGNED:-0}" = "1" ] || [ "${LOKI_VERIFY_ALLOW_UNSIGNED:-}" = "1" ]; then
|
|
3205
3205
|
printf 'attestation: UNSIGNED (accepted by --allow-unsigned; integrity not attested)\n'
|
|
@@ -3209,7 +3209,7 @@ sys.exit(0 if ok else 3)" "$lib" "$pj" 2>/dev/null || rc=$?
|
|
|
3209
3209
|
return 1
|
|
3210
3210
|
fi
|
|
3211
3211
|
printf 'attestation: %s\n' "$att"
|
|
3212
|
-
#
|
|
3212
|
+
# UNCHECKED (a well-formed token whose kid no local key matches, or a check that
|
|
3213
3213
|
# could not run) is not a pass: anyone can mint a foreign-kid token. rc 2, VERDICT not VERIFIED.
|
|
3214
3214
|
if [ "$att" = "UNCHECKED" ]; then
|
|
3215
3215
|
echo " Not verified: no local key matches this attestation, so who signed it is unproven."
|
package/completions/loki.bash
CHANGED
|
@@ -115,11 +115,6 @@ _loki_completion() {
|
|
|
115
115
|
COMPREPLY=( $(compgen -W "${telemetry_cmds}" -- "$cur") )
|
|
116
116
|
;;
|
|
117
117
|
|
|
118
|
-
trigger)
|
|
119
|
-
local trigger_cmds="start stop status list add remove daemon install uninstall test logs help --dry-run --loop --no-load"
|
|
120
|
-
COMPREPLY=( $(compgen -W "${trigger_cmds}" -- "$cur") )
|
|
121
|
-
;;
|
|
122
|
-
|
|
123
118
|
agent)
|
|
124
119
|
local agent_cmds="list info run start help"
|
|
125
120
|
COMPREPLY=( $(compgen -W "${agent_cmds}" -- "$cur") )
|
package/dashboard/__init__.py
CHANGED
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -330,5 +330,4 @@ docker/ Container distribution (Dockerfiles, compose)
|
|
|
330
330
|
---
|
|
331
331
|
|
|
332
332
|
For deeper detail, see `SKILL.md` (core skill), `skills/quality-gates.md` (the
|
|
333
|
-
canonical 8-gate table),
|
|
334
|
-
pattern), and the `references/` directory.
|
|
333
|
+
canonical 8-gate table), and the `references/` directory.
|
package/docs/CLI-REFERENCE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLI reference
|
|
2
2
|
|
|
3
|
-
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.
|
|
3
|
+
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.75.1. Do not edit by hand.
|
|
4
4
|
Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for the full inventory.
|
|
5
5
|
|
|
6
6
|
| Command | Description |
|
package/docs/COMPONENTS.md
CHANGED
|
@@ -349,7 +349,7 @@ skill file.
|
|
|
349
349
|
- `model-selection.md`, `providers.md`, `healing.md`, `testing.md`,
|
|
350
350
|
`production.md`, `troubleshooting.md`, `agents.md`, `artifacts.md`,
|
|
351
351
|
`patterns-advanced.md`, `parallel-workflows.md`, `github-integration.md`,
|
|
352
|
-
`
|
|
352
|
+
`memory.md`, `documentation.md`, `compound-learning.md`,
|
|
353
353
|
`magic-modules.md`, `mirofish-integration.md`, `openspec-integration.md`.
|
|
354
354
|
|
|
355
355
|
**Dependencies:**
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Loki Mode, by [Autonomi](https://www.autonomi.dev/), turns a spec or an issue into a verified pull request with a receipt you can check. This page covers installing it, a first run, and the options around it.
|
|
4
4
|
|
|
5
|
-
**Version:** v11.
|
|
5
|
+
**Version:** v11.75.1
|
|
6
6
|
|
|
7
7
|
**Engine note:** `loki start` is the primary command. `loki start`, `loki "<task>"`, `loki owner/repo#N` and `loki quick` all run the Loki 10 engine, whatever the flag order or input form (PRD file, issue ref, task or one-word brief). The previous loop runs only on `--engine legacy`, `LOKI_ENGINE=legacy`, or for one call when a flag Loki 10 cannot honor is present. See the [user guide](v10/GUIDE.md) and the [5-minute quickstart](QUICKSTART.md).
|
|
8
8
|
|
package/docs/ROUTING-EVIDENCE.md
CHANGED
|
@@ -16,8 +16,7 @@ or in any external claim until it is re-derived (issue 210).
|
|
|
16
16
|
- Claude Opus 4.6: resolved 75.6 at $275.76 total.
|
|
17
17
|
- Derived: an open model matching frontier accuracy at ~7.5x lower cost.
|
|
18
18
|
- Source: cited from the cost-context comment that previously lived in
|
|
19
|
-
providers/opencode.sh
|
|
20
|
-
item 3.
|
|
19
|
+
providers/opencode.sh.
|
|
21
20
|
|
|
22
21
|
## opencode viability
|
|
23
22
|
|
package/docs/V10-RESEARCH.md
CHANGED
|
@@ -6,7 +6,7 @@ Compiled 2026-09-25 from 60+ sources. Tags:
|
|
|
6
6
|
- [VEN] vendor or industry report
|
|
7
7
|
- UNVERIFIED: secondary source only, or the primary page could not be fetched
|
|
8
8
|
|
|
9
|
-
Vendor figures are vendor claims.
|
|
9
|
+
Vendor figures are vendor claims. Re-check a source before quoting it publicly.
|
|
10
10
|
|
|
11
11
|
## The findings that shape v10
|
|
12
12
|
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# Architecture reference: gates, healing, memory, harness knobs
|
|
2
2
|
|
|
3
|
-
Moved out of CLAUDE.md (S-78) to keep the root file short. Content unchanged
|
|
4
|
-
except where noted.
|
|
5
|
-
|
|
6
3
|
## Quality Gates (8 gates; see `skills/quality-gates.md` for the canonical table)
|
|
7
4
|
1. Static analysis (CodeQL, ESLint)
|
|
8
5
|
2. Test suite pass/fail (red blocks; coverage % not measured in this release)
|
|
@@ -15,10 +12,9 @@ except where noted.
|
|
|
15
12
|
|
|
16
13
|
Conditional auditor (not numbered): Backward-compatibility / legacy-healing-auditor (healing mode only - behavioral preservation, v6.67.0).
|
|
17
14
|
|
|
18
|
-
Note
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
model explicitly rather than inherit the session model.
|
|
15
|
+
Note: unanimous APPROVE is required, and a reproduced blocking finding always
|
|
16
|
+
vetoes regardless of vote count. Every council/review agent pins its model
|
|
17
|
+
explicitly rather than inheriting the session model.
|
|
22
18
|
|
|
23
19
|
## Legacy System Healing (introduced v6.67.0)
|
|
24
20
|
- **Status**: Still active, no breaking changes since v6.67.0. Note: in v7.4.20 the `legacy-healing-auditor` reviewer was gated on healing-mode signals to avoid firing on non-healing changes.
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# Project structure and codebase knowledge graph
|
|
2
2
|
|
|
3
|
-
Moved out of CLAUDE.md (S-78) to keep the root file short. Content unchanged.
|
|
4
|
-
|
|
5
3
|
## Project Structure
|
|
6
4
|
|
|
7
5
|
```
|
|
@@ -37,7 +35,6 @@ skills/ # On-demand skill modules (v3.0 architecture)
|
|
|
37
35
|
patterns-advanced.md # OptiMind, k8s-valkey, Constitutional AI
|
|
38
36
|
parallel-workflows.md # Git worktrees, parallel streams, auto-merge
|
|
39
37
|
github-integration.md # GitHub issue import, PR creation, notifications
|
|
40
|
-
sdlc-fleet.md # Six-role standing fleet pattern (see CLAUDE.md)
|
|
41
38
|
references/ # Detailed documentation (21 files)
|
|
42
39
|
legacy-healing-patterns.md # Amazon AGI Lab: friction, adapters, archaeology
|
|
43
40
|
openai-patterns.md # OpenAI Agents SDK: guardrails, tripwires, handoffs
|
|
@@ -64,7 +61,7 @@ events/ # Unified Event Bus (v5.17.0)
|
|
|
64
61
|
emit.sh # Bash helper for emitting events
|
|
65
62
|
docs/ # Architecture documentation
|
|
66
63
|
SYNERGY-ROADMAP.md # 5-pillar tool integration architecture
|
|
67
|
-
v10/ #
|
|
64
|
+
v10/ # User guides: GUIDE.md, GUIDE-MODERNIZE.md, CLI-MODERN.md, DEPS.md
|
|
68
65
|
dev/ # Developer reference docs (this file and its siblings)
|
|
69
66
|
autonomy/ # Runtime and autonomous execution
|
|
70
67
|
context-tracker.py # Context window usage tracking
|