Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83e3be9298 |
@@ -311,6 +311,13 @@ def _handoff_mine_and_pin(cfg, args, backend, snapshot: str, dry: bool):
|
|||||||
# branch, or every later run re-scans the same stale window.
|
# branch, or every later run re-scans the same stale window.
|
||||||
state.set_last_harvest(project, started)
|
state.set_last_harvest(project, started)
|
||||||
state.save()
|
state.save()
|
||||||
|
# Drop the pinned (often empty) digests so the NEXT run re-harvests
|
||||||
|
# instead of reloading a []-pin and never calling harvest again.
|
||||||
|
try:
|
||||||
|
if os.path.exists(digests_path):
|
||||||
|
os.remove(digests_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
return 0, None
|
return 0, None
|
||||||
payload = make_tasks_payload(
|
payload = make_tasks_payload(
|
||||||
tasks,
|
tasks,
|
||||||
@@ -342,13 +349,21 @@ def _run_handoff(cfg, args, *, seed_tasks, task_meta: Dict[str, Any], dry: bool)
|
|||||||
tasks = seed_tasks
|
tasks = seed_tasks
|
||||||
if tasks is None:
|
if tasks is None:
|
||||||
snapshot = os.path.join(hdir, "tasks.json")
|
snapshot = os.path.join(hdir, "tasks.json")
|
||||||
|
tasks = None
|
||||||
if os.path.exists(snapshot):
|
if os.path.exists(snapshot):
|
||||||
tasks, _meta = load_tasks_file(
|
try:
|
||||||
snapshot,
|
tasks, _meta = load_tasks_file(
|
||||||
holdout_fraction=cfg.get("holdout_fraction", 0.34),
|
snapshot,
|
||||||
seed=cfg.get("seed", 42),
|
holdout_fraction=cfg.get("holdout_fraction", 0.34),
|
||||||
)
|
seed=cfg.get("seed", 42),
|
||||||
else:
|
)
|
||||||
|
except (ValueError, json.JSONDecodeError, OSError):
|
||||||
|
# Interrupted/corrupt pin from an earlier round: re-mine instead
|
||||||
|
# of crashing the run.
|
||||||
|
print("[sleep] handoff: tasks.json unreadable — re-mining",
|
||||||
|
file=sys.stderr)
|
||||||
|
tasks = None
|
||||||
|
if tasks is None:
|
||||||
rc, tasks = _handoff_mine_and_pin(cfg, args, backend, snapshot, dry)
|
rc, tasks = _handoff_mine_and_pin(cfg, args, backend, snapshot, dry)
|
||||||
if tasks is None:
|
if tasks is None:
|
||||||
return rc
|
return rc
|
||||||
|
|||||||
@@ -215,6 +215,45 @@ class TestHandoffCli(unittest.TestCase):
|
|||||||
state = SleepState.load(cfg.state_path)
|
state = SleepState.load(cfg.state_path)
|
||||||
self.assertIsNotNone(state.last_harvest_for(proj))
|
self.assertIsNotNone(state.last_harvest_for(proj))
|
||||||
|
|
||||||
|
def test_no_tasks_removes_digests_pin_so_next_run_reharvests(self):
|
||||||
|
"""After a no-task night the (often empty) digests.json pin must be
|
||||||
|
removed, or every later run reloads a []-pin and never harvests again."""
|
||||||
|
from skillopt_sleep.__main__ import main
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as proj, \
|
||||||
|
tempfile.TemporaryDirectory() as home:
|
||||||
|
hdir = os.path.join(proj, ".skillopt-sleep-handoff")
|
||||||
|
os.makedirs(hdir, exist_ok=True)
|
||||||
|
digests_path = os.path.join(hdir, "digests.json")
|
||||||
|
with open(digests_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump([], f)
|
||||||
|
rc = main([
|
||||||
|
"run", "--backend", "handoff",
|
||||||
|
"--project", proj,
|
||||||
|
"--claude-home", os.path.join(home, ".claude"),
|
||||||
|
])
|
||||||
|
self.assertEqual(rc, 0)
|
||||||
|
# the empty pin must be gone so the next run re-harvests
|
||||||
|
self.assertFalse(os.path.exists(digests_path))
|
||||||
|
|
||||||
|
def test_corrupt_tasks_pin_falls_back_to_remine(self):
|
||||||
|
"""An interrupted/corrupt tasks.json snapshot must not crash the run."""
|
||||||
|
from skillopt_sleep.__main__ import main
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as proj, \
|
||||||
|
tempfile.TemporaryDirectory() as home:
|
||||||
|
hdir = os.path.join(proj, ".skillopt-sleep-handoff")
|
||||||
|
os.makedirs(hdir, exist_ok=True)
|
||||||
|
with open(os.path.join(hdir, "tasks.json"), "w", encoding="utf-8") as f:
|
||||||
|
f.write('{"tasks": [ truncated...')
|
||||||
|
rc = main([
|
||||||
|
"run", "--backend", "handoff",
|
||||||
|
"--project", proj,
|
||||||
|
"--claude-home", os.path.join(home, ".claude"),
|
||||||
|
])
|
||||||
|
# corrupt snapshot -> re-mine -> no tasks -> 0 (not a crash)
|
||||||
|
self.assertEqual(rc, 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user