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.
Files changed (166) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +2 -3
  3. package/VERSION +1 -1
  4. package/autonomy/completion-council.sh +10 -10
  5. package/autonomy/council-v2.sh +4 -4
  6. package/autonomy/grill.sh +1 -1
  7. package/autonomy/lib/browser-open.sh +1 -1
  8. package/autonomy/lib/chat.sh +1 -1
  9. package/autonomy/lib/cockpit-render.sh +1 -1
  10. package/autonomy/lib/done-recognition.sh +4 -4
  11. package/autonomy/lib/efficiency_cost.py +1 -1
  12. package/autonomy/lib/engine10-push.sh +2 -2
  13. package/autonomy/lib/prd-enrich.sh +1 -1
  14. package/autonomy/lib/proof-check.sh +1 -1
  15. package/autonomy/lib/proof-generator.py +3 -3
  16. package/autonomy/lib/proof-pr.sh +1 -1
  17. package/autonomy/lib/skill-link-heal.sh +1 -1
  18. package/autonomy/lib/voter-agents.sh +1 -1
  19. package/autonomy/loki +26 -37
  20. package/autonomy/prd-checklist.sh +1 -1
  21. package/autonomy/receipt_jwt.py +1 -1
  22. package/autonomy/run.sh +28 -28
  23. package/autonomy/trigger-schedule.py +6 -414
  24. package/autonomy/tui.sh +1 -1
  25. package/autonomy/verify.sh +4 -4
  26. package/completions/loki.bash +0 -5
  27. package/dashboard/__init__.py +1 -1
  28. package/docs/ARCHITECTURE.md +1 -2
  29. package/docs/CLI-REFERENCE.md +1 -1
  30. package/docs/COMPONENTS.md +1 -1
  31. package/docs/INSTALLATION.md +1 -1
  32. package/docs/ROUTING-EVIDENCE.md +1 -2
  33. package/docs/V10-RESEARCH.md +1 -1
  34. package/docs/dev/architecture-reference.md +3 -7
  35. package/docs/dev/project-structure.md +1 -4
  36. package/docs/dev/release-checklist.md +13 -31
  37. package/docs/research-2026-07/OBSERVABILITY-TOOL-SPEC.md +1 -1
  38. package/docs/v11/MARKET-SLICES.md +1 -1
  39. package/docs/v11/REGISTRIES.md +1 -1
  40. package/docs/v11/ROUTER-1.md +2 -2
  41. package/loki-ts/dist/loki.js +2 -2
  42. package/mcp/__init__.py +1 -1
  43. package/package.json +2 -2
  44. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  45. package/skills/extending.md +2 -3
  46. package/skills/factory-operations.md +11 -18
  47. package/skills/release-cadence.md +2 -2
  48. package/web-app/dist/assets/{AdminPage-jkjilfbl.js → AdminPage-xKgtIhk2.js} +1 -1
  49. package/web-app/dist/assets/{Avatar-bd5JWkSJ.js → Avatar-Q14AHox9.js} +1 -1
  50. package/web-app/dist/assets/{Badge-DVRit2Xw.js → Badge-yk5M-lip.js} +1 -1
  51. package/web-app/dist/assets/{Button-BNAB-pVZ.js → Button-ynznk0At.js} +1 -1
  52. package/web-app/dist/assets/{CockpitPage-DpG29h_w.js → CockpitPage-C8B765za.js} +1 -1
  53. package/web-app/dist/assets/{ComparePage-Dasg5V41.js → ComparePage-DqzmWKnf.js} +1 -1
  54. package/web-app/dist/assets/{ErrorBoundary-DJojse6b.js → ErrorBoundary-DdBPd_Lq.js} +1 -1
  55. package/web-app/dist/assets/{EvidenceReceiptPanel-Blxw7_u1.js → EvidenceReceiptPanel-jb4Tp38b.js} +1 -1
  56. package/web-app/dist/assets/{GitHubIssuesPanel-BBvSFj80.js → GitHubIssuesPanel-xJQiLcxs.js} +1 -1
  57. package/web-app/dist/assets/{GitHubPRsPanel-BCIKeSBg.js → GitHubPRsPanel-84W3Dhk_.js} +1 -1
  58. package/web-app/dist/assets/{HomePage-C7d__oNG.js → HomePage-St1Xl2tu.js} +2 -2
  59. package/web-app/dist/assets/{LoginPage-BXljL3yY.js → LoginPage-jRt0NUHb.js} +1 -1
  60. package/web-app/dist/assets/{MagicPage-Xjkts_yE.js → MagicPage-CQLFe7LR.js} +1 -1
  61. package/web-app/dist/assets/{MetricsPage-kQvVp-j2.js → MetricsPage-zzX9eTC5.js} +1 -1
  62. package/web-app/dist/assets/{NotFoundPage-D-4ZlEaO.js → NotFoundPage-DDu023SY.js} +1 -1
  63. package/web-app/dist/assets/{ProjectPage-DfzR5RXP.js → ProjectPage-D60UURaH.js} +3 -3
  64. package/web-app/dist/assets/{ProjectsPage-DFDsWzkV.js → ProjectsPage-DqVsKxlt.js} +1 -1
  65. package/web-app/dist/assets/{SettingsPage-CLPIoYT3.js → SettingsPage-G7pPKvo0.js} +1 -1
  66. package/web-app/dist/assets/{ShowcasePage-PZKkyGeY.js → ShowcasePage-CrU_aKgS.js} +1 -1
  67. package/web-app/dist/assets/{SystemSettingsPage-BsBlcnzo.js → SystemSettingsPage-ClKGQFsB.js} +1 -1
  68. package/web-app/dist/assets/{TeamsPage-BSjgNhoU.js → TeamsPage-B530vSNe.js} +1 -1
  69. package/web-app/dist/assets/{TemplatesPage-W2KeAzbg.js → TemplatesPage-COmv_Cqd.js} +1 -1
  70. package/web-app/dist/assets/{TerminalOutput-BjroTiCC.js → TerminalOutput-CufG9YzF.js} +1 -1
  71. package/web-app/dist/assets/{activity-BXZ5YYes.js → activity-Ct1Updr6.js} +1 -1
  72. package/web-app/dist/assets/{bell-D1urp5pa.js → bell-rcvhQuGr.js} +1 -1
  73. package/web-app/dist/assets/{bot-Chedf2bF.js → bot-DzeqEI-b.js} +1 -1
  74. package/web-app/dist/assets/{check-DhaCuOCV.js → check-zacnZHyC.js} +1 -1
  75. package/web-app/dist/assets/{chevron-left-D7BpQyA6.js → chevron-left-Lxnkl8Qg.js} +1 -1
  76. package/web-app/dist/assets/{circle-alert-DnGvQ_AQ.js → circle-alert-DoqSdQdB.js} +1 -1
  77. package/web-app/dist/assets/{clock-CqfxWhHM.js → clock-B7bJcVIC.js} +1 -1
  78. package/web-app/dist/assets/{cloud-BKxp5AWB.js → cloud-DdA9Bu6q.js} +1 -1
  79. package/web-app/dist/assets/{code-xml-BDQ6GBtt.js → code-xml-CAZEZ9T8.js} +1 -1
  80. package/web-app/dist/assets/{database-BmpEx1y_.js → database-Da2GkEIN.js} +1 -1
  81. package/web-app/dist/assets/{dollar-sign-DYNY9R_C.js → dollar-sign-DjDiG-37.js} +1 -1
  82. package/web-app/dist/assets/{file-code-corner-BJi0IBFd.js → file-code-corner-CSL-Jij-.js} +1 -1
  83. package/web-app/dist/assets/{file-plus-Dqyb2bcQ.js → file-plus-02DRA8pV.js} +1 -1
  84. package/web-app/dist/assets/{globe-DPh4RWfM.js → globe-CGyB90kK.js} +1 -1
  85. package/web-app/dist/assets/{hammer-B3xnuoA1.js → hammer-BaQB96TG.js} +1 -1
  86. package/web-app/dist/assets/{index-BpX8DcTr.js → index-lcoI1_Kk.js} +3 -3
  87. package/web-app/dist/assets/{layers-BRCmyCW8.js → layers-BWJBvoeg.js} +1 -1
  88. package/web-app/dist/assets/{loader-circle-n07Pky0M.js → loader-circle-CdcI0FeS.js} +1 -1
  89. package/web-app/dist/assets/{lock-BWqOuQmS.js → lock-BBkw5NJx.js} +1 -1
  90. package/web-app/dist/assets/{package-BJlB_Z-Z.js → package-BpPRO3LM.js} +1 -1
  91. package/web-app/dist/assets/{plus-oH17BZu2.js → plus-D7XbyK29.js} +1 -1
  92. package/web-app/dist/assets/{refresh-cw-CPQ476LL.js → refresh-cw-DLG3hyAi.js} +1 -1
  93. package/web-app/dist/assets/{rotate-ccw-7hLvOcnS.js → rotate-ccw-Bqau4R_S.js} +1 -1
  94. package/web-app/dist/assets/{scroll-text-BCmLauQn.js → scroll-text-BtIL3cNk.js} +1 -1
  95. package/web-app/dist/assets/{server-DfTfvW84.js → server-tZCmZ4yH.js} +1 -1
  96. package/web-app/dist/assets/{shield-alert-CaeGRq19.js → shield-alert-K5JXzFKg.js} +1 -1
  97. package/web-app/dist/assets/{thumbs-up-Dz-l2GPC.js → thumbs-up-CHCVyJdw.js} +1 -1
  98. package/web-app/dist/assets/{trash-2-B-OsDmSo.js → trash-2-DncNxVq0.js} +1 -1
  99. package/web-app/dist/assets/{trending-down-CXTFOxzq.js → trending-down-D8Bp0azX.js} +1 -1
  100. package/web-app/dist/assets/{trending-up-FQEdqr3v.js → trending-up-AixETdKR.js} +1 -1
  101. package/web-app/dist/assets/{upload-CacWSNTg.js → upload-VCbjFn2H.js} +1 -1
  102. package/web-app/dist/assets/{usePolling-cIjZ9qd2.js → usePolling-CQvKD2Jj.js} +1 -1
  103. package/web-app/dist/assets/{user-DAouWFHi.js → user-SK-YguQ6.js} +1 -1
  104. package/web-app/dist/index.html +1 -1
  105. package/autonomy/company-seats.json +0 -82
  106. package/docs/COMPETITIVE-INTEL-2026-09.md +0 -444
  107. package/docs/FOUNDER-DECISIONS-TRUST-SEMANTICS-2026-08-01.md +0 -145
  108. package/docs/FOUNDER-STATUS-AND-DECISIONS-2026-07-01.md +0 -132
  109. package/docs/LOKI-10-BUILD-PROMPT.md +0 -350
  110. package/docs/STRATEGY-2026-2028.md +0 -124
  111. package/docs/V10-VISION.md +0 -161
  112. package/docs/v10/ADOPTION-PLAN.md +0 -375
  113. package/docs/v10/ARCHITECT-CUTS.md +0 -744
  114. package/docs/v10/BACKLOG.md +0 -231
  115. package/docs/v10/BOARD.md +0 -1403
  116. package/docs/v10/CONTROL-PLANE.md +0 -120
  117. package/docs/v10/CONTROL.md +0 -36
  118. package/docs/v10/CP-ASK-PLAN.md +0 -85
  119. package/docs/v10/CP-ENTERPRISE-UI.md +0 -537
  120. package/docs/v10/D51-PHASE-B.md +0 -120
  121. package/docs/v10/D61-SPEED.md +0 -105
  122. package/docs/v10/D63-CARDS.md +0 -354
  123. package/docs/v10/DECISIONS.md +0 -915
  124. package/docs/v10/E2E-D65.md +0 -123
  125. package/docs/v10/ENGINE-LAWS.md +0 -163
  126. package/docs/v10/ENGINE-MEASURE.md +0 -69
  127. package/docs/v10/ENGINE.md +0 -743
  128. package/docs/v10/FAILURE-CLASSES.md +0 -2026
  129. package/docs/v10/FC21B-FC22-PLAN.md +0 -107
  130. package/docs/v10/FIRST-RUN-GATE.md +0 -39
  131. package/docs/v10/FOUNDER-QUEUE.md +0 -29
  132. package/docs/v10/GAP-RESEARCH-1003.md +0 -32
  133. package/docs/v10/GUARDS.md +0 -883
  134. package/docs/v10/L0-WAVE1.md +0 -81
  135. package/docs/v10/LEGACY-REMOVAL.md +0 -408
  136. package/docs/v10/MEDIUM-ANALYSIS.md +0 -346
  137. package/docs/v10/METRICS.md +0 -645
  138. package/docs/v10/MODERNIZE.md +0 -437
  139. package/docs/v10/OPENCODE-PARITY-PLAN.md +0 -84
  140. package/docs/v10/OPERATING-MODEL.md +0 -60
  141. package/docs/v10/PLAN-V12.md +0 -896
  142. package/docs/v10/PROGRESS.md +0 -2735
  143. package/docs/v10/RELEASE-11.md +0 -77
  144. package/docs/v10/RETRO-2026.md +0 -650
  145. package/docs/v10/ROADMAP-D63.md +0 -35
  146. package/docs/v10/SCALE.md +0 -140
  147. package/docs/v10/SCORECARD-PLAN.md +0 -250
  148. package/docs/v10/SWARM-PROMPT.md +0 -98
  149. package/docs/v10/SWARM.md +0 -293
  150. package/docs/v10/cp-redesign/PARITY.md +0 -188
  151. package/docs/v10/cp-redesign/legacy/INDEX.md +0 -1167
  152. package/docs/v10/roadmap-10/01-measurement.md +0 -113
  153. package/docs/v10/roadmap-10/02-cost.md +0 -75
  154. package/docs/v10/roadmap-10/03-speed.md +0 -65
  155. package/docs/v10/roadmap-10/04-first-run.md +0 -61
  156. package/docs/v10/roadmap-10/05-deploy.md +0 -61
  157. package/docs/v10/roadmap-10/06-stability.md +0 -64
  158. package/docs/v10/roadmap-10/07-quality.md +0 -57
  159. package/docs/v10/roadmap-10/08-proof.md +0 -48
  160. package/docs/v10/roadmap-10/09-safety.md +0 -74
  161. package/docs/v10/roadmap-10/README.md +0 -1464
  162. package/skills/sdlc-fleet.md +0 -170
  163. package/tools/cockpit/SPEC.md +0 -275
  164. package/tools/cockpit/index.html +0 -146
  165. package/tools/cockpit/roi-basis.json +0 -33
  166. 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 time
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
- if is_seat_action(schedule):
212
- cmd = build_seat_command(schedule)
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.DEVNULL if extra else subprocess.PIPE,
227
- stderr=subprocess.DEVNULL if extra else subprocess.PIPE,
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
- if getattr(args, "loop", False):
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
- # FC-10: tput can print 0 or nothing on a pty with no size; fall back to COLUMNS, then 80
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
@@ -71,7 +71,7 @@ VERIFY_EXIT_ERROR=3
71
71
  VERIFY_EXIT_USAGE=64
72
72
  VERIFY_SCHEMA_VERSION="1.0"
73
73
 
74
- # S-216: verify runs from the tree under review (tree="."), and a bare
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
- # D76: a missing or unusable run-id pointer must not silently skip the receipt check.
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
- # D47: UNSIGNED is never a pass; --allow-unsigned / LOKI_VERIFY_ALLOW_UNSIGNED=1 accepts it, said aloud.
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
- # D76: UNCHECKED (a well-formed token whose kid no local key matches, or a check that
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."
@@ -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") )
@@ -7,6 +7,6 @@ Modules:
7
7
  control: Session control helpers (status, atomic writes, origin check)
8
8
  """
9
9
 
10
- __version__ = "11.74.2"
10
+ __version__ = "11.75.1"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -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), `skills/sdlc-fleet.md` (the standing SDLC fleet
334
- pattern), and the `references/` directory.
333
+ canonical 8-gate table), and the `references/` directory.
@@ -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.74.2. Do not edit by hand.
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 |
@@ -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
- `sdlc-fleet.md`, `memory.md`, `documentation.md`, `compound-learning.md`,
352
+ `memory.md`, `documentation.md`, `compound-learning.md`,
353
353
  `magic-modules.md`, `mirofish-integration.md`, `openspec-integration.md`.
354
354
 
355
355
  **Dependencies:**
@@ -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.74.2
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
 
@@ -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; competitive context in docs/COMPETITIVE-INTEL-2026-09.md
20
- item 3.
19
+ providers/opencode.sh.
21
20
 
22
21
  ## opencode viability
23
22
 
@@ -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. Treat this as the evidence behind the design rules in `docs/LOKI-10-BUILD-PROMPT.md` section 5c. Re-check a source before quoting it publicly.
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 (v10): `docs/v10/DECISIONS.md` D12 refines the review rule above -- unanimous
19
- APPROVE is still required, and a reproduced blocking finding always vetoes
20
- regardless of vote count. D13 requires every council/review agent to pin its
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/ # v10 program: OPERATING-MODEL.md, DECISIONS.md, BOARD.md, BACKLOG.md, PROGRESS.md
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