Skip to content

Commit 0f1f7e7

Browse files
fix: make launchd service importable
1 parent ff4591a commit 0f1f7e7

4 files changed

Lines changed: 19 additions & 8 deletions

File tree

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,18 +315,21 @@ See `docs/safety.md` for details.
315315
- memory/skill proposals
316316
- review/apply lifecycle
317317
- dataset exports and manifests
318-
- replay benchmark reports
318+
- benchmark reports
319319
- training config generation
320320
- Hermes setup/status/controller run UX
321321
- persisted controller run history
322+
- macOS launchd service plist generation
323+
- controller-managed dataset auto-export behind explicit policy gates
322324

323325
### Next product steps
324326

325-
1. Add a real background service runner (`launchd` first, then Linux systemd/cron).
326-
2. Add dataset readiness judging before any training plan or training runner.
327-
3. Add evaluator staleness detection when evaluator component hashes change.
328-
4. Add stronger evidence-trust scoring so exports learn from verified work, not claims.
329-
5. Add training planner artifacts, still approval-gated and no auto-run.
327+
0. Run a local real-system smoke test against this machine's Hermes `state.db` in an isolated SkillLoop project. Verify `setup --connect hermes --start`, `status`, `controller history/show`, `service install/status/uninstall`, and generated dataset manifests before loading any persistent service.
328+
1. Build the P1 dataset readiness judge before any training plan or training runner. It should read dataset manifests and return `ready`, `collect_more_data`, or `blocked` with machine-readable reasons.
329+
2. Add evaluator staleness detection when evaluator component hashes change, so controller-managed datasets do not depend on outdated scores.
330+
3. Add stronger evidence-trust scoring so exports learn from verified work, not assistant claims.
331+
4. Add approval-gated training planner artifacts after readiness/staleness gates exist.
332+
5. Add Linux service generation (`systemd` unit or cron) after the macOS launchd path has real local use.
330333

331334
## Development checks
332335

docs/architecture.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,9 @@ This boundary is deliberate: it keeps the learning layer inspectable, reviewable
222222

223223
## Roadmap priorities
224224

225+
0. Run a local real-system smoke test against this machine's Hermes `state.db` in an isolated SkillLoop project. Verify controller ticks, status/history/show, launchd plist generation, service uninstall, and dataset manifest generation before loading any persistent service.
225226
1. Add a dataset readiness judge before any training plan is recommended.
226227
2. Add evaluator staleness detection when evaluator code/provenance changes.
227228
3. Add stronger evidence-trust scoring so learning artifacts depend on tool/user evidence rather than assistant claims.
228-
4. Add Linux service generation (`systemd` unit or cron) after the macOS launchd path has had real local use.
229-
5. Add approval-gated training plans; keep training execution separate until readiness, cost, evaluation, and promotion gates exist.
229+
4. Add approval-gated training plans; keep training execution separate until readiness, cost, evaluation, and promotion gates exist.
230+
5. Add Linux service generation (`systemd` unit or cron) after the macOS launchd path has had real local use.

skillloop/service.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class ServiceSpec:
2020
state_dir: Path
2121
interval_seconds: int = DEFAULT_INTERVAL_SECONDS
2222
python_executable: str = sys.executable
23+
python_path: str | None = None
2324

2425
@property
2526
def stdout_path(self) -> Path:
@@ -58,6 +59,7 @@ def build_service_spec(
5859
state_dir=state,
5960
interval_seconds=interval_seconds,
6061
python_executable=python_executable or sys.executable,
62+
python_path=str(Path(__file__).resolve().parents[1]),
6163
)
6264

6365

@@ -80,6 +82,7 @@ def launchd_plist(spec: ServiceSpec) -> dict[str, Any]:
8082
"StandardErrorPath": str(spec.stderr_path),
8183
"EnvironmentVariables": {
8284
"PYTHONUNBUFFERED": "1",
85+
**({"PYTHONPATH": spec.python_path} if spec.python_path else {}),
8386
},
8487
}
8588

@@ -121,6 +124,7 @@ def write_service_metadata(spec: ServiceSpec, *, kind: str, path: str | Path) ->
121124
"controller",
122125
"run",
123126
],
127+
"python_path": spec.python_path,
124128
}
125129
spec.metadata_path.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
126130
return spec.metadata_path

tests/test_service.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def test_launchd_plist_runs_controller_tick(tmp_path):
3131
"controller",
3232
"run",
3333
]
34+
assert payload["EnvironmentVariables"]["PYTHONUNBUFFERED"] == "1"
35+
assert payload["EnvironmentVariables"]["PYTHONPATH"]
3436

3537

3638
def test_service_install_status_and_uninstall_cli(tmp_path, capsys):
@@ -57,6 +59,7 @@ def test_service_install_status_and_uninstall_cli(tmp_path, capsys):
5759
assert metadata["kind"] == "launchd"
5860
assert metadata["label"] == "com.skillloop.test"
5961
assert metadata["path"] == str(plist_path)
62+
assert metadata["python_path"]
6063

6164
assert main(["--path", str(tmp_path), "service", "status"]) == 0
6265
assert main(["--path", str(tmp_path), "service", "status", "--json"]) == 0

0 commit comments

Comments
 (0)