Jonathan Haas commited on
Commit
61cd78a
·
1 Parent(s): 0291c72

refactor: extract cli entrypoint orchestration helpers

Browse files
TODO.md CHANGED
@@ -1,4 +1,4 @@
1
- # Jarvis TODO — Wave 74/106 Runtime Decomposition
2
 
3
  Last updated: 2026-02-27
4
 
@@ -8,8 +8,8 @@ Last updated: 2026-02-27
8
  - `[x]` Completed
9
 
10
  ## Completion summary
11
- - Total items: 279
12
- - Completed: 279
13
  - Remaining: 0
14
 
15
  ---
@@ -402,6 +402,17 @@ Last updated: 2026-02-27
402
  - [x] `W106-AJ07` Run full quality/security/readiness gates.
403
  - [x] `W106-AJ08` Update TODO + tranche snapshot.
404
 
 
 
 
 
 
 
 
 
 
 
 
405
  ---
406
 
407
  ## Outcome snapshot (latest completed tranche)
@@ -433,6 +444,7 @@ Last updated: 2026-02-27
433
  - `runtime_voice_status.py`
434
  - `runtime_observability_snapshot.py`
435
  - `runtime_watchdog.py`
 
436
  - Compatibility preservation:
437
  - `services.py` now re-exports defaults/constants via `_services_defaults` alias.
438
  - Mutable bootstrap defaults (`_proactive_state`, `_privacy_posture`, `_motion_safety_envelope`, `_release_channel_state`) now initialize from factory helpers.
@@ -472,6 +484,7 @@ Last updated: 2026-02-27
472
  - Persona-style update + memory summary upsert moved from `Jarvis._set_persona_style` into `runtime_preferences.set_persona_style`.
473
  - Observability status publication moved from `Jarvis._publish_observability_status` into `runtime_observability_status.publish_observability_status`.
474
  - Telemetry tool-error counter refresh moved from `Jarvis._refresh_tool_error_counters` into `runtime_telemetry.refresh_tool_error_counters`.
 
475
  - Validation (Wave 75D/E):
476
  - `uv run pytest -q tests/test_import_boundaries.py`: `177 passed`.
477
  - `uv run pytest -q tests/test_runtime_operator_status.py tests/test_main_lifecycle.py -k "operator_auth or startup_summary_lines_include_core_status"`: `8 passed`.
@@ -515,7 +528,9 @@ Last updated: 2026-02-27
515
  - `uv run pytest -q tests/test_runtime_watchdog.py tests/test_runtime_observability_snapshot.py tests/test_runtime_voice_status.py tests/test_runtime_turn.py tests/test_main_audio.py`: `21 passed`.
516
  - `uv run pytest -q tests/test_runtime_telemetry.py tests/test_runtime_observability_status.py tests/test_main_lifecycle.py tests/test_import_boundaries.py -k "runtime_telemetry or runtime_observability_status or publish_observability_status or refresh_tool_error_counters or import_boundary"`: `208 passed, 35 deselected`.
517
  - `uv run pytest -q tests/test_runtime_watchdog.py tests/test_runtime_observability_snapshot.py tests/test_runtime_voice_status.py tests/test_runtime_preferences.py tests/test_runtime_multimodal.py`: `19 passed`.
 
 
518
  - `./scripts/test_soak_reliability.sh 2`: accepted; `cycles_completed=2/2`, `phase_count=8`, `failed_count=0`, artifact `.artifacts/quality/soak-profile-fast-repeat2.json`.
519
- - `make check`: `831 passed`.
520
- - `make security-gate`: `831 passed`; fault subset `3 passed`.
521
  - `./scripts/jarvis_readiness.sh fast`: pass; strict eval `159/159`.
 
1
+ # Jarvis TODO — Wave 74/107 Runtime Decomposition
2
 
3
  Last updated: 2026-02-27
4
 
 
8
  - `[x]` Completed
9
 
10
  ## Completion summary
11
+ - Total items: 287
12
+ - Completed: 287
13
  - Remaining: 0
14
 
15
  ---
 
402
  - [x] `W106-AJ07` Run full quality/security/readiness gates.
403
  - [x] `W106-AJ08` Update TODO + tranche snapshot.
404
 
405
+ ## AK) Wave 107 (completed): main entrypoint orchestration extraction
406
+
407
+ - [x] `W107-AK01` Profile top-level `main()` orchestration in `src/jarvis/__main__.py`.
408
+ - [x] `W107-AK02` Create `runtime_entrypoint.py` for backup/restore handling and event-loop bootstrap helpers.
409
+ - [x] `W107-AK03` Rewire `main()` to runtime entrypoint helpers while preserving behavior.
410
+ - [x] `W107-AK04` Preserve backup/restore JSON error semantics and signal-driven shutdown behavior.
411
+ - [x] `W107-AK05` Add focused unit coverage in `tests/test_runtime_entrypoint.py`.
412
+ - [x] `W107-AK06` Extend import-boundary coverage for `runtime_entrypoint`.
413
+ - [x] `W107-AK07` Run full quality/security/readiness gates.
414
+ - [x] `W107-AK08` Update TODO + tranche snapshot.
415
+
416
  ---
417
 
418
  ## Outcome snapshot (latest completed tranche)
 
444
  - `runtime_voice_status.py`
445
  - `runtime_observability_snapshot.py`
446
  - `runtime_watchdog.py`
447
+ - `runtime_entrypoint.py`
448
  - Compatibility preservation:
449
  - `services.py` now re-exports defaults/constants via `_services_defaults` alias.
450
  - Mutable bootstrap defaults (`_proactive_state`, `_privacy_posture`, `_motion_safety_envelope`, `_release_channel_state`) now initialize from factory helpers.
 
484
  - Persona-style update + memory summary upsert moved from `Jarvis._set_persona_style` into `runtime_preferences.set_persona_style`.
485
  - Observability status publication moved from `Jarvis._publish_observability_status` into `runtime_observability_status.publish_observability_status`.
486
  - Telemetry tool-error counter refresh moved from `Jarvis._refresh_tool_error_counters` into `runtime_telemetry.refresh_tool_error_counters`.
487
+ - CLI backup/restore and asyncio event-loop orchestration moved from `main()` body into `runtime_entrypoint` helpers.
488
  - Validation (Wave 75D/E):
489
  - `uv run pytest -q tests/test_import_boundaries.py`: `177 passed`.
490
  - `uv run pytest -q tests/test_runtime_operator_status.py tests/test_main_lifecycle.py -k "operator_auth or startup_summary_lines_include_core_status"`: `8 passed`.
 
528
  - `uv run pytest -q tests/test_runtime_watchdog.py tests/test_runtime_observability_snapshot.py tests/test_runtime_voice_status.py tests/test_runtime_turn.py tests/test_main_audio.py`: `21 passed`.
529
  - `uv run pytest -q tests/test_runtime_telemetry.py tests/test_runtime_observability_status.py tests/test_main_lifecycle.py tests/test_import_boundaries.py -k "runtime_telemetry or runtime_observability_status or publish_observability_status or refresh_tool_error_counters or import_boundary"`: `208 passed, 35 deselected`.
530
  - `uv run pytest -q tests/test_runtime_watchdog.py tests/test_runtime_observability_snapshot.py tests/test_runtime_voice_status.py tests/test_runtime_preferences.py tests/test_runtime_multimodal.py`: `19 passed`.
531
+ - `uv run pytest -q tests/test_runtime_entrypoint.py tests/test_import_boundaries.py tests/test_main_lifecycle.py -k "runtime_entrypoint or import_boundary or backup or restore or main"`: `242 passed`.
532
+ - `uv run pytest -q tests/test_runtime_observability_status.py tests/test_runtime_telemetry.py tests/test_runtime_watchdog.py tests/test_runtime_observability_snapshot.py tests/test_runtime_voice_status.py`: `14 passed`.
533
  - `./scripts/test_soak_reliability.sh 2`: accepted; `cycles_completed=2/2`, `phase_count=8`, `failed_count=0`, artifact `.artifacts/quality/soak-profile-fast-repeat2.json`.
534
+ - `make check`: `836 passed`.
535
+ - `make security-gate`: `836 passed`; fault subset `3 passed`.
536
  - `./scripts/jarvis_readiness.sh fast`: pass; strict eval `159/159`.
src/jarvis/__main__.py CHANGED
@@ -15,9 +15,7 @@ from __future__ import annotations
15
 
16
  import argparse
17
  import asyncio
18
- import json
19
  import logging
20
- import signal
21
  import time
22
  import threading
23
  from pathlib import Path
@@ -161,6 +159,10 @@ from jarvis.runtime_multimodal import (
161
  multimodal_grounding_snapshot_for_runtime as _runtime_multimodal_grounding_snapshot_for_runtime,
162
  )
163
  from jarvis.runtime_watchdog import watchdog_loop as _runtime_watchdog_loop
 
 
 
 
164
  from jarvis.runtime_constants import (
165
  ATTENTION_RECENCY_SEC,
166
  CONVERSATION_TRACE_MAXLEN,
@@ -988,40 +990,16 @@ def main():
988
  datefmt="%H:%M:%S",
989
  )
990
 
991
- if args.backup or args.restore:
992
- config = Config()
993
- try:
994
- if args.backup:
995
- result = create_backup_bundle(config, args.backup)
996
- else:
997
- result = restore_backup_bundle(config, args.restore, overwrite=bool(args.force))
998
- except Exception as exc:
999
- print(json.dumps({"ok": False, "error": str(exc)}, indent=2))
1000
- raise SystemExit(1) from exc
1001
- print(json.dumps(result, indent=2))
1002
  return
1003
 
1004
  jarvis = Jarvis(args)
1005
-
1006
- loop = asyncio.new_event_loop()
1007
- asyncio.set_event_loop(loop)
1008
- task = loop.create_task(jarvis.run())
1009
-
1010
- def shutdown(sig, frame):
1011
- task.cancel()
1012
-
1013
- signal.signal(signal.SIGINT, shutdown)
1014
- if hasattr(signal, "SIGTERM"):
1015
- signal.signal(signal.SIGTERM, shutdown)
1016
-
1017
- try:
1018
- loop.run_until_complete(task)
1019
- finally:
1020
- with suppress(Exception):
1021
- loop.run_until_complete(loop.shutdown_asyncgens())
1022
- with suppress(Exception):
1023
- loop.run_until_complete(loop.shutdown_default_executor())
1024
- loop.close()
1025
 
1026
 
1027
  if __name__ == "__main__":
 
15
 
16
  import argparse
17
  import asyncio
 
18
  import logging
 
19
  import time
20
  import threading
21
  from pathlib import Path
 
159
  multimodal_grounding_snapshot_for_runtime as _runtime_multimodal_grounding_snapshot_for_runtime,
160
  )
161
  from jarvis.runtime_watchdog import watchdog_loop as _runtime_watchdog_loop
162
+ from jarvis.runtime_entrypoint import (
163
+ maybe_run_backup_or_restore as _runtime_maybe_run_backup_or_restore,
164
+ run_jarvis_event_loop as _runtime_run_jarvis_event_loop,
165
+ )
166
  from jarvis.runtime_constants import (
167
  ATTENTION_RECENCY_SEC,
168
  CONVERSATION_TRACE_MAXLEN,
 
990
  datefmt="%H:%M:%S",
991
  )
992
 
993
+ if _runtime_maybe_run_backup_or_restore(
994
+ args,
995
+ config_class=Config,
996
+ create_backup_bundle_fn=create_backup_bundle,
997
+ restore_backup_bundle_fn=restore_backup_bundle,
998
+ ):
 
 
 
 
 
999
  return
1000
 
1001
  jarvis = Jarvis(args)
1002
+ _runtime_run_jarvis_event_loop(jarvis)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1003
 
1004
 
1005
  if __name__ == "__main__":
src/jarvis/runtime_entrypoint.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Entrypoint orchestration helpers for Jarvis CLI runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import signal
8
+ from contextlib import suppress
9
+ from typing import Any, Callable
10
+
11
+
12
+ def maybe_run_backup_or_restore(
13
+ args: Any,
14
+ *,
15
+ config_class: Callable[[], Any],
16
+ create_backup_bundle_fn: Callable[[Any, str], dict[str, Any]],
17
+ restore_backup_bundle_fn: Callable[[Any, str], dict[str, Any]],
18
+ ) -> bool:
19
+ if not (args.backup or args.restore):
20
+ return False
21
+ config = config_class()
22
+ try:
23
+ if args.backup:
24
+ result = create_backup_bundle_fn(config, args.backup)
25
+ else:
26
+ result = restore_backup_bundle_fn(config, args.restore, overwrite=bool(args.force))
27
+ except Exception as exc:
28
+ print(json.dumps({"ok": False, "error": str(exc)}, indent=2))
29
+ raise SystemExit(1) from exc
30
+ print(json.dumps(result, indent=2))
31
+ return True
32
+
33
+
34
+ def run_jarvis_event_loop(jarvis: Any) -> None:
35
+ loop = asyncio.new_event_loop()
36
+ asyncio.set_event_loop(loop)
37
+ task = loop.create_task(jarvis.run())
38
+
39
+ def shutdown(sig, frame):
40
+ task.cancel()
41
+
42
+ signal.signal(signal.SIGINT, shutdown)
43
+ if hasattr(signal, "SIGTERM"):
44
+ signal.signal(signal.SIGTERM, shutdown)
45
+
46
+ try:
47
+ loop.run_until_complete(task)
48
+ finally:
49
+ with suppress(Exception):
50
+ loop.run_until_complete(loop.shutdown_asyncgens())
51
+ with suppress(Exception):
52
+ loop.run_until_complete(loop.shutdown_default_executor())
53
+ loop.close()
tests/test_import_boundaries.py CHANGED
@@ -19,6 +19,7 @@ import pytest
19
  ("jarvis.runtime_operator_server", "jarvis.__main__"),
20
  ("jarvis.runtime_audio_output", "jarvis.__main__"),
21
  ("jarvis.runtime_lifecycle", "jarvis.__main__"),
 
22
  ("jarvis.runtime_observability_snapshot", "jarvis.__main__"),
23
  ("jarvis.runtime_observability_status", "jarvis.__main__"),
24
  ("jarvis.runtime_memory_correction", "jarvis.__main__"),
 
19
  ("jarvis.runtime_operator_server", "jarvis.__main__"),
20
  ("jarvis.runtime_audio_output", "jarvis.__main__"),
21
  ("jarvis.runtime_lifecycle", "jarvis.__main__"),
22
+ ("jarvis.runtime_entrypoint", "jarvis.__main__"),
23
  ("jarvis.runtime_observability_snapshot", "jarvis.__main__"),
24
  ("jarvis.runtime_observability_status", "jarvis.__main__"),
25
  ("jarvis.runtime_memory_correction", "jarvis.__main__"),
tests/test_runtime_entrypoint.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from types import SimpleNamespace
4
+ from unittest.mock import patch
5
+
6
+ import pytest
7
+
8
+ from jarvis.runtime_entrypoint import (
9
+ maybe_run_backup_or_restore,
10
+ run_jarvis_event_loop,
11
+ )
12
+
13
+
14
+ def test_maybe_run_backup_or_restore_returns_false_when_no_maintenance_args() -> None:
15
+ args = SimpleNamespace(backup=None, restore=None, force=False)
16
+ ran = maybe_run_backup_or_restore(
17
+ args,
18
+ config_class=lambda: object(),
19
+ create_backup_bundle_fn=lambda config, path: {},
20
+ restore_backup_bundle_fn=lambda config, path, overwrite=False: {},
21
+ )
22
+ assert ran is False
23
+
24
+
25
+ def test_maybe_run_backup_or_restore_backup_prints_result(capsys) -> None:
26
+ args = SimpleNamespace(backup="/tmp/backup.zip", restore=None, force=False)
27
+ ran = maybe_run_backup_or_restore(
28
+ args,
29
+ config_class=lambda: object(),
30
+ create_backup_bundle_fn=lambda config, path: {"ok": True, "path": path},
31
+ restore_backup_bundle_fn=lambda config, path, overwrite=False: {},
32
+ )
33
+ captured = capsys.readouterr()
34
+ assert ran is True
35
+ assert '"ok": true' in captured.out.lower()
36
+ assert "/tmp/backup.zip" in captured.out
37
+
38
+
39
+ def test_maybe_run_backup_or_restore_failure_raises_system_exit(capsys) -> None:
40
+ args = SimpleNamespace(backup="/tmp/backup.zip", restore=None, force=False)
41
+
42
+ with pytest.raises(SystemExit) as exc_info:
43
+ maybe_run_backup_or_restore(
44
+ args,
45
+ config_class=lambda: object(),
46
+ create_backup_bundle_fn=lambda config, path: (_ for _ in ()).throw(RuntimeError("boom")),
47
+ restore_backup_bundle_fn=lambda config, path, overwrite=False: {},
48
+ )
49
+
50
+ captured = capsys.readouterr()
51
+ assert exc_info.value.code == 1
52
+ assert '"ok": false' in captured.out.lower()
53
+ assert "boom" in captured.out
54
+
55
+
56
+ def test_run_jarvis_event_loop_bootstraps_and_cleans_up() -> None:
57
+ class _FakeTask:
58
+ def __init__(self) -> None:
59
+ self.cancelled = False
60
+
61
+ def cancel(self) -> None:
62
+ self.cancelled = True
63
+
64
+ class _FakeLoop:
65
+ def __init__(self) -> None:
66
+ self.calls: list[object] = []
67
+ self.task = _FakeTask()
68
+ self.closed = False
69
+
70
+ def create_task(self, coro):
71
+ self.calls.append(("create_task", coro))
72
+ return self.task
73
+
74
+ def run_until_complete(self, awaitable):
75
+ self.calls.append(("run_until_complete", awaitable))
76
+ return None
77
+
78
+ def shutdown_asyncgens(self):
79
+ return "shutdown_asyncgens"
80
+
81
+ def shutdown_default_executor(self):
82
+ return "shutdown_default_executor"
83
+
84
+ def close(self):
85
+ self.closed = True
86
+
87
+ fake_loop = _FakeLoop()
88
+ jarvis = SimpleNamespace(run=lambda: "run-coro")
89
+ signal_calls: list[tuple[object, object]] = []
90
+
91
+ with patch("jarvis.runtime_entrypoint.asyncio.new_event_loop", return_value=fake_loop), \
92
+ patch("jarvis.runtime_entrypoint.asyncio.set_event_loop") as set_event_loop, \
93
+ patch("jarvis.runtime_entrypoint.signal.signal", side_effect=lambda sig, handler: signal_calls.append((sig, handler))):
94
+ run_jarvis_event_loop(jarvis)
95
+
96
+ set_event_loop.assert_called_once_with(fake_loop)
97
+ assert fake_loop.closed is True
98
+ assert any(call[0] == "create_task" for call in fake_loop.calls)
99
+ assert len(signal_calls) >= 1