oc_chat
Loading...
Searching...
No Matches
oc_chat.registry.hub_log Namespace Reference

Classes

class  UnregisteredSenderError
class  RegistryHubLog

Functions

str|None _plan_subject (str|None subject)
HubMessage _message_from_row (object args_json)

Variables

str HUB_OPERATION_TYPE = "hub.message"
 _PLAN_ID_RE = re.compile(r"^[0-9]{3,}-[A-Za-z0-9][A-Za-z0-9_\-.]*$")
dict _ACTOR_TYPE_BY_SLOT_KIND

Detailed Description

The registry backend for the hub log: one message, one operation-log row.

Purpose
-------
Plan 259 P4. The hub has one log contract and two backends, and this is
the one oc_chat plugs in. It lives on the REGISTRY side on purpose,
exactly as ``registry/queue_backend.py`` does: the operation log already
carries actor, actor type, credential id and an append-only guarantee
enforced by a database trigger, so the hub inherits those instead of
growing a second copy of them.

A hub message is ONE row of ``operation_log`` with
``operation_type='hub.message'``, ``command`` = the hub kind and
``related_plan_id`` = the subject when the subject is a plan id. There is
no projection: a message changes no projected state, it IS the state, and
``SQLiteBackend._apply_domain_projection`` is an ``if``/``elif`` chain
with no ``else``, so an unmatched type projects nothing. **No projector
was added, because none is required** -- a witness asserts the
observable half of that.

**The row's signature and the message's claim are different things.**
``actor_type`` and ``hmac_key_id`` are written by the storage layer from
the SLOT, and a caller cannot choose them; the message's ``provenance``
is a payload field a caller writes freely. The hub's human rule is
enforced on the claim, in ``core/hub.check_ruling_provenance``, and this
module does not second-guess it -- but the row beside it records who the
process was, which is the cross-check the design note asked for.

The measured preflight fact
---------------------------
A ``human_operator`` row written from a CLI process that supplies no
``auth_proof`` **is accepted, unsigned**. ``Registry._preflight_auth``
(``registry/api.py:115``) runs the role guard and returns ``None`` the
moment ``auth_proof`` is ``None``; ``hub.message`` matches no entry in
``ROLE_ALLOWSETS`` and no prefix in ``PREFIX_ALLOWSETS``, so the guard
permits all three slot kinds. The row still lands carrying an
``hmac_key_id``, because the storage layer falls back to the slot's
ACTIVE credential when the caller supplies none -- so that key id names a
credential which did not sign this row.

So the registry mode's signature is a **claim**, like the file mode's,
and the hub's own human rule is what does the work in both. That is a
finding, not a design; it is filed rather than fixed here, because
demanding a credential would be a security-layer change and those are
the operator's.

Responsibilities
----------------
- :class:`RegistryHubLog` -- the four :class:`~oc_orchestrator.hub.HubLog`
  operations over the operation log.
- :data:`HUB_OPERATION_TYPE` -- the one operation type, pinned as a
  constant so a witness can assert it.
- :class:`UnregisteredSenderError` -- the adapter's named refusal.

Diagnostics
-----------
Domain: OC_CHAT.MAIN
Levels:
    L2 - lifecycle events (a message recorded)
    L3 - semantic details (a refusal, a filter)
    L4 - deep tracing (module load)

Contracts
---------
- This module MUTATES only through ``registry.domain.hub_message``: no
  SQL write, no direct table update.
- The READ is one ``SELECT`` in this file rather than a fifth function in
  ``registry/readers.py``. That is the honest place: ``readers.py``
  returns ``registry/dataclasses.py`` types, and a hub reader must return
  :class:`~oc_orchestrator.hub.HubEntry` -- a DISPATCH-layer type. Putting
  that import into the general reader library would push a dispatch type
  into every consumer of it, including the registry CLI. The adapter is
  already the one place the two vocabularies meet, so the read lives
  here, exactly as ``queue_backend`` keeps its own ``plans`` projection
  query rather than widening ``readers``.
- The import direction is registry -> dispatch, which is the permitted
  one and the one ``registry/mirror_observer.py`` and
  ``registry/queue_backend.py`` already take. Nothing in the dispatch
  layer imports this module; the CLI reaches it through one lazy import
  behind ``--backend registry``.
- ``seq`` is the row's ``state_version``: monotonic within the store, as
  the contract requires. It is NOT comparable with the file log's
  line count, and the equivalence witness compares the FOLD rather than
  the numbering for that reason.

Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
- oc_orchestrator.hub (the contract; moved there by plan 272, and
  named directly rather than through the shim because this module is
  the registry-side backend of the seam, not a caller that always
  spelled the old path)
- oc_chat.registry.api (the Registry facade)
- oc_chat.registry.domain (the typed hub operation)

Function Documentation

◆ _message_from_row()

HubMessage oc_chat.registry.hub_log._message_from_row ( object args_json)
protected
Rebuild the message one ``hub.message`` payload carried.

Definition at line 290 of file hub_log.py.

Here is the caller graph for this function:

◆ _plan_subject()

str | None oc_chat.registry.hub_log._plan_subject ( str | None subject)
protected
*subject* if it is plan-shaped, else ``None``. See the constant above.

Definition at line 283 of file hub_log.py.

Here is the caller graph for this function:

Variable Documentation

◆ _ACTOR_TYPE_BY_SLOT_KIND

dict oc_chat.registry.hub_log._ACTOR_TYPE_BY_SLOT_KIND
protected
Initial value:
1= {
2 "operator": "human_operator",
3 "orchestrator": "orchestrator_agent",
4 "worker": "worker_agent",
5}

Definition at line 138 of file hub_log.py.

◆ _PLAN_ID_RE

oc_chat.registry.hub_log._PLAN_ID_RE = re.compile(r"^[0-9]{3,}-[A-Za-z0-9][A-Za-z0-9_\-.]*$")
protected

Definition at line 131 of file hub_log.py.

◆ HUB_OPERATION_TYPE

str oc_chat.registry.hub_log.HUB_OPERATION_TYPE = "hub.message"

Definition at line 124 of file hub_log.py.