From 3aa3e540d6fdc1f37f0defde84f67e75331bf9ea Mon Sep 17 00:00:00 2001 From: song Date: Wed, 16 Sep 2026 17:21:19 -0400 Subject: [PATCH 1/2] refactor(todos): import the Todo task-class vocabulary from its owner Track A for #4447. TODO_TASK_CLASS_ADVANCEMENT was restated as a literal in three modules that each classify a Todo, and TODO_TASK_CLASS_MONITOR in one of them, while control_plane/todos/contract.py already owns both and already exports the closed set they belong to. A literal copy cannot be caught by reading either module: the values match today, so nothing fails, and a future change has three places to land. Import the owner's names instead; every module that restates them already imports from the todos layer, so no dependency direction moves. Semantic same-runtime forks 18 -> 16; budgets and the smoke anchor move together. Refs #4447 Co-Authored-By: Claude Fable 5.1 Signed-off-by: song --- examples/semantic-vocabulary-drift-smoke.py | 6 +++--- loopx/control_plane/goals/goal_frontier/__init__.py | 10 +++++++--- .../goals/goal_frontier/long_todo_chain.py | 4 +++- loopx/control_plane/todos/frontier_revision.py | 9 +++++++-- loopx/semantics/vocabulary_v0.json | 6 +++--- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/examples/semantic-vocabulary-drift-smoke.py b/examples/semantic-vocabulary-drift-smoke.py index 5e3b06d18..2016b453e 100755 --- a/examples/semantic-vocabulary-drift-smoke.py +++ b/examples/semantic-vocabulary-drift-smoke.py @@ -145,8 +145,8 @@ TWIN_ROOT_ANCHOR = "loopx/control_plane" TWIN_BUDGET_ANCHOR = 43 BUDGET_ANCHOR = { - "same_runtime_forks": 25, - "same_runtime_fork_definitions": 58, + "same_runtime_forks": 23, + "same_runtime_fork_definitions": 52, "conflicting_values": 18, "conflicting_definitions": 59, "schema_version_same_runtime_forks": 7, @@ -154,7 +154,7 @@ "multi_value_forks": 4, "multi_value_forks_semantic": 3, "multi_value_fork_definitions": 10, - "same_runtime_forks_semantic": 18, + "same_runtime_forks_semantic": 16, "conflicting_values_semantic": 2, } # Budgets for the legacy should-run decision fields, anchored the same way so a diff --git a/loopx/control_plane/goals/goal_frontier/__init__.py b/loopx/control_plane/goals/goal_frontier/__init__.py index bb42ee15e..b403a96fb 100644 --- a/loopx/control_plane/goals/goal_frontier/__init__.py +++ b/loopx/control_plane/goals/goal_frontier/__init__.py @@ -11,7 +11,13 @@ from ...agents.profile import agent_profile_requires_vision from ...agents.runtime_model import peer_work_key, select_peer_for_work from ...runtime.time import parse_timestamp -from ...todos.contract import normalize_todo_replan_obligation_id +# Refs #4447: the todo contract owns this vocabulary; import it instead of +# restating the literal in every module that classifies a Todo. +from ...todos.contract import ( + TODO_TASK_CLASS_ADVANCEMENT, + TODO_TASK_CLASS_MONITOR, + normalize_todo_replan_obligation_id, +) from ...todos.todo_semantics import ( todo_advancement_frontier_counts, todo_item_is_watch_only_monitor, @@ -99,8 +105,6 @@ MONITOR_NO_CHANGE_STREAK_TRIGGER = "monitor_no_change_streak" VISION_PROFILE_MISSING_TRIGGER = "required_agent_vision_missing" TODO_SUCCESSION_GAP_TRIGGER = TODO_SUCCESSION_WARNING_REASON_CODE -TODO_TASK_CLASS_ADVANCEMENT = "advancement_task" -TODO_TASK_CLASS_MONITOR = "continuous_monitor" def safe_non_negative_int(value: Any) -> int: diff --git a/loopx/control_plane/goals/goal_frontier/long_todo_chain.py b/loopx/control_plane/goals/goal_frontier/long_todo_chain.py index 73cad3581..ee77bf4fd 100644 --- a/loopx/control_plane/goals/goal_frontier/long_todo_chain.py +++ b/loopx/control_plane/goals/goal_frontier/long_todo_chain.py @@ -6,6 +6,9 @@ from typing import Any from ...runtime.time import parse_timestamp +# Refs #4447: the todo contract owns this vocabulary; import it instead of +# restating the literal in every module that classifies a Todo. +from ...todos.contract import TODO_TASK_CLASS_ADVANCEMENT from ...todos.frontier_revision import ( TODO_FRONTIER_REVISION_SCHEMA_VERSION, advancement_frontier_revision_from_index, @@ -16,7 +19,6 @@ LONG_TODO_CHAIN_TRIGGER = "long_todo_chain" -TODO_TASK_CLASS_ADVANCEMENT = "advancement_task" LONG_TODO_CHAIN_FRONTIER_REVISION_SCHEMA_VERSION = ( TODO_FRONTIER_REVISION_SCHEMA_VERSION ) diff --git a/loopx/control_plane/todos/frontier_revision.py b/loopx/control_plane/todos/frontier_revision.py index 9348876b0..4c2f590d5 100644 --- a/loopx/control_plane/todos/frontier_revision.py +++ b/loopx/control_plane/todos/frontier_revision.py @@ -8,13 +8,18 @@ from typing import Any from ..effect_runtime import effect_runtime_result -from .contract import normalize_todo_claimed_by, normalize_todo_excluded_agents +# Refs #4447: the todo contract owns this vocabulary; import it instead of +# restating the literal in every module that classifies a Todo. +from .contract import ( + TODO_TASK_CLASS_ADVANCEMENT, + normalize_todo_claimed_by, + normalize_todo_excluded_agents, +) from .todo_semantics import todo_item_task_class TODO_FRONTIER_REVISION_SCHEMA_VERSION = "todo_frontier_revision_v0" TODO_FRONTIER_REVISION_INDEX_SCHEMA_VERSION = "todo_frontier_revision_index_v0" -TODO_TASK_CLASS_ADVANCEMENT = "advancement_task" FRONTIER_REVISION_FIELDS = ( "todo_id", diff --git a/loopx/semantics/vocabulary_v0.json b/loopx/semantics/vocabulary_v0.json index 75aa6c4a2..aa5cd103a 100644 --- a/loopx/semantics/vocabulary_v0.json +++ b/loopx/semantics/vocabulary_v0.json @@ -886,15 +886,15 @@ }, "inventory_ratchets": { "meaning": "Counts read from the generated inventory. A same-runtime fork is one constant name with one value defined in two or more modules of the same runtime; a conflicting value is one name with different values. Both the number of affected names and the number of definitions are budgets, so a third spelling of an already-conflicting name is still a regression.", - "same_runtime_forks": 25, - "same_runtime_fork_definitions": 58, + "same_runtime_forks": 23, + "same_runtime_fork_definitions": 52, "conflicting_values": 18, "conflicting_definitions": 59, "schema_version_same_runtime_forks": 7, "multi_value_twins": 19, "multi_value_forks": 4, "multi_value_fork_definitions": 10, - "same_runtime_forks_semantic": 18, + "same_runtime_forks_semantic": 16, "conflicting_values_semantic": 2, "multi_value_meaning": "Enums, named closed sets, Literal aliases, and TypeScript as-const arrays are vocabulary exactly as a NAME = \"value\" constant is, so they get the same collision rule. One name defined in two modules with identical values is a twin; with different values it is a fork. The semantic multi-value-fork budget excludes only names declared in scope_declarations.", "multi_value_forks_note": "The 4 counted forks include SOURCE_SURFACES, whose four definitions are four CLI commands each listing its own data sources; that is bounded-context reuse of one name, not drift. It stays in the budget until M0.5 adds a scope field (RFC Section 5) and must not be removed by renaming.", From e385e0ed608b06a05fe36c093e4e8e95b38e096f Mon Sep 17 00:00:00 2001 From: song <22676124+songoow@users.noreply.github.com> Date: Wed, 16 Sep 2026 21:55:52 -0400 Subject: [PATCH 2/2] fix(todos): drop the dead task-class import this refactor left unused long_todo_chain.py restated TODO_TASK_CLASS_ADVANCEMENT but never used it, so importing the owner's name left an unused import and F401 failed the test-suite lint. No module imports the constant from here, so the fix is to remove the import rather than keep a dead re-export. Verified with ruff's rule: the module no longer binds the name and the drift smoke still reports same_runtime_forks=23 with the owner as the only definition site. Co-Authored-By: Claude Fable 5 Signed-off-by: song <22676124+songoow@users.noreply.github.com> --- loopx/control_plane/goals/goal_frontier/long_todo_chain.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/loopx/control_plane/goals/goal_frontier/long_todo_chain.py b/loopx/control_plane/goals/goal_frontier/long_todo_chain.py index ee77bf4fd..f60c4029d 100644 --- a/loopx/control_plane/goals/goal_frontier/long_todo_chain.py +++ b/loopx/control_plane/goals/goal_frontier/long_todo_chain.py @@ -6,9 +6,6 @@ from typing import Any from ...runtime.time import parse_timestamp -# Refs #4447: the todo contract owns this vocabulary; import it instead of -# restating the literal in every module that classifies a Todo. -from ...todos.contract import TODO_TASK_CLASS_ADVANCEMENT from ...todos.frontier_revision import ( TODO_FRONTIER_REVISION_SCHEMA_VERSION, advancement_frontier_revision_from_index,