From 94d11ba07c06ee54cb4274d3c0244a98cde10f05 Mon Sep 17 00:00:00 2001 From: psychologister <230141834+psychologister@users.noreply.github.com> Date: Thu, 12 Nov 2026 11:20:43 +0800 Subject: [PATCH] state_projection: single-source the backlog-hygiene bullet grammar active_state_projection.BACKLOG_HYGIENE_BULLET_PATTERN is the compiled grammar for a Next Action / Operating Lessons bullet (^\s*(?:[-*]|\d+[.)])\s+(.+?)\s*$). state_projection restated the identical regex as its own BULLET_PATTERN and used it in _section_entries to split the Next Action section into entries. state_projection already imports SECTION_HEADING_PATTERN from that very owner module, so borrowing one more compiled pattern adds no import edge and cannot cycle. BULLET_PATTERN is retired and _section_entries now matches against the owner; the re import stays because PRIORITY_PATTERN and the Next Action executable/user-wait regexes still use it. The owner's SECTION_HEADING_PATTERN and the consumer's PRIORITY_PATTERN are different grammars and are left untouched. Two existing machines miss this copy. The by-name semantic inventory only catalogues UPPER_NAME carriers and would not pair two differently named bindings; the status-facade vocabulary-owners machine is also name-keyed, so a copy renamed to BULLET_PATTERN slips past it. That machine is extended here to record state_projection as an importer of the owner name, and a new guard keys on the compiled pattern literal -- a whole-tree census, identity for the consumer, retirement, the distinct neighbours, and behavioural parity through the public active_state_next_action_entries gate (marker stripping, non-bullet passthrough and continuation grouping), so a renamed restatement or a silent grammar change fails. Refs #4447. Signed-off-by: psychologister <230141834+psychologister@users.noreply.github.com> --- loopx/state_projection.py | 8 +- ...t_state_projection_bullet_pattern_owner.py | 229 ++++++++++++++++++ .../test_status_facade_vocabulary_owners.py | 5 +- 3 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 tests/architecture/test_state_projection_bullet_pattern_owner.py diff --git a/loopx/state_projection.py b/loopx/state_projection.py index 4fe8b02986..6b79503330 100644 --- a/loopx/state_projection.py +++ b/loopx/state_projection.py @@ -5,7 +5,10 @@ import re from .control_plane.goals.active_state_metadata import todo_role_for_heading -from .control_plane.status.active_state_projection import SECTION_HEADING_PATTERN +from .control_plane.status.active_state_projection import ( + BACKLOG_HYGIENE_BULLET_PATTERN, + SECTION_HEADING_PATTERN, +) from .control_plane.todos.contract import ( TODO_TASK_PATTERN, build_todo_id, @@ -45,7 +48,6 @@ ACTIVE_STATE_PROJECTION_DIAGNOSTICS_SCHEMA_VERSION = "active_state_projection_diagnostics_v0" TODO_ITEM_SCHEMA_VERSION = "todo_item_v0" -BULLET_PATTERN = re.compile(r"^\s*(?:[-*]|\d+[.)])\s+(.+?)\s*$") PRIORITY_PATTERN = re.compile(r"^\[(P[0-4])\]\s+(.+)$", re.IGNORECASE) NEXT_ACTION_EXECUTABLE_PATTERN = re.compile( r"(?i)\b(?:run|repair|fix|implement|add|update|write|record|validate|" @@ -399,7 +401,7 @@ def append_entry(parts: list[str]) -> None: for line in lines: if line.strip().startswith("