personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(supervisor): run pending maint tasks before spawning children

Closes the journal-writer/migration race by running maintenance tasks
in the supervisor's main process before any writer child (convey, sense,
cortex) is spawned. Migration code moves from convey/maint.py to
think/maint.py (clean break, no shim) — convey's startup block and
import are removed; sol maint CLI and the --skip-maint flag are
unchanged (flag is now a no-op). Phase 2 will add standalone-writer
guards so CLIs like sol dream refuse to start without a running
supervisor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+24 -16
-9
convey/cli.py
··· 63 63 64 64 def main() -> None: 65 65 """Main CLI entry point for convey command.""" 66 - from pathlib import Path 67 - 68 66 from think.utils import ( 69 67 get_journal, 70 68 setup_cli, ··· 72 70 ) 73 71 74 72 from . import create_app 75 - from .maint import run_pending_tasks 76 73 77 74 parser = argparse.ArgumentParser(description="Convey web interface") 78 75 parser.add_argument( ··· 88 85 ) 89 86 args = setup_cli(parser) 90 87 journal = get_journal() 91 - 92 - # Run pending maintenance tasks before starting 93 - if not args.skip_maint: 94 - ran, succeeded = run_pending_tasks(Path(journal)) 95 - if ran > 0: 96 - logger.info(f"Completed {succeeded}/{ran} maintenance task(s)") 97 88 98 89 app = create_app(journal) 99 90 password = _resolve_config_password_hash()
convey/maint.py think/maint.py
+1 -1
convey/maint_cli.py
··· 20 20 21 21 from think.utils import get_journal, setup_cli 22 22 23 - from .maint import ( 23 + from think.maint import ( 24 24 get_state_file, 25 25 get_task_by_name, 26 26 get_task_status,
+2 -2
docs/APPS.md
··· 455 455 456 456 ### 11. `maint/` - Maintenance Tasks 457 457 458 - Define one-time maintenance scripts that run automatically on Convey startup. 458 + Define one-time maintenance scripts that run automatically when supervisor starts. 459 459 460 460 **Key Points:** 461 461 - Create `maint/` directory with standalone Python scripts (each with a `main()` function) ··· 468 468 469 469 **Reference implementations:** 470 470 - Example task: `apps/entities/maint/001_migrate_to_journal_entities.py` - real migration task demonstrating maint patterns 471 - - Discovery logic: `convey/maint.py` - `discover_tasks()`, `run_task()` 471 + - Discovery logic: `think/maint.py` - `discover_tasks()`, `run_task()` 472 472 473 473 --- 474 474
+4 -4
tests/test_maint.py
··· 8 8 9 9 import pytest 10 10 11 - from convey.maint import ( 11 + from think.maint import ( 12 12 MaintTask, 13 13 get_state_file, 14 14 get_task_status, ··· 115 115 def mock_discover_tasks(): 116 116 return tasks 117 117 118 - monkeypatch.setattr("convey.maint.discover_tasks", mock_discover_tasks) 118 + monkeypatch.setattr("think.maint.discover_tasks", mock_discover_tasks) 119 119 120 120 # Success task with timestamp 121 121 success_file = get_state_file(temp_journal, "chat", "done") ··· 164 164 def mock_discover_tasks(): 165 165 return tasks 166 166 167 - monkeypatch.setattr("convey.maint.discover_tasks", mock_discover_tasks) 167 + monkeypatch.setattr("think.maint.discover_tasks", mock_discover_tasks) 168 168 169 169 # Success task with line events and duration 170 170 success_file = get_state_file(temp_journal, "chat", "done") ··· 199 199 def mock_discover_tasks(): 200 200 return tasks 201 201 202 - monkeypatch.setattr("convey.maint.discover_tasks", mock_discover_tasks) 202 + monkeypatch.setattr("think.maint.discover_tasks", mock_discover_tasks) 203 203 204 204 # In-progress task: exec event but no exit event 205 205 state_file = get_state_file(temp_journal, "chat", "running")
+17
think/supervisor.py
··· 21 21 22 22 from think import routines, scheduler 23 23 from think.callosum import CallosumConnection, CallosumServer 24 + from think.maint import run_pending_tasks 24 25 from think.runner import DailyLogWriter 25 26 from think.runner import ManagedProcess as RunnerManagedProcess 26 27 from think.utils import ( ··· 1434 1435 1435 1436 # Remote mode: run sync instead of local processing 1436 1437 _is_remote_mode = bool(args.remote) 1438 + 1439 + # Run pending journal-maintenance tasks before spawning any writer children. 1440 + # Callosum isn't up yet (emit_fn=None); migrations log through supervisor's logger only. 1441 + try: 1442 + ran, succeeded = run_pending_tasks(journal_path, emit_fn=None) 1443 + if ran > 0: 1444 + if ran == succeeded: 1445 + logging.info("Completed %d/%d maintenance task(s)", succeeded, ran) 1446 + else: 1447 + logging.error( 1448 + "Maintenance tasks completed with failures: %d/%d succeeded", 1449 + succeeded, 1450 + ran, 1451 + ) 1452 + except Exception: 1453 + logging.exception("Maintenance runner raised; continuing startup") 1437 1454 1438 1455 # Start Callosum in-process first - it's the message bus that other services depend on 1439 1456 try: