diff --git a/solstone/apps/chat/tests/test_liveness_chat_bar.py b/solstone/apps/chat/tests/test_liveness_chat_bar.py index f8f6aea62..25dcdc787 100644 --- a/solstone/apps/chat/tests/test_liveness_chat_bar.py +++ b/solstone/apps/chat/tests/test_liveness_chat_bar.py @@ -40,7 +40,7 @@ def chat_client(tmp_path, monkeypatch): (config_dir / "journal.json").write_text( json.dumps( { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, } ) + "\n", diff --git a/solstone/apps/chat/tests/test_liveness_live.py b/solstone/apps/chat/tests/test_liveness_live.py index bc58ec003..c59ad1e0b 100644 --- a/solstone/apps/chat/tests/test_liveness_live.py +++ b/solstone/apps/chat/tests/test_liveness_live.py @@ -19,7 +19,7 @@ def chat_client(tmp_path, monkeypatch): (config_dir / "journal.json").write_text( json.dumps( { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, } ) + "\n", diff --git a/solstone/apps/chat/tests/test_origin_tag_live.py b/solstone/apps/chat/tests/test_origin_tag_live.py index e5d440dee..8760ab396 100644 --- a/solstone/apps/chat/tests/test_origin_tag_live.py +++ b/solstone/apps/chat/tests/test_origin_tag_live.py @@ -25,7 +25,7 @@ def chat_client(tmp_path, monkeypatch): (config_dir / "journal.json").write_text( json.dumps( { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, } ) + "\n", diff --git a/solstone/apps/chat/tests/test_routes.py b/solstone/apps/chat/tests/test_routes.py index 212a8d650..26eda3361 100644 --- a/solstone/apps/chat/tests/test_routes.py +++ b/solstone/apps/chat/tests/test_routes.py @@ -407,7 +407,7 @@ def test_state_missing_identity_returns_default_names(tmp_path, monkeypatch): config_dir = journal / "config" config_dir.mkdir(parents=True) (config_dir / "journal.json").write_text( - json.dumps({"setup": {"completed_at": "2026-05-09T00:00:00Z"}}) + "\n", + json.dumps({"setup": {"completed_at": 1700000000000}}) + "\n", encoding="utf-8", ) _set_today(monkeypatch, "20990110") diff --git a/solstone/apps/settings/tests/test_chat_routes.py b/solstone/apps/settings/tests/test_chat_routes.py index edfd3990e..8bd61d3c2 100644 --- a/solstone/apps/settings/tests/test_chat_routes.py +++ b/solstone/apps/settings/tests/test_chat_routes.py @@ -20,7 +20,7 @@ CHAT_THINKING_COPY_NAMES = ( def _base_config() -> dict: return { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, } diff --git a/solstone/apps/settings/tests/test_facet_config_routes.py b/solstone/apps/settings/tests/test_facet_config_routes.py index b3790f3e4..e7dbcc001 100644 --- a/solstone/apps/settings/tests/test_facet_config_routes.py +++ b/solstone/apps/settings/tests/test_facet_config_routes.py @@ -15,7 +15,7 @@ def _settings_client(settings_env): journal_path, _config = settings_env() config_path = journal_path / "config" / "journal.json" config = json.loads(config_path.read_text(encoding="utf-8")) - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} config_path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8") app = create_app(str(journal_path)) app.config["TESTING"] = True diff --git a/solstone/apps/settings/tests/test_facet_detail.py b/solstone/apps/settings/tests/test_facet_detail.py index 2ac35b216..d106e5b13 100644 --- a/solstone/apps/settings/tests/test_facet_detail.py +++ b/solstone/apps/settings/tests/test_facet_detail.py @@ -43,7 +43,7 @@ def _settings_client(settings_env): journal_path, _config = settings_env() config_path = journal_path / "config" / "journal.json" config = json.loads(config_path.read_text(encoding="utf-8")) - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} config_path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8") app = create_app(str(journal_path)) app.config["TESTING"] = True diff --git a/solstone/apps/settings/tests/test_journal_config_routes.py b/solstone/apps/settings/tests/test_journal_config_routes.py index 0fc78de42..d1aecc870 100644 --- a/solstone/apps/settings/tests/test_journal_config_routes.py +++ b/solstone/apps/settings/tests/test_journal_config_routes.py @@ -12,7 +12,7 @@ from solstone.think.utils import get_config def _base_config() -> dict: return { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, } diff --git a/solstone/apps/settings/tests/test_parakeet_cpp_settings.py b/solstone/apps/settings/tests/test_parakeet_cpp_settings.py index e17f354af..11f7930bc 100644 --- a/solstone/apps/settings/tests/test_parakeet_cpp_settings.py +++ b/solstone/apps/settings/tests/test_parakeet_cpp_settings.py @@ -70,7 +70,7 @@ def test_transcribe_payload_marks_linux_parakeet_cpp_runtime( from solstone.apps.settings import routes journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", @@ -91,7 +91,7 @@ def test_parakeet_cpp_device_round_trips_through_settings_config(settings_env) - from solstone.think import supervisor journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", diff --git a/solstone/apps/settings/tests/test_processing_routes.py b/solstone/apps/settings/tests/test_processing_routes.py index befaf4c51..95272051c 100644 --- a/solstone/apps/settings/tests/test_processing_routes.py +++ b/solstone/apps/settings/tests/test_processing_routes.py @@ -9,7 +9,7 @@ from solstone.convey.reasons import INVALID_CONFIG_VALUE def _base_config() -> dict: return { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, } diff --git a/solstone/apps/settings/tests/test_sol_voice_routes.py b/solstone/apps/settings/tests/test_sol_voice_routes.py index 133e27ec9..d96176a1e 100644 --- a/solstone/apps/settings/tests/test_sol_voice_routes.py +++ b/solstone/apps/settings/tests/test_sol_voice_routes.py @@ -19,7 +19,7 @@ from solstone.convey.sol_initiated.settings import load_settings def _base_config() -> dict: return { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, } diff --git a/solstone/apps/settings/tests/test_spa_routes.py b/solstone/apps/settings/tests/test_spa_routes.py index f9e24c3a9..da290c63b 100644 --- a/solstone/apps/settings/tests/test_spa_routes.py +++ b/solstone/apps/settings/tests/test_spa_routes.py @@ -10,7 +10,7 @@ from solstone.convey import create_app def _settings_client(settings_env): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} config_path = journal_path / "config" / "journal.json" config_path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8") facet_dir = journal_path / "facets" / "known" diff --git a/solstone/apps/settings/tests/test_storage_routes.py b/solstone/apps/settings/tests/test_storage_routes.py index f09d45165..b8d4e0eb9 100644 --- a/solstone/apps/settings/tests/test_storage_routes.py +++ b/solstone/apps/settings/tests/test_storage_routes.py @@ -45,7 +45,7 @@ def _write(path: Path, content: str = "x") -> Path: def test_storage_get_includes_journal_log_retention_defaults(settings_env): - journal_path, _config = settings_env({"setup": {"completed_at": "2026-05-09"}}) + journal_path, _config = settings_env({"setup": {"completed_at": 1700000000000}}) client = _client(journal_path) response = client.get("/app/settings/api/storage") @@ -58,7 +58,7 @@ def test_storage_get_includes_journal_log_retention_defaults(settings_env): def test_storage_put_journal_logs_preserves_raw_media_and_reverse(settings_env): journal_path, _config = settings_env( { - "setup": {"completed_at": "2026-05-09"}, + "setup": {"completed_at": 1700000000000}, "retention": { "raw_media": "days", "raw_media_days": 21, @@ -107,7 +107,7 @@ def test_storage_put_journal_logs_preserves_raw_media_and_reverse(settings_env): ], ) def test_storage_put_journal_logs_validates_values(settings_env, payload): - journal_path, _config = settings_env({"setup": {"completed_at": "2026-05-09"}}) + journal_path, _config = settings_env({"setup": {"completed_at": 1700000000000}}) client = _client(journal_path) response = client.put("/app/settings/api/storage", json=payload) @@ -119,7 +119,7 @@ def test_storage_put_journal_logs_validates_values(settings_env, payload): def test_storage_prune_logs_dry_run_serializes_result_and_deletes_nothing( settings_env, ): - journal_path, _config = settings_env({"setup": {"completed_at": "2026-05-09"}}) + journal_path, _config = settings_env({"setup": {"completed_at": 1700000000000}}) old_token = _write(journal_path / "tokens" / f"{_old_day()}.jsonl") client = _client(journal_path) @@ -144,7 +144,7 @@ def test_storage_prune_logs_dry_run_serializes_result_and_deletes_nothing( def test_storage_prune_logs_serializes_root_task_log_dry_run(settings_env): - journal_path, _config = settings_env({"setup": {"completed_at": "2026-05-09"}}) + journal_path, _config = settings_env({"setup": {"completed_at": 1700000000000}}) old_line = _root_task_log_line(_old_day(), "old root line") root_log = journal_path / "task_log.txt" root_log.write_bytes(old_line) @@ -167,7 +167,7 @@ def test_storage_prune_logs_serializes_root_task_log_dry_run(settings_env): def test_storage_prune_logs_disabled_config_deletes_nothing(settings_env): journal_path, _config = settings_env( { - "setup": {"completed_at": "2026-05-09"}, + "setup": {"completed_at": 1700000000000}, "retention": {"journal_logs": {"enabled": False, "days": 30}}, } ) @@ -190,7 +190,7 @@ def test_storage_prune_logs_disabled_config_deletes_nothing(settings_env): def test_storage_prune_logs_validates_days(settings_env): - journal_path, _config = settings_env({"setup": {"completed_at": "2026-05-09"}}) + journal_path, _config = settings_env({"setup": {"completed_at": 1700000000000}}) client = _client(journal_path) response = client.post( @@ -203,7 +203,7 @@ def test_storage_prune_logs_validates_days(settings_env): def test_storage_prune_logs_serializes_partial_errors(settings_env, monkeypatch): - journal_path, _config = settings_env({"setup": {"completed_at": "2026-05-09"}}) + journal_path, _config = settings_env({"setup": {"completed_at": 1700000000000}}) client = _client(journal_path) result = PruneResult( enabled=True, diff --git a/solstone/apps/settings/tests/test_transcribe_resource_payload.py b/solstone/apps/settings/tests/test_transcribe_resource_payload.py index 6568f82fa..e778f8779 100644 --- a/solstone/apps/settings/tests/test_transcribe_resource_payload.py +++ b/solstone/apps/settings/tests/test_transcribe_resource_payload.py @@ -60,7 +60,7 @@ def _client(journal_path): def _ready_journal(settings_env): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", @@ -213,7 +213,7 @@ def test_transcribe_route_includes_resource_block(settings_env, monkeypatch): def test_transcribe_route_passes_confidential_lane_flag(settings_env, monkeypatch): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} config.setdefault("services", {})["confidential"] = { "enabled_at": "2026-05-24T00:00:00Z" } diff --git a/solstone/apps/settings/tests/test_vision_routes.py b/solstone/apps/settings/tests/test_vision_routes.py index 767404823..f225e188d 100644 --- a/solstone/apps/settings/tests/test_vision_routes.py +++ b/solstone/apps/settings/tests/test_vision_routes.py @@ -9,7 +9,7 @@ from solstone.think.utils import get_config def _base_config() -> dict: return { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, "describe": {"max_extractions": 37}, } diff --git a/solstone/apps/settings/tests/test_workspace_install_copy_template.py b/solstone/apps/settings/tests/test_workspace_install_copy_template.py index 0fe4adead..078a2e153 100644 --- a/solstone/apps/settings/tests/test_workspace_install_copy_template.py +++ b/solstone/apps/settings/tests/test_workspace_install_copy_template.py @@ -14,7 +14,7 @@ from solstone.convey import create_app @pytest.fixture def settings_client(settings_env): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", diff --git a/solstone/apps/thinking/tests/test_confidential_attestation_payload.py b/solstone/apps/thinking/tests/test_confidential_attestation_payload.py index 29e6b31b2..d92482540 100644 --- a/solstone/apps/thinking/tests/test_confidential_attestation_payload.py +++ b/solstone/apps/thinking/tests/test_confidential_attestation_payload.py @@ -19,7 +19,7 @@ HOSTILE_ARCH = "" def _client(settings_env, *, confidential: bool = False): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} if confidential: config.setdefault("services", {})["confidential"] = { "enabled_at": "2026-05-24T00:00:00Z", diff --git a/solstone/apps/thinking/tests/test_keys_check_route.py b/solstone/apps/thinking/tests/test_keys_check_route.py index 1e0d42841..479770996 100644 --- a/solstone/apps/thinking/tests/test_keys_check_route.py +++ b/solstone/apps/thinking/tests/test_keys_check_route.py @@ -16,7 +16,7 @@ from solstone.convey import create_app @pytest.fixture def settings_client_with_journal(settings_env): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} config_path = journal_path / "config" / "journal.json" config_path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8") app = create_app(str(journal_path)) diff --git a/solstone/apps/thinking/tests/test_local_bootstrap_routes.py b/solstone/apps/thinking/tests/test_local_bootstrap_routes.py index c020dbaa1..28bbfc4b5 100644 --- a/solstone/apps/thinking/tests/test_local_bootstrap_routes.py +++ b/solstone/apps/thinking/tests/test_local_bootstrap_routes.py @@ -36,7 +36,7 @@ def _client(journal_path): def _settings_config() -> dict: return { - "setup": {"completed_at": "2026-05-09T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, "providers": { "generate": {"provider": "google", "model": "gemini-flash-latest"}, "cogitate": {"provider": "openai", "model": "gpt-5.4-mini"}, diff --git a/solstone/apps/thinking/tests/test_local_endpoint_routes.py b/solstone/apps/thinking/tests/test_local_endpoint_routes.py index 2ac88347b..ffd80df90 100644 --- a/solstone/apps/thinking/tests/test_local_endpoint_routes.py +++ b/solstone/apps/thinking/tests/test_local_endpoint_routes.py @@ -36,7 +36,7 @@ def _write_config(journal_path: Path, config: dict[str, Any]) -> None: def _ready_settings_env(settings_env) -> tuple[Path, dict[str, Any]]: journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} _write_config(journal_path, config) return journal_path, config diff --git a/solstone/apps/thinking/tests/test_providers_payload_extended.py b/solstone/apps/thinking/tests/test_providers_payload_extended.py index a0389c165..dd4dc35ec 100644 --- a/solstone/apps/thinking/tests/test_providers_payload_extended.py +++ b/solstone/apps/thinking/tests/test_providers_payload_extended.py @@ -44,7 +44,7 @@ def _settings_client_with_journal(settings_env, thinking_app): from solstone.convey import state journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", @@ -156,7 +156,7 @@ def test_get_providers_reports_none_lane_when_no_engine_selected( ): journal_path, config = settings_env( { - "setup": {"completed_at": "2026-05-23T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, "env": {}, "providers": {"contexts": {}, "models": {}}, } diff --git a/solstone/apps/thinking/tests/test_validate_model_route.py b/solstone/apps/thinking/tests/test_validate_model_route.py index 34f1f2791..a65e8c58a 100644 --- a/solstone/apps/thinking/tests/test_validate_model_route.py +++ b/solstone/apps/thinking/tests/test_validate_model_route.py @@ -14,7 +14,7 @@ from solstone.convey import create_app @pytest.fixture def settings_client_with_journal(settings_env): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} config_path = journal_path / "config" / "journal.json" config_path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8") app = create_app(str(journal_path)) diff --git a/solstone/apps/thinking/tests/test_workspace_html.py b/solstone/apps/thinking/tests/test_workspace_html.py index 8e0f6dcf6..4b720032a 100644 --- a/solstone/apps/thinking/tests/test_workspace_html.py +++ b/solstone/apps/thinking/tests/test_workspace_html.py @@ -17,7 +17,7 @@ STATIC = Path(__file__).resolve().parents[1] / "static" / "thinking.js" def test_workspace_renders_each_lane(settings_env): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", @@ -114,7 +114,7 @@ def test_workspace_renders_each_lane(settings_env): def test_thinking_literal_paths_resolve(settings_env): journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", @@ -137,7 +137,7 @@ def test_thinking_state_degrades_when_initial_payload_fails( from solstone.apps.thinking import routes journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", @@ -507,7 +507,7 @@ def test_thinking_copy_payload_shape_carries_deck_blocks() -> None: def test_thinking_state_serves_byo_model_copy_bytes(settings_env) -> None: journal_path, config = settings_env() - config["setup"] = {"completed_at": "2026-05-23T00:00:00Z"} + config["setup"] = {"completed_at": 1700000000000} (journal_path / "config" / "journal.json").write_text( json.dumps(config, indent=2) + "\n", encoding="utf-8", diff --git a/solstone/convey/root.py b/solstone/convey/root.py index 9b358f301..3e27f7750 100644 --- a/solstone/convey/root.py +++ b/solstone/convey/root.py @@ -36,6 +36,7 @@ from solstone.think.utils import ( ensure_journal_config, get_config, get_journal, + journal_is_active, ) from . import bridge as convey_bridge @@ -59,16 +60,6 @@ from .utils import error_response, error_response_with_reason logger = logging.getLogger(__name__) -def _is_setup_complete() -> bool: - """Check if initial setup has been completed.""" - try: - config = get_config() - return bool(config.get("setup", {}).get("completed_at")) - except Exception: - # Intended fail-closed-on-unreadable-config: require setup flow. - return False - - bp = Blueprint( "root", __name__, @@ -132,7 +123,7 @@ def require_access() -> Any: ) # Check setup state - if not _is_setup_complete(): + if not journal_is_active(get_journal()): return redirect(url_for("root.init")) return None @@ -326,7 +317,7 @@ def init_local_capability() -> Any: @bp.route("/init") def init() -> Any: - if _is_setup_complete(): + if journal_is_active(get_journal()): return redirect(url_for("root.index")) # Preserve the setup page's historical config materialization on page load; diff --git a/solstone/think/utils.py b/solstone/think/utils.py index b8750ae09..ac30f9ca2 100644 --- a/solstone/think/utils.py +++ b/solstone/think/utils.py @@ -812,7 +812,11 @@ def journal_is_active(path: str | Path) -> bool: with open(config_path, "r", encoding="utf-8") as f: config = json.load(f) completed = config["setup"]["completed_at"] - return isinstance(completed, (int, float)) and completed > 0 + return ( + isinstance(completed, (int, float)) + and not isinstance(completed, bool) + and completed > 0 + ) except ( OSError, json.JSONDecodeError, diff --git a/tests/test_app_import_journal.py b/tests/test_app_import_journal.py index 3f8b5e700..76bd39b8a 100644 --- a/tests/test_app_import_journal.py +++ b/tests/test_app_import_journal.py @@ -18,7 +18,7 @@ def _temp_journal(monkeypatch, tmp_path): (config_dir / "journal.json").write_text( json.dumps( { - "setup": {"completed_at": "2026-04-26T00:00:00Z"}, + "setup": {"completed_at": 1700000000000}, } ), encoding="utf-8", diff --git a/tests/test_config.py b/tests/test_config.py index 16537858c..7cdf61c9d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -191,6 +191,8 @@ def _write_journal_config(journal_path, config): ({"setup": {"completed_at": 1.5}}, True), ({"setup": {"completed_at": 0}}, False), ({"setup": {"completed_at": None}}, False), + ({"setup": {"completed_at": True}}, False), + ({"setup": {"completed_at": False}}, False), ({}, False), ({"setup": {"completed_at": "foo"}}, False), ({"identity": {"name": "Active User"}}, False), diff --git a/tests/test_setup_predicate_shared.py b/tests/test_setup_predicate_shared.py new file mode 100644 index 000000000..7594cec53 --- /dev/null +++ b/tests/test_setup_predicate_shared.py @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: AGPL-3.0-only +# Copyright (c) 2026 sol pbc + +import json +from pathlib import Path +from types import SimpleNamespace + +import pytest + +from solstone.convey import create_app + + +def _write_iso_completed_journal( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> Path: + journal = tmp_path / "journal" + journal.mkdir() + config_dir = journal / "config" + config_dir.mkdir() + (config_dir / "journal.json").write_text( + json.dumps({"setup": {"completed_at": "2026-04-26T00:00:00Z"}}), + encoding="utf-8", + ) + monkeypatch.setenv("SOLSTONE_JOURNAL", str(journal)) + return journal + + +def test_access_gate_treats_iso_completed_at_as_incomplete( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + journal = _write_iso_completed_journal(tmp_path, monkeypatch) + app = create_app(str(journal)) + app.config["TESTING"] = True + client = app.test_client() + + resp = client.get("/") + + assert resp.status_code == 302 + assert "/init" in resp.headers["Location"] + + +def test_secure_listener_treats_iso_completed_at_as_incomplete( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + from solstone.convey.secure_listener import runtime as rt + from solstone.think.link.ca import load_or_generate_ca + from solstone.think.link.paths import ca_dir + + previous_runtime = rt._runtime + try: + rt._runtime = None + _write_iso_completed_journal(tmp_path, monkeypatch) + load_or_generate_ca(ca_dir()) + app = SimpleNamespace(config={"SECURE_LISTENER_ENABLED": True}) + + rt.start_secure_listener(app) + + assert rt._runtime is None + assert not getattr(app, "secure_listener_started", False) + finally: + rt.stop_all_secure_listener() + rt._runtime = previous_runtime