@smilintux/skcapstone 0.9.0 → 0.12.5

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 (284) hide show
  1. package/.env.example +10 -4
  2. package/.github/workflows/ci.yml +2 -2
  3. package/.github/workflows/publish.yml +9 -2
  4. package/.openclaw-workspace.json +2 -2
  5. package/CLAUDE.md +37 -0
  6. package/MISSION.md +17 -2
  7. package/README.md +282 -3
  8. package/docker/Dockerfile +7 -7
  9. package/docker/compose-templates/dev-team.yml +12 -12
  10. package/docker/compose-templates/mini-team.yml +9 -9
  11. package/docker/compose-templates/ops-team.yml +10 -10
  12. package/docker/compose-templates/research-team.yml +10 -10
  13. package/docker/entrypoint.sh +4 -4
  14. package/docs/ADR-optional-integration-backbone.md +181 -0
  15. package/docs/ARCHITECTURE.md +186 -43
  16. package/docs/BOND_WITH_GROK.md +6 -6
  17. package/docs/CUSTOM_AGENT.md +278 -1
  18. package/docs/DREAMING.md +70 -0
  19. package/docs/GETTING_STARTED.md +10 -7
  20. package/docs/QUICKSTART.md +10 -6
  21. package/docs/SKJOULE_ARCHITECTURE.md +3 -3
  22. package/docs/SOUL_SWAPPER.md +5 -5
  23. package/docs/hammertime-audit.md +402 -0
  24. package/docs/sk-integration-HANDOFF.md +117 -0
  25. package/docs/skscheduler.md +155 -0
  26. package/docs/superpowers/examples/jobs.yaml +31 -0
  27. package/docs/superpowers/plans/2026-06-08-skscheduler.md +1265 -0
  28. package/docs/superpowers/specs/2026-06-08-skscheduler-design.md +186 -0
  29. package/examples/custom-bond-template.json +1 -1
  30. package/examples/grok-feb.json +1 -1
  31. package/examples/queen-ava-feb.json +1 -1
  32. package/launchd/com.skcapstone.daemon.plist +52 -0
  33. package/launchd/com.skcapstone.memory-compress.plist +45 -0
  34. package/launchd/com.skcapstone.skcomms-heartbeat.plist +33 -0
  35. package/launchd/com.skcapstone.skcomms-queue-drain.plist +34 -0
  36. package/launchd/install-launchd.sh +156 -0
  37. package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
  38. package/package.json +1 -1
  39. package/pyproject.toml +16 -10
  40. package/scripts/archive-sessions.sh +95 -0
  41. package/scripts/check-updates.py +4 -4
  42. package/scripts/install-bundle.sh +8 -8
  43. package/scripts/install.ps1 +12 -11
  44. package/scripts/install.sh +196 -11
  45. package/scripts/model-fallback-monitor.sh +102 -0
  46. package/scripts/notion-api.py +259 -0
  47. package/scripts/nvidia-proxy.mjs +908 -0
  48. package/scripts/proxy-monitor.sh +89 -0
  49. package/scripts/refresh-anthropic-token.sh +172 -0
  50. package/scripts/release.sh +98 -0
  51. package/scripts/session-to-memory.py +219 -0
  52. package/scripts/skgateway.mjs +856 -0
  53. package/scripts/telegram-catchup-all.sh +147 -0
  54. package/scripts/verify_install.sh +2 -2
  55. package/scripts/wargov-ufo-capture/README.md +43 -0
  56. package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
  57. package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
  58. package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
  59. package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
  60. package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
  61. package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
  62. package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
  63. package/scripts/watch-anthropic-token.sh +212 -0
  64. package/scripts/windows/install-tasks.ps1 +7 -7
  65. package/scripts/windows/skcapstone-task.xml +1 -1
  66. package/src/skcapstone/__init__.py +45 -3
  67. package/src/skcapstone/_cli_monolith.py +20 -15
  68. package/src/skcapstone/activity.py +5 -1
  69. package/src/skcapstone/agent_card.py +3 -2
  70. package/src/skcapstone/api.py +41 -40
  71. package/src/skcapstone/auction.py +14 -11
  72. package/src/skcapstone/backup.py +2 -1
  73. package/src/skcapstone/blueprint_registry.py +4 -3
  74. package/src/skcapstone/blueprints/builtins/itil-operations.yaml +40 -0
  75. package/src/skcapstone/brain_first.py +238 -0
  76. package/src/skcapstone/changelog.py +1 -1
  77. package/src/skcapstone/chat.py +22 -17
  78. package/src/skcapstone/cli/__init__.py +9 -1
  79. package/src/skcapstone/cli/_common.py +1 -0
  80. package/src/skcapstone/cli/agents_spawner.py +5 -2
  81. package/src/skcapstone/cli/alerts.py +25 -4
  82. package/src/skcapstone/cli/bench.py +15 -15
  83. package/src/skcapstone/cli/chat.py +7 -4
  84. package/src/skcapstone/cli/consciousness.py +5 -2
  85. package/src/skcapstone/cli/context_cmd.py +18 -4
  86. package/src/skcapstone/cli/daemon.py +121 -42
  87. package/src/skcapstone/cli/gtd.py +26 -1
  88. package/src/skcapstone/cli/housekeeping.py +3 -3
  89. package/src/skcapstone/cli/identity_cmd.py +378 -0
  90. package/src/skcapstone/cli/joule_cmd.py +7 -3
  91. package/src/skcapstone/cli/memory.py +8 -6
  92. package/src/skcapstone/cli/peers_dir.py +1 -1
  93. package/src/skcapstone/cli/register_cmd.py +29 -3
  94. package/src/skcapstone/cli/scheduler_cmd.py +167 -0
  95. package/src/skcapstone/cli/session.py +25 -0
  96. package/src/skcapstone/cli/setup.py +96 -29
  97. package/src/skcapstone/cli/shell_cmd.py +53 -1
  98. package/src/skcapstone/cli/skills_cmd.py +2 -2
  99. package/src/skcapstone/cli/soul.py +8 -5
  100. package/src/skcapstone/cli/status.py +37 -11
  101. package/src/skcapstone/cli/telegram.py +21 -0
  102. package/src/skcapstone/cli/test_cmd.py +5 -5
  103. package/src/skcapstone/cli/test_connection.py +2 -2
  104. package/src/skcapstone/cli/upgrade_cmd.py +23 -14
  105. package/src/skcapstone/cli/version_cmd.py +1 -1
  106. package/src/skcapstone/cli/watch_cmd.py +9 -6
  107. package/src/skcapstone/cloud9_bridge.py +14 -14
  108. package/src/skcapstone/codex_setup.py +255 -0
  109. package/src/skcapstone/config_validator.py +7 -4
  110. package/src/skcapstone/consciousness_config.py +5 -1
  111. package/src/skcapstone/consciousness_loop.py +313 -273
  112. package/src/skcapstone/context_loader.py +121 -0
  113. package/src/skcapstone/coord_federation.py +2 -1
  114. package/src/skcapstone/coordination.py +23 -6
  115. package/src/skcapstone/crush_integration.py +2 -1
  116. package/src/skcapstone/daemon.py +151 -88
  117. package/src/skcapstone/dashboard.py +10 -10
  118. package/src/skcapstone/data/sk-agent-picker.sh +421 -0
  119. package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
  120. package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
  121. package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
  122. package/src/skcapstone/data/systemd/skcapstone.service +37 -0
  123. package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
  124. package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
  125. package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
  126. package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
  127. package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
  128. package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
  129. package/src/skcapstone/defaults/claude/settings.json +74 -0
  130. package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
  131. package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
  132. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
  133. package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
  134. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
  135. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
  136. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
  137. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
  138. package/src/skcapstone/defaults/unhinged.json +13 -0
  139. package/src/skcapstone/discovery.py +43 -20
  140. package/src/skcapstone/doctor.py +941 -22
  141. package/src/skcapstone/dreaming.py +1183 -109
  142. package/src/skcapstone/emotion_tracker.py +2 -2
  143. package/src/skcapstone/export.py +4 -3
  144. package/src/skcapstone/fuse_mount.py +35 -25
  145. package/src/skcapstone/gui_installer.py +2 -2
  146. package/src/skcapstone/heartbeat.py +34 -30
  147. package/src/skcapstone/housekeeping.py +14 -14
  148. package/src/skcapstone/install_wizard.py +209 -7
  149. package/src/skcapstone/itil.py +13 -4
  150. package/src/skcapstone/kms_scheduler.py +10 -8
  151. package/src/skcapstone/launchd.py +426 -0
  152. package/src/skcapstone/mcp_launcher.py +15 -1
  153. package/src/skcapstone/mcp_server.py +341 -49
  154. package/src/skcapstone/mcp_tools/__init__.py +2 -0
  155. package/src/skcapstone/mcp_tools/_helpers.py +2 -2
  156. package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
  157. package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
  158. package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
  159. package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
  160. package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
  161. package/src/skcapstone/mcp_tools/did_tools.py +11 -8
  162. package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
  163. package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
  164. package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
  165. package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
  166. package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
  167. package/src/skcapstone/mdns_discovery.py +2 -2
  168. package/src/skcapstone/memory_curator.py +1 -1
  169. package/src/skcapstone/memory_engine.py +10 -3
  170. package/src/skcapstone/metrics.py +30 -16
  171. package/src/skcapstone/migrate_memories.py +4 -3
  172. package/src/skcapstone/migrate_multi_agent.py +8 -7
  173. package/src/skcapstone/models.py +47 -5
  174. package/src/skcapstone/notifications.py +42 -18
  175. package/src/skcapstone/onboard.py +1000 -126
  176. package/src/skcapstone/operator_link.py +170 -0
  177. package/src/skcapstone/peer_directory.py +4 -4
  178. package/src/skcapstone/peers.py +19 -19
  179. package/src/skcapstone/pillars/__init__.py +7 -5
  180. package/src/skcapstone/pillars/consciousness.py +191 -0
  181. package/src/skcapstone/pillars/identity.py +51 -7
  182. package/src/skcapstone/pillars/memory.py +9 -3
  183. package/src/skcapstone/pillars/sync.py +2 -2
  184. package/src/skcapstone/preflight.py +3 -3
  185. package/src/skcapstone/providers/docker.py +28 -28
  186. package/src/skcapstone/register.py +6 -6
  187. package/src/skcapstone/registry_client.py +5 -4
  188. package/src/skcapstone/runtime.py +14 -3
  189. package/src/skcapstone/scheduled_tasks.py +254 -19
  190. package/src/skcapstone/scheduler_jobs.py +456 -0
  191. package/src/skcapstone/scheduler_runner.py +239 -0
  192. package/src/skcapstone/scheduler_state.py +162 -0
  193. package/src/skcapstone/sdk.py +310 -0
  194. package/src/skcapstone/service_health.py +279 -39
  195. package/src/skcapstone/session_briefing.py +108 -0
  196. package/src/skcapstone/session_capture.py +1 -1
  197. package/src/skcapstone/shell.py +7 -1
  198. package/src/skcapstone/soul.py +3 -1
  199. package/src/skcapstone/soul_switch.py +3 -1
  200. package/src/skcapstone/summary.py +6 -6
  201. package/src/skcapstone/sync_engine.py +15 -15
  202. package/src/skcapstone/sync_watcher.py +2 -2
  203. package/src/skcapstone/systemd.py +72 -21
  204. package/src/skcapstone/team_comms.py +8 -8
  205. package/src/skcapstone/team_engine.py +1 -1
  206. package/src/skcapstone/testrunner.py +3 -3
  207. package/src/skcapstone/trust_graph.py +40 -5
  208. package/src/skcapstone/unified_search.py +15 -6
  209. package/src/skcapstone/uninstall_wizard.py +11 -3
  210. package/src/skcapstone/version_check.py +8 -4
  211. package/src/skcapstone/warmth_anchor.py +4 -2
  212. package/src/skcapstone/whoami.py +4 -4
  213. package/systemd/skcapstone.service +4 -6
  214. package/systemd/skcapstone@.service +7 -8
  215. package/systemd/skcomms-heartbeat.service +21 -0
  216. package/systemd/skcomms-heartbeat.timer +12 -0
  217. package/systemd/skcomms-queue-drain.service +17 -0
  218. package/systemd/skcomms-queue-drain.timer +12 -0
  219. package/tests/conftest.py +39 -0
  220. package/tests/integration/test_consciousness_e2e.py +39 -39
  221. package/tests/test_agent_card.py +1 -1
  222. package/tests/test_agent_home_scaffold.py +34 -0
  223. package/tests/test_alerts_consumer_topics.py +27 -0
  224. package/tests/test_backup.py +2 -1
  225. package/tests/test_chat.py +6 -6
  226. package/tests/test_claude_md.py +2 -2
  227. package/tests/test_cli_skills.py +10 -10
  228. package/tests/test_cli_test_cmd.py +4 -4
  229. package/tests/test_cli_test_connection.py +1 -1
  230. package/tests/test_cloud9_bridge.py +6 -6
  231. package/tests/test_consciousness_e2e.py +1 -1
  232. package/tests/test_consciousness_loop.py +10 -10
  233. package/tests/test_coordination.py +25 -0
  234. package/tests/test_cross_package.py +21 -21
  235. package/tests/test_daemon.py +4 -4
  236. package/tests/test_daemon_shutdown.py +1 -1
  237. package/tests/test_docker_provider.py +29 -29
  238. package/tests/test_doctor.py +400 -0
  239. package/tests/test_doctor_skscheduler.py +50 -0
  240. package/tests/test_dreaming_engine.py +147 -0
  241. package/tests/test_dreaming_gtd_capture.py +35 -0
  242. package/tests/test_e2e_automated.py +8 -5
  243. package/tests/test_fuse_mount.py +10 -10
  244. package/tests/test_gtd_brief.py +46 -0
  245. package/tests/test_gtd_malformed_tolerance.py +31 -0
  246. package/tests/test_housekeeping.py +15 -15
  247. package/tests/test_identity_migrate.py +251 -0
  248. package/tests/test_integration_backbone.py +598 -0
  249. package/tests/test_itil_gtd_lifecycle.py +37 -0
  250. package/tests/test_jobs_dropins.py +84 -0
  251. package/tests/test_mcp_server.py +82 -37
  252. package/tests/test_models.py +48 -4
  253. package/tests/test_multi_agent.py +31 -29
  254. package/tests/test_notifications.py +122 -32
  255. package/tests/test_onboard.py +63 -75
  256. package/tests/test_operator_link.py +78 -0
  257. package/tests/test_peers.py +14 -14
  258. package/tests/test_pillars.py +98 -0
  259. package/tests/test_preflight.py +3 -3
  260. package/tests/test_runtime.py +21 -0
  261. package/tests/test_scheduled_tasks.py +11 -6
  262. package/tests/test_scheduler_cli.py +47 -0
  263. package/tests/test_scheduler_features.py +133 -0
  264. package/tests/test_scheduler_integration.py +87 -0
  265. package/tests/test_scheduler_jobs.py +155 -0
  266. package/tests/test_scheduler_runner.py +64 -0
  267. package/tests/test_scheduler_state.py +57 -0
  268. package/tests/test_sdk.py +70 -0
  269. package/tests/test_service_health_incidents.py +34 -0
  270. package/tests/test_service_registry.py +52 -0
  271. package/tests/test_session_briefing.py +130 -0
  272. package/tests/test_snapshots.py +4 -4
  273. package/tests/test_sync_pipeline.py +26 -26
  274. package/tests/test_team_comms.py +2 -2
  275. package/tests/test_testrunner.py +2 -2
  276. package/tests/test_trust_graph.py +18 -0
  277. package/tests/test_unified_search.py +2 -2
  278. package/tests/test_version_check.py +10 -0
  279. package/tests/test_version_cmd.py +8 -8
  280. package/tests/test_whoami.py +1 -1
  281. package/systemd/skcomm-heartbeat.service +0 -18
  282. package/systemd/skcomm-queue-drain.service +0 -17
  283. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
  284. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
@@ -0,0 +1,155 @@
1
+ # skscheduler — Unified Fleet Job Scheduler
2
+
3
+ > The single declarative registry + per-node runner for all recurring jobs across the
4
+ > SK fleet. One `jobs.yaml`, synced everywhere; each node runs its own scheduler thread
5
+ > (inside the skcapstone daemon) and fires only the jobs whose affinity includes it.
6
+ > Design spec: [`docs/superpowers/specs/2026-06-08-skscheduler-design.md`](superpowers/specs/2026-06-08-skscheduler-design.md).
7
+
8
+ ## Why it exists
9
+ Scheduling was fragmented across **four** mechanisms (skcapstone `TaskScheduler` interval
10
+ callbacks, legacy crontab, systemd user timers, Claude-Code crons) with no single place to
11
+ define, run, or observe jobs — and `service_health` running on multiple nodes caused
12
+ Syncthing `.sync-conflict-*` files (the root incident). skscheduler unifies this: **one
13
+ registry, per-job node-affinity** (so a job runs on exactly the intended node — preventing
14
+ the multi-writer class), cron **and** interval, and agent-judgment jobs (not just Python).
15
+
16
+ ## Architecture
17
+
18
+ ```mermaid
19
+ flowchart TD
20
+ Y["~/.skcapstone/config/jobs.yaml<br/>(Syncthing-synced registry)"] -->|read by every node| D
21
+ subgraph node["each fleet node (.158, .41, …)"]
22
+ D["skcapstone daemon<br/>(systemd user service)"] --> TS["TaskScheduler<br/>tick loop (5s)"]
23
+ TS -->|interval callbacks| IC["heartbeat · memory_promotion<br/>dreaming_reflection · reprobe"]
24
+ TS -->|config jobs| DUE{"due? (cron|interval)<br/>AND node in affinity?"}
25
+ DUE -->|yes| LK["per-job overlap lock<br/>(singleton; skip if running)"]
26
+ LK --> JIT["jitter splay"] --> RUN["JobRunner.run + retries/backoff"]
27
+ RUN --> DISP{type}
28
+ DISP -->|python| PY["import module:fn()"]
29
+ DISP -->|shell| SH["subprocess (timeout)"]
30
+ DISP -->|agent| AG["claude -p --agent NAME"]
31
+ RUN --> STATE["SchedulerState.record_run<br/>(node-local, NEVER synced)"]
32
+ RUN --> NOTIFY["notify? → sk-alert (Telegram)"]
33
+ end
34
+ STATE --> SJ["~/.skcapstone/scheduler/&lt;host&gt;/state.json"]
35
+ RUN --> LOG["~/.skcapstone/scheduler/&lt;host&gt;/logs/&lt;job&gt;-&lt;ts&gt;.log"]
36
+ ```
37
+
38
+ **Sync-safety invariant:** the registry (`jobs.yaml`) is synced; **run-state and logs are
39
+ node-local and never synced** (`~/.skcapstone/.stignore` excludes `scheduler/`). The
40
+ scheduler can never become a sync-conflict source.
41
+
42
+ ## Job lifecycle (one due job)
43
+
44
+ ```mermaid
45
+ sequenceDiagram
46
+ participant Loop as TaskScheduler tick
47
+ participant Due as is_due()
48
+ participant Aff as job_runs_here()
49
+ participant Lock as overlap lock
50
+ participant Run as JobRunner
51
+ participant St as SchedulerState
52
+ participant Al as sk-alert
53
+ Loop->>Due: cron slot passed / interval elapsed since last_run?
54
+ Due-->>Loop: due
55
+ Loop->>Aff: this node's alias in job.nodes?
56
+ Aff-->>Loop: yes
57
+ Loop->>Lock: acquire (non-blocking)
58
+ alt previous run still going
59
+ Lock-->>Loop: busy → skip (no double-run)
60
+ else free
61
+ Lock-->>Loop: got it (in a worker thread)
62
+ Loop->>Run: jitter splay, then attempt 1..N
63
+ loop until ok or retries exhausted
64
+ Run->>Run: dispatch(type) with timeout
65
+ Run-->>Loop: ok? else backoff + retry
66
+ end
67
+ Loop->>St: record_run(ok, error, fire_time)
68
+ Loop->>Al: notify policy met? → DM Chef
69
+ end
70
+ ```
71
+
72
+ ## Due-check logic
73
+
74
+ ```mermaid
75
+ flowchart LR
76
+ A{schedule set?} -->|cron| C["most-recent cron slot ≤ now"]
77
+ C --> C2{"last_run < that slot?"}
78
+ C2 -->|yes| DUE([due])
79
+ C2 -->|no| NO([not due])
80
+ A -->|every_seconds| I{"now − last_run ≥ interval?"}
81
+ I -->|yes| DUE
82
+ I -->|no| NO
83
+ A -->|neither| NO
84
+ ```
85
+ Cron uses `croniter` (a declared dependency — if missing, **cron jobs silently can't fire**;
86
+ `pip install -e .` in the venv fixes it). Missed slots are caught up once on next tick
87
+ (misfire catch-up); per-job `catchup: false` opts out.
88
+
89
+ ## `jobs.yaml` schema (complete)
90
+
91
+ ```yaml
92
+ jobs:
93
+ <job-name>: # YAML key = unique job id
94
+ # — scheduling (exactly one) —
95
+ schedule: "0 6 * * *" # cron (m h dom mon dow)
96
+ every: 5m # OR interval: 30s | 5m | 1h | 1d | <seconds>
97
+ # — what to run (by type) —
98
+ type: agent # agent | shell | python (default python)
99
+ agent: lumina # agent type → runs `claude -p "<prompt>" --agent <name>`
100
+ prompt: "…" # agent prompt
101
+ command: "skmem-pg-backup"# shell type → subprocess
102
+ callback: "module.path:fn"# python type → import & call
103
+ # — placement —
104
+ nodes: [".41"] # affinity: "all" | [host aliases] (alias = SK_NODE_ALIAS)
105
+ # — limits / reliability (added 2026-06-09) —
106
+ timeout: 900 # hard-kill seconds (default 900)
107
+ retries: 0 # extra attempts on failure (linear)
108
+ retry_backoff: 0 # seconds between attempts
109
+ jitter: 0 # max random splay (s) before dispatch — fleet anti-stampede
110
+ notify: off # off | on_failure | on_success | always → sk-alert (Telegram)
111
+ notify_level: warn # sk-alert level for failures (info|warn|crit)
112
+ catchup: true # run once on a missed slot (default true)
113
+ enabled: true
114
+ ```
115
+
116
+ ## Reliability features (2026-06-09)
117
+ | field | purpose | infra it serves |
118
+ |---|---|---|
119
+ | `retries` + `retry_backoff` | re-attempt on transient failure | flaky network/registry/LLM endpoints |
120
+ | `jitter` | random splay before dispatch | many nodes sharing a cron slot hitting one resource |
121
+ | `notify` (+`notify_level`) | sk-alert on `on_failure`/`on_success`/`always` with output tail | unattended jobs; result delivery (e.g. sktrip) |
122
+ | `catchup` | catch a missed slot once | laptops/asleep nodes (vs strict real-time jobs) |
123
+ | overlap lock | singleton per job | long runs that exceed their interval |
124
+ | node affinity | exactly-one-node execution | stateful/single-writer jobs (the original conflict fix) |
125
+
126
+ ## State, logs, observability
127
+ - **State** (node-local): `~/.skcapstone/scheduler/<host>/state.json` — `last_run`, `ok`, `error`, run counts. Never synced.
128
+ - **Logs** (node-local): `~/.skcapstone/scheduler/<host>/logs/<job>-<ts>.log` — captured stdout+stderr.
129
+ - **CLI**: `skcapstone scheduler …` (list / run-now / status). `skcapstone doctor` validates scheduler health.
130
+
131
+ ## Operating it
132
+ ```bash
133
+ systemctl --user status skcapstone.service # the daemon that runs the scheduler
134
+ systemctl --user start skcapstone.service # ⚠ if inactive, NO jobs.yaml jobs fire
135
+ echo "$SK_NODE_ALIAS" # must match a value in a job's nodes: list
136
+ skcapstone scheduler status # last run / ok / errors per job
137
+ ```
138
+
139
+ ## Troubleshooting
140
+ | symptom | cause | fix |
141
+ |---|---|---|
142
+ | no jobs fire | `skcapstone.service` inactive | `systemctl --user start skcapstone.service` |
143
+ | cron jobs never fire (interval ok) | `croniter` not installed in venv | `~/.skenv/bin/pip install -e .` |
144
+ | job runs on wrong/extra node | `nodes:` affinity / `SK_NODE_ALIAS` mismatch | set the env / fix the list |
145
+ | job overlaps itself | run time > interval | raise interval, or rely on the overlap lock (it skips) |
146
+ | no failure alerts | `notify: off` | set `notify: on_failure` |
147
+
148
+ ## Relationship to other schedulers
149
+ - **systemd user timers** — keep for OS-level/boot jobs and as the process supervisor for the daemon itself; migrate *application* recurring jobs here.
150
+ - **Claude-Code crons** — separate system (cloud agents); documented, not migrated.
151
+ - **legacy crontab** — retire into `jobs.yaml`.
152
+
153
+ ## Roadmap (not yet implemented)
154
+ job dependencies (`after:`), dead-man's-switch staleness alerts, per-job env/secrets injection,
155
+ a web/TUI run-history view, and distributed locking (currently affinity-by-declaration, not election).
@@ -0,0 +1,31 @@
1
+ # Reference for ~/.skcapstone/config/jobs.yaml — the skscheduler registry.
2
+ #
3
+ # This file is Syncthing-synced across the fleet. Each node runs its own
4
+ # scheduler (inside the skcapstone daemon) and fires ONLY the jobs whose
5
+ # `nodes` affinity includes that host. Scheduler run-state is node-local and
6
+ # never synced (see ~/.skcapstone/.stignore -> `scheduler`).
7
+ #
8
+ # Job fields:
9
+ # schedule: "<cron>" OR every: <30s|5m|1h|1d|seconds> (one of the two)
10
+ # type: agent | shell | python
11
+ # nodes: all | [<host alias>, ...] (alias via SK_NODE_ALIAS env)
12
+ # agent: <name> (agent type) — runs `claude -p "<prompt>" --agent <name>`
13
+ # prompt: "<text>" (agent type)
14
+ # command: "<cmd>" (shell type)
15
+ # callback: module:fn (python type)
16
+ # timeout: <seconds> (default 900)
17
+ # enabled: true|false
18
+
19
+ jobs:
20
+ gtd-inbox-triage:
21
+ schedule: "0 6 * * *" # daily 06:00
22
+ type: agent
23
+ nodes: [".41"]
24
+ agent: lumina
25
+ prompt: >
26
+ Triage the GTD inbox: for each item, clarify into next-action /
27
+ project / someday-maybe, or archive noise; move resolved-ITIL items
28
+ to done; surface stale projects. Use the gtd_* and itil_* MCP tools.
29
+ Keep it concise.
30
+ timeout: 900
31
+ enabled: true