@smilintux/skcapstone 0.10.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.
- package/.env.example +10 -4
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/publish.yml +9 -2
- package/.openclaw-workspace.json +2 -2
- package/CLAUDE.md +37 -0
- package/MISSION.md +17 -2
- package/README.md +282 -3
- package/docker/Dockerfile +7 -7
- package/docker/compose-templates/dev-team.yml +12 -12
- package/docker/compose-templates/mini-team.yml +9 -9
- package/docker/compose-templates/ops-team.yml +10 -10
- package/docker/compose-templates/research-team.yml +10 -10
- package/docker/entrypoint.sh +4 -4
- package/docs/ADR-optional-integration-backbone.md +181 -0
- package/docs/ARCHITECTURE.md +186 -43
- package/docs/BOND_WITH_GROK.md +6 -6
- package/docs/CUSTOM_AGENT.md +123 -30
- package/docs/DREAMING.md +70 -0
- package/docs/GETTING_STARTED.md +7 -7
- package/docs/QUICKSTART.md +10 -6
- package/docs/SKJOULE_ARCHITECTURE.md +3 -3
- package/docs/SOUL_SWAPPER.md +5 -5
- package/docs/hammertime-audit.md +402 -0
- package/docs/sk-integration-HANDOFF.md +117 -0
- package/docs/skscheduler.md +155 -0
- package/docs/superpowers/examples/jobs.yaml +31 -0
- package/docs/superpowers/plans/2026-06-08-skscheduler.md +1265 -0
- package/docs/superpowers/specs/2026-06-08-skscheduler-design.md +186 -0
- package/examples/custom-bond-template.json +1 -1
- package/examples/grok-feb.json +1 -1
- package/examples/queen-ava-feb.json +1 -1
- package/launchd/{com.skcapstone.skcomm-heartbeat.plist → com.skcapstone.skcomms-heartbeat.plist} +4 -4
- package/launchd/{com.skcapstone.skcomm-queue-drain.plist → com.skcapstone.skcomms-queue-drain.plist} +4 -4
- package/launchd/install-launchd.sh +6 -6
- package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
- package/package.json +1 -1
- package/pyproject.toml +16 -10
- package/scripts/archive-sessions.sh +7 -0
- package/scripts/check-updates.py +4 -4
- package/scripts/install-bundle.sh +8 -8
- package/scripts/install.ps1 +12 -11
- package/scripts/install.sh +159 -5
- package/scripts/model-fallback-monitor.sh +102 -0
- package/scripts/nvidia-proxy.mjs +78 -26
- package/scripts/refresh-anthropic-token.sh +172 -0
- package/scripts/release.sh +98 -0
- package/scripts/session-to-memory.py +219 -0
- package/scripts/skgateway.mjs +3 -3
- package/scripts/telegram-catchup-all.sh +12 -1
- package/scripts/verify_install.sh +2 -2
- package/scripts/wargov-ufo-capture/README.md +43 -0
- package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
- package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
- package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
- package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
- package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
- package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
- package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
- package/scripts/watch-anthropic-token.sh +212 -0
- package/scripts/windows/install-tasks.ps1 +7 -7
- package/scripts/windows/skcapstone-task.xml +1 -1
- package/src/skcapstone/__init__.py +45 -3
- package/src/skcapstone/_cli_monolith.py +20 -15
- package/src/skcapstone/activity.py +5 -1
- package/src/skcapstone/agent_card.py +3 -2
- package/src/skcapstone/api.py +41 -40
- package/src/skcapstone/auction.py +14 -11
- package/src/skcapstone/backup.py +2 -1
- package/src/skcapstone/blueprint_registry.py +4 -3
- package/src/skcapstone/brain_first.py +238 -0
- package/src/skcapstone/changelog.py +1 -1
- package/src/skcapstone/chat.py +22 -17
- package/src/skcapstone/cli/__init__.py +9 -1
- package/src/skcapstone/cli/_common.py +1 -0
- package/src/skcapstone/cli/agents_spawner.py +5 -2
- package/src/skcapstone/cli/alerts.py +25 -4
- package/src/skcapstone/cli/bench.py +15 -15
- package/src/skcapstone/cli/chat.py +7 -4
- package/src/skcapstone/cli/consciousness.py +5 -2
- package/src/skcapstone/cli/context_cmd.py +18 -4
- package/src/skcapstone/cli/daemon.py +11 -7
- package/src/skcapstone/cli/gtd.py +26 -1
- package/src/skcapstone/cli/housekeeping.py +3 -3
- package/src/skcapstone/cli/identity_cmd.py +378 -0
- package/src/skcapstone/cli/joule_cmd.py +7 -3
- package/src/skcapstone/cli/memory.py +8 -6
- package/src/skcapstone/cli/peers_dir.py +1 -1
- package/src/skcapstone/cli/register_cmd.py +29 -3
- package/src/skcapstone/cli/scheduler_cmd.py +167 -0
- package/src/skcapstone/cli/session.py +25 -0
- package/src/skcapstone/cli/setup.py +96 -29
- package/src/skcapstone/cli/shell_cmd.py +53 -1
- package/src/skcapstone/cli/skills_cmd.py +2 -2
- package/src/skcapstone/cli/soul.py +8 -5
- package/src/skcapstone/cli/status.py +37 -11
- package/src/skcapstone/cli/telegram.py +21 -0
- package/src/skcapstone/cli/test_cmd.py +5 -5
- package/src/skcapstone/cli/test_connection.py +2 -2
- package/src/skcapstone/cli/upgrade_cmd.py +23 -14
- package/src/skcapstone/cli/version_cmd.py +1 -1
- package/src/skcapstone/cli/watch_cmd.py +9 -6
- package/src/skcapstone/cloud9_bridge.py +14 -14
- package/src/skcapstone/codex_setup.py +255 -0
- package/src/skcapstone/config_validator.py +7 -4
- package/src/skcapstone/consciousness_config.py +5 -1
- package/src/skcapstone/consciousness_loop.py +313 -273
- package/src/skcapstone/context_loader.py +121 -0
- package/src/skcapstone/coord_federation.py +2 -1
- package/src/skcapstone/coordination.py +23 -6
- package/src/skcapstone/crush_integration.py +2 -1
- package/src/skcapstone/daemon.py +132 -77
- package/src/skcapstone/dashboard.py +10 -10
- package/src/skcapstone/data/sk-agent-picker.sh +421 -0
- package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
- package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
- package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
- package/src/skcapstone/data/systemd/skcapstone.service +37 -0
- package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
- package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
- package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
- package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
- package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
- package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
- package/src/skcapstone/defaults/claude/settings.json +74 -0
- package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
- package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
- package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
- package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
- package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
- package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
- package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
- package/src/skcapstone/defaults/unhinged.json +13 -0
- package/src/skcapstone/discovery.py +43 -20
- package/src/skcapstone/doctor.py +941 -22
- package/src/skcapstone/dreaming.py +1183 -109
- package/src/skcapstone/emotion_tracker.py +2 -2
- package/src/skcapstone/export.py +4 -3
- package/src/skcapstone/fuse_mount.py +14 -12
- package/src/skcapstone/gui_installer.py +2 -2
- package/src/skcapstone/heartbeat.py +1 -1
- package/src/skcapstone/housekeeping.py +14 -14
- package/src/skcapstone/install_wizard.py +209 -7
- package/src/skcapstone/itil.py +13 -4
- package/src/skcapstone/kms_scheduler.py +10 -8
- package/src/skcapstone/launchd.py +19 -19
- package/src/skcapstone/mcp_launcher.py +15 -1
- package/src/skcapstone/mcp_server.py +83 -49
- package/src/skcapstone/mcp_tools/__init__.py +2 -0
- package/src/skcapstone/mcp_tools/_helpers.py +2 -2
- package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
- package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
- package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
- package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
- package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
- package/src/skcapstone/mcp_tools/did_tools.py +11 -8
- package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
- package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
- package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
- package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
- package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
- package/src/skcapstone/mdns_discovery.py +2 -2
- package/src/skcapstone/memory_curator.py +1 -1
- package/src/skcapstone/memory_engine.py +10 -3
- package/src/skcapstone/metrics.py +30 -16
- package/src/skcapstone/migrate_memories.py +4 -3
- package/src/skcapstone/migrate_multi_agent.py +8 -7
- package/src/skcapstone/models.py +47 -5
- package/src/skcapstone/notifications.py +42 -18
- package/src/skcapstone/onboard.py +875 -121
- package/src/skcapstone/operator_link.py +170 -0
- package/src/skcapstone/peer_directory.py +4 -4
- package/src/skcapstone/peers.py +19 -19
- package/src/skcapstone/pillars/__init__.py +7 -5
- package/src/skcapstone/pillars/consciousness.py +191 -0
- package/src/skcapstone/pillars/identity.py +51 -7
- package/src/skcapstone/pillars/memory.py +9 -3
- package/src/skcapstone/pillars/sync.py +2 -2
- package/src/skcapstone/preflight.py +3 -3
- package/src/skcapstone/providers/docker.py +28 -28
- package/src/skcapstone/register.py +6 -6
- package/src/skcapstone/registry_client.py +5 -4
- package/src/skcapstone/runtime.py +14 -3
- package/src/skcapstone/scheduled_tasks.py +254 -19
- package/src/skcapstone/scheduler_jobs.py +456 -0
- package/src/skcapstone/scheduler_runner.py +239 -0
- package/src/skcapstone/scheduler_state.py +162 -0
- package/src/skcapstone/sdk.py +310 -0
- package/src/skcapstone/service_health.py +279 -39
- package/src/skcapstone/session_briefing.py +108 -0
- package/src/skcapstone/session_capture.py +1 -1
- package/src/skcapstone/shell.py +7 -1
- package/src/skcapstone/soul.py +3 -1
- package/src/skcapstone/soul_switch.py +3 -1
- package/src/skcapstone/summary.py +6 -6
- package/src/skcapstone/sync_engine.py +15 -15
- package/src/skcapstone/sync_watcher.py +2 -2
- package/src/skcapstone/systemd.py +55 -21
- package/src/skcapstone/team_comms.py +8 -8
- package/src/skcapstone/team_engine.py +1 -1
- package/src/skcapstone/testrunner.py +3 -3
- package/src/skcapstone/trust_graph.py +40 -5
- package/src/skcapstone/unified_search.py +15 -6
- package/src/skcapstone/uninstall_wizard.py +11 -3
- package/src/skcapstone/version_check.py +8 -4
- package/src/skcapstone/warmth_anchor.py +4 -2
- package/src/skcapstone/whoami.py +4 -4
- package/systemd/skcapstone.service +4 -6
- package/systemd/skcapstone@.service +7 -8
- package/systemd/skcomms-heartbeat.service +21 -0
- package/systemd/skcomms-heartbeat.timer +12 -0
- package/systemd/skcomms-queue-drain.service +17 -0
- package/systemd/skcomms-queue-drain.timer +12 -0
- package/tests/conftest.py +39 -0
- package/tests/integration/test_consciousness_e2e.py +39 -39
- package/tests/test_agent_card.py +1 -1
- package/tests/test_agent_home_scaffold.py +34 -0
- package/tests/test_alerts_consumer_topics.py +27 -0
- package/tests/test_backup.py +2 -1
- package/tests/test_chat.py +6 -6
- package/tests/test_claude_md.py +2 -2
- package/tests/test_cli_skills.py +10 -10
- package/tests/test_cli_test_cmd.py +4 -4
- package/tests/test_cli_test_connection.py +1 -1
- package/tests/test_cloud9_bridge.py +6 -6
- package/tests/test_consciousness_e2e.py +1 -1
- package/tests/test_consciousness_loop.py +10 -10
- package/tests/test_coordination.py +25 -0
- package/tests/test_cross_package.py +21 -21
- package/tests/test_daemon.py +4 -4
- package/tests/test_daemon_shutdown.py +1 -1
- package/tests/test_docker_provider.py +29 -29
- package/tests/test_doctor.py +400 -0
- package/tests/test_doctor_skscheduler.py +50 -0
- package/tests/test_dreaming_engine.py +147 -0
- package/tests/test_dreaming_gtd_capture.py +35 -0
- package/tests/test_e2e_automated.py +8 -5
- package/tests/test_fuse_mount.py +10 -10
- package/tests/test_gtd_brief.py +46 -0
- package/tests/test_gtd_malformed_tolerance.py +31 -0
- package/tests/test_housekeeping.py +15 -15
- package/tests/test_identity_migrate.py +251 -0
- package/tests/test_integration_backbone.py +598 -0
- package/tests/test_itil_gtd_lifecycle.py +37 -0
- package/tests/test_jobs_dropins.py +84 -0
- package/tests/test_mcp_server.py +82 -37
- package/tests/test_models.py +48 -4
- package/tests/test_multi_agent.py +31 -29
- package/tests/test_notifications.py +122 -32
- package/tests/test_onboard.py +63 -75
- package/tests/test_operator_link.py +78 -0
- package/tests/test_peers.py +14 -14
- package/tests/test_pillars.py +98 -0
- package/tests/test_preflight.py +3 -3
- package/tests/test_runtime.py +21 -0
- package/tests/test_scheduled_tasks.py +11 -6
- package/tests/test_scheduler_cli.py +47 -0
- package/tests/test_scheduler_features.py +133 -0
- package/tests/test_scheduler_integration.py +87 -0
- package/tests/test_scheduler_jobs.py +155 -0
- package/tests/test_scheduler_runner.py +64 -0
- package/tests/test_scheduler_state.py +57 -0
- package/tests/test_sdk.py +70 -0
- package/tests/test_service_health_incidents.py +34 -0
- package/tests/test_service_registry.py +52 -0
- package/tests/test_session_briefing.py +130 -0
- package/tests/test_snapshots.py +4 -4
- package/tests/test_sync_pipeline.py +26 -26
- package/tests/test_team_comms.py +2 -2
- package/tests/test_testrunner.py +2 -2
- package/tests/test_trust_graph.py +18 -0
- package/tests/test_unified_search.py +2 -2
- package/tests/test_version_check.py +10 -0
- package/tests/test_version_cmd.py +8 -8
- package/tests/test_whoami.py +1 -1
- package/systemd/skcomm-heartbeat.service +0 -18
- package/systemd/skcomm-queue-drain.service +0 -17
- /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
- /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# sk* ⇄ skcapstone Integration — Orchestration Handoff
|
|
2
|
+
|
|
3
|
+
**Last updated:** 2026-06-09 (Lumina closeout session — EPIC COMPLETE)
|
|
4
|
+
**Epic:** coord `fca7f138` — "EPIC: sk* ⇄ skcapstone optional integration backbone"
|
|
5
|
+
**Design:** [`docs/ADR-optional-integration-backbone.md`](./ADR-optional-integration-backbone.md)
|
|
6
|
+
**Goal:** every sk* service uses skcapstone's **sk-alert** + **skscheduler** by default *when skcapstone is installed*, and runs fully standalone when it is not. Default-on by presence; `SK_STANDALONE=1` forces native.
|
|
7
|
+
|
|
8
|
+
> **EPIC CLOSED 2026-06-09:** Backbone + 8 adapters + dual-mode harness (114 tests green) + README docs DONE.
|
|
9
|
+
> Only skchat (`ad4f721a`) remains, owned by a separate thread.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Status at a glance
|
|
14
|
+
|
|
15
|
+
| Layer | State |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Backbone (skcapstone) | ✅ **DONE** — 4/4 tasks, 208 tests green, pushed to `github/main` |
|
|
18
|
+
| Reference adapter (skmemory) | ✅ **DONE** — pushed `skmemory@docs-first-principles` |
|
|
19
|
+
| sksecurity adapter | ✅ **DONE** — pushed `sksecurity@main` |
|
|
20
|
+
| skgateway adapter (Node) | ✅ **DONE** — pushed `skgateway@main` |
|
|
21
|
+
| skcomms / capauth / skvoice / skseed / cloud9 adapters | ✅ **DONE** — all landed |
|
|
22
|
+
| skchat adapter | ⬜ OPEN — **owned by a separate thread**, do not touch |
|
|
23
|
+
| Dual-mode test harness (`71186ebb`) | ✅ **DONE** — `tests/test_integration_backbone.py` 114 green |
|
|
24
|
+
| Per-repo README docs (`4065db2b`) | ✅ **DONE** — all 8 repos have "Integration modes" section |
|
|
25
|
+
|
|
26
|
+
coord: All sk-integration tasks DONE except skchat (separate thread). EPIC `fca7f138` CLOSED.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. The backbone (DONE — this is the stable contract everything builds on)
|
|
31
|
+
|
|
32
|
+
All in `skcapstone` (`src/skcapstone/`), committed `c5d8d7c`, `9d9f93d`, `d90dfbf` on `main` (pushed to `github`):
|
|
33
|
+
|
|
34
|
+
- **`sdk.py`** — the ONLY public surface consumers import. Semver-frozen:
|
|
35
|
+
- `is_available() -> bool`
|
|
36
|
+
- `alert(topic, payload, *, level='info', notify=None, ttl_seconds=86400) -> bool`
|
|
37
|
+
- `register_job(spec, home=None) -> str` / `unregister_job(name, home=None) -> bool`
|
|
38
|
+
- `coord_create(title, **kw) -> str`
|
|
39
|
+
- `register_service(name, health_url=None, pid_file=None, home=None) -> str`
|
|
40
|
+
- **`scheduler_jobs.py`** — `load_jobs_with_dropins()` merges `jobs.yaml` + `jobs.d/*.yaml`; `register_job()`/`unregister_job()` write atomic per-job fragments. Runtime callers (daemon `scheduled_tasks.py`, `cli/scheduler_cmd.py`, `doctor.py`) all repointed to the merged loader. **Honours `SKCAPSTONE_HOME`** (was a bug, fixed in `9d9f93d`).
|
|
41
|
+
- **`cli/alerts.py`** — `skcapstone alerts` subscribes to `*.critical`/`*.error`/`*.warn` and styles consumer topics by severity suffix.
|
|
42
|
+
- **`service_health.py`** — `check_all_services()` unions `~/.skcapstone/registry/*.json` (written by `register_service`) with the built-in checks.
|
|
43
|
+
- Tests: `tests/test_sdk.py`, `test_jobs_dropins.py`, `test_alerts_consumer_topics.py`, `test_service_registry.py`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 3. The adapter pattern (copy this for every remaining consumer)
|
|
48
|
+
|
|
49
|
+
**Canonical reference: `skmemory/skmemory/integration.py`** (commit `be33179`). Each adapter:
|
|
50
|
+
|
|
51
|
+
1. Add `<pkg>/integration.py` with the optional-import guard:
|
|
52
|
+
```python
|
|
53
|
+
try:
|
|
54
|
+
from skcapstone import sdk as _sdk
|
|
55
|
+
except Exception:
|
|
56
|
+
_sdk = None
|
|
57
|
+
def is_present() -> bool:
|
|
58
|
+
if os.environ.get("SK_STANDALONE"): return False
|
|
59
|
+
if _sdk is None: return False
|
|
60
|
+
try: return bool(_sdk.is_available())
|
|
61
|
+
except Exception: return False
|
|
62
|
+
```
|
|
63
|
+
2. `alert(event, payload, level)` → `_sdk.alert(f"{SERVICE}.{level}", {"event": event, **payload}, level=level, notify=level in {"warn","error","critical"})` when present, else structured log.
|
|
64
|
+
**CRITICAL CONVENTION:** topic is `<service>.<severity>` (e.g. `skvoice.error`), and the semantic **event name goes in the payload `event` field — NOT the topic suffix.** Otherwise `skcapstone alerts`' `*.error`/`*.critical`/`*.warn` wildcards never match it. (This was a real bug caught building skmemory.)
|
|
65
|
+
3. `ensure_schedule()` → `_sdk.register_job({...})` (a `type: shell` job running the service's periodic CLI command) when present, else rely on the service's native systemd timer / thread loop.
|
|
66
|
+
4. `register_self(pid_file=None)` → `_sdk.register_service(SERVICE, pid_file=...)`.
|
|
67
|
+
5. **Wire into real call sites:** alert into the service's failure path; `ensure_schedule()` + `register_self()` into its startup / post-install lifecycle.
|
|
68
|
+
6. Add an optional `[skcapstone]` extra to `pyproject.toml` (`skcapstone = ["skcapstone>=0.6.8"]`). **Never a hard dependency.**
|
|
69
|
+
7. Tests `tests/test_integration_adapter.py`: standalone (`SK_STANDALONE=1`), absent (`monkeypatch.setattr(integration, "_sdk", None)`), integrated (sandbox `SKCAPSTONE_HOME` to `tmp_path` + `monkeypatch.setattr(skcapstone, "AGENT_HOME", str(tmp_path))`).
|
|
70
|
+
|
|
71
|
+
**Node/non-Python services (reference: `skgateway/src/integration.mjs`, commit `cc7bf1a`):** can't import the SDK — integrate **file-based** by writing the same `~/.skcapstone/pubsub/topics/<topic>/msg-*.json` and `~/.skcapstone/registry/<name>.json` formats. Validated round-trip: Node `alert()` → Python `PubSub.poll()` reads it back intact. Presence = shared home exists + `SK_STANDALONE` unset.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 4. What's left (the next session's work-list)
|
|
76
|
+
|
|
77
|
+
All tagged `sk-integration` on the coord board. Run `skcapstone coord status` / inspect `~/.skcapstone/coordination/tasks/`.
|
|
78
|
+
|
|
79
|
+
### Consumer adapters (mechanical mirrors of skmemory — for sonnet)
|
|
80
|
+
| Task | Repo | Native fallback to preserve | Suggested scheduled job |
|
|
81
|
+
|---|---|---|---|
|
|
82
|
+
| `eae9b815` | **skcomms** (`skcapstone-repos/skcomms`) | peer `_notify_others()` / log; daemon heartbeat thread + systemd | heartbeat/health; note skcomms is folding INTO skcomms (canonical) |
|
|
83
|
+
| `44b11628` | **capauth** | log (no native alerting yet) | key-rotation check (signing-daemon TODO — ensure_schedule may be a stub) |
|
|
84
|
+
| `66881a86` | **skvoice** | log; `while True` loop + skvoice systemd | service health / TTS-cache prune |
|
|
85
|
+
| `aaafe0d8` | **skseed** | log; pure kernel (periodic task type only) | belief-audit / germination; already duck-types AdaptedPrompt at `llm.py:40` |
|
|
86
|
+
| `fb925612` | **cloud9** | log; systemd `cloud9-daemon.timer` + launchd plist | rehydration/FEB-state check |
|
|
87
|
+
| `ad4f721a` | **skchat** | **OWNED BY ANOTHER THREAD — leave it.** Note: it already soft-bridges skcapstone MCP at `memory_bridge.py:24`; fold that into the one adapter. |
|
|
88
|
+
|
|
89
|
+
### Cross-cutting
|
|
90
|
+
- `71186ebb` — **dual-mode test harness**: parametrized over all consumers, asserts standalone AND integrated mode. This is the system acceptance gate for the EPIC.
|
|
91
|
+
- `4065db2b` — per-repo README "Integration modes" + `~/.skcapstone/` filesystem-contract section.
|
|
92
|
+
- `6b9a41a1` — note task (reference-pattern pointer); close once all adapters land.
|
|
93
|
+
|
|
94
|
+
### Final step
|
|
95
|
+
Once all adapters + cross-cutting are done, **complete the EPIC `fca7f138`** and consider squashing the `skcomms` superseded task notes.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 5. Gotchas / things to know
|
|
100
|
+
|
|
101
|
+
- **coord CLI:** `skcapstone coord claim <id> --agent <name>` and `complete <id> --agent <name>` (the `--agent` flag is required). `create` uses `--by`. Tasks are immutable after creation (no edit/update subcommand) — supersede by completing + creating a replacement.
|
|
102
|
+
- **Don't commit the other threads' work.** In `skcapstone` the skcomms→skcomms migration shares the repo; in `skmemory` the mxbai-cutover changes (`backends/pgvector_backend.py`, `cli.py`) are unstaged from another thread. Stage only your adapter files explicitly — never `git add -A`.
|
|
103
|
+
- **skcapstone remotes:** `github` is canonical (per `~/clawd/scripts/push-pending.sh`); `origin` and `forgejo` also exist and may be stale (the local `@{u}` tracks `forgejo`, which lags — verify against `github/main`).
|
|
104
|
+
- **Test sandboxing:** consumers without a conftest that sets `SKCAPSTONE_HOME` must sandbox it per-test, AND `monkeypatch.setattr(skcapstone, "AGENT_HOME", str(tmp_path))` because skcapstone captures `AGENT_HOME` at import.
|
|
105
|
+
- **Pre-existing broken tests (NOT yours):** `sksecurity/tests/test_truth_engine.py` fails to collect (imports a missing `_check_skmemory`); `skgateway/tests/classifier.test.mjs` has 2 pre-existing failures. Run adapter tests by file to avoid these.
|
|
106
|
+
- **Leak check after integrated tests:** `ls ~/.skcapstone/config/jobs.d/<svc>_*.yaml ~/.skcapstone/registry/<svc>.json` should be clean — if a fragment leaks to the real home, a test isn't sandboxing `SKCAPSTONE_HOME`.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 6. Commits / branches (all pushed)
|
|
111
|
+
|
|
112
|
+
| Repo | Branch | Commits |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| skcapstone | `main` (→ `github`) | `c5d8d7c` backbone · `9d9f93d` home-fix+convention · `d90dfbf` ADR §3.5 |
|
|
115
|
+
| skmemory | `docs-first-principles` | `be33179` reference adapter |
|
|
116
|
+
| sksecurity | `main` | `e65979b` threat-sharing adapter |
|
|
117
|
+
| skgateway | `main` | `cc7bf1a` Node file-based bridge |
|
|
@@ -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/<host>/state.json"]
|
|
35
|
+
RUN --> LOG["~/.skcapstone/scheduler/<host>/logs/<job>-<ts>.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
|