oc_chat
Loading...
Searching...
No Matches
store_scope.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oc_chat/registry/store_scope.py
4
5# OCT-LINT: disable=tests_exist reason="Covered by oc_chat/tests/registry/test_schema_v0_3_2.py"
6"""
7Central store-scope guard for the OC Chat registry.
8
9Purpose
10-------
11Enforce the V0.22 §5.3.1 / AC §8.57 contract: every live runtime command
12(queue, round, auth) must refuse an ``authored_projection`` store with a
13clear diagnostic, and the authored-projection rebuild must stamp the store
14before any live open can succeed.
15
16Responsibilities
17----------------
18- Expose ``assert_live_store(conn)`` — raises ``StoreScopeError`` when
19 ``registry_meta.store_scope = 'authored_projection'``.
20- Expose ``stamp_authored_projection(conn)`` — writes the scope stamp
21 before the rebuild path returns a store handle.
22- Tolerate pre-v0.3.2 databases (missing ``store_scope`` column) by
23 treating them as ``live_store`` (lenient mode, dormant in v1).
24
25Diagnostics
26-----------
27Domain: OC_CHAT.MAIN
28Levels:
29 L2 - store-scope lifecycle
30 L3 - scope check details
31 L4 - column presence probes
32
33Contracts
34---------
35- Callers own connection and transaction lifecycle.
36- ``assert_live_store`` is idempotent and read-only; safe to call in any
37 transaction mode.
38- ``stamp_authored_projection`` must be called before the temp store is
39 returned to callers; the stamp is permanent for that DB file.
40
41Dependencies
42------------
43- sqlite3 (the caller's connection and its OperationalError)
44- oc_diagnostics (structured debug logging via _dbg)
45"""
46from __future__ import annotations
47
48import sqlite3
49
50from oc_diagnostics import _dbg
51
52_dbg("OC_CHAT.MAIN", "module", "loaded", 4)
53
54_AUTHORED_PROJECTION = "authored_projection"
55_LIVE_STORE = "live_store"
56
57
58class StoreScopeError(RuntimeError):
59 """Raised when a live command targets an authored-projection store."""
60
61 def __init__(self, scope: str = _AUTHORED_PROJECTION) -> None:
62 super().__init__(
63 f"This registry is an '{scope}' store and cannot execute live commands. "
64 "Import a runtime checkpoint to upgrade it to a 'live_store'."
65 )
66 self.scope = scope
67
68
69def assert_live_store(conn: sqlite3.Connection) -> None:
70 """Raise ``StoreScopeError`` if the connected store is an authored projection.
71
72 Silently returns for pre-v0.3.2 databases (missing ``store_scope`` column)
73 and for uninitialized databases (missing ``registry_meta`` row) — both are
74 treated as ``live_store`` in v1 (dormant guard per L-a §9.3).
75 """
76
77 try:
78 row = conn.execute(
79 "SELECT store_scope FROM registry_meta WHERE id = 1"
80 ).fetchone()
81 except sqlite3.OperationalError:
82 return
83 if row is None:
84 return
85 try:
86 scope = str(row["store_scope"])
87 except (TypeError, KeyError, IndexError):
88 scope = str(row[0])
89 if scope == _AUTHORED_PROJECTION:
90 raise StoreScopeError(scope)
91
92
93def stamp_authored_projection(conn: sqlite3.Connection) -> None:
94 """Write ``store_scope = 'authored_projection'`` to ``registry_meta``.
95
96 Called by the authored-projection rebuild path before returning a store
97 handle so that any subsequent live-command open fails fast.
98 Callers own the enclosing transaction.
99 """
100
101 conn.execute(
102 "UPDATE registry_meta SET store_scope = ? WHERE id = 1",
103 (_AUTHORED_PROJECTION,),
104 )
None __init__(self, str scope=_AUTHORED_PROJECTION)
None stamp_authored_projection(sqlite3.Connection conn)
None assert_live_store(sqlite3.Connection conn)