Add renormalization rule guide

This commit is contained in:
2026-05-23 17:01:41 +02:00
parent f3805eab79
commit d4a195d0b5
3 changed files with 96 additions and 1 deletions

View File

@@ -178,6 +178,38 @@ class ConsistencyReport:
return [i for i in self.issues if i.severity == "INFO"] return [i for i in self.issues if i.severity == "INFO"]
@dataclass(frozen=True)
class RenormalizationRule:
check_id: str
invariant: str
detection: str
repair: str
test_anchor: str
RENORMALIZATION_RULES: tuple[RenormalizationRule, ...] = (
RenormalizationRule(
check_id="C-23",
invariant="Planning-state workplans cannot contain active task work.",
detection=(
"Workplan status is proposed, ready, or backlog while a linked "
"task is in_progress or blocked."
),
repair="Patch the DB workstream and workplan frontmatter to status=active.",
test_anchor="tests/test_consistency_check.py::TestLifecycleRenormalization",
),
)
RENORMALIZATION_NEXT_GUARD_CHECKLIST: tuple[str, ...] = (
"Name the invariant and assign the next C-id.",
"Add rule metadata to RENORMALIZATION_RULES.",
"Add detection in check_repo before generic drift rules that could fight it.",
"Add the repair branch in fix_repo, using file writes only through helpers.",
"Add detection and repair tests under TestLifecycleRenormalization.",
)
@contextmanager @contextmanager
def run_lock(name: str): def run_lock(name: str):
"""Hold a nonblocking process lock for long-running consistency modes.""" """Hold a nonblocking process lock for long-running consistency modes."""
@@ -1999,6 +2031,24 @@ def archive_closed_workplans(
# Output / rendering # Output / rendering
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def render_renormalization_guide() -> str:
lines = ["Lifecycle Renormalization Rules", "=" * 33]
for rule in RENORMALIZATION_RULES:
lines.extend(
[
"",
f"{rule.check_id}: {rule.invariant}",
f" Detect: {rule.detection}",
f" Repair: {rule.repair}",
f" Tests: {rule.test_anchor}",
]
)
lines.extend(["", "Add The Next Guard"])
for idx, item in enumerate(RENORMALIZATION_NEXT_GUARD_CHECKLIST, 1):
lines.append(f" {idx}. {item}")
return "\n".join(lines)
def render_text(report: ConsistencyReport, show_info: bool = True) -> str: def render_text(report: ConsistencyReport, show_info: bool = True) -> str:
SEP = "=" * 66 SEP = "=" * 66
lines = [ lines = [
@@ -2089,6 +2139,8 @@ def main() -> None:
help="Run checks against all repos with a resolvable path on this host") help="Run checks against all repos with a resolvable path on this host")
group.add_argument("--here", metavar="PATH", nargs="?", const="", group.add_argument("--here", metavar="PATH", nargs="?", const="",
help="Infer repo slug from git remote URL at PATH (default: CWD)") help="Infer repo slug from git remote URL at PATH (default: CWD)")
group.add_argument("--renormalization-guide", action="store_true",
help="Print lifecycle renormalization rules and the add-next-guard checklist")
parser.add_argument("--fix", action="store_true", parser.add_argument("--fix", action="store_true",
help="Apply auto-fixable issues (status drift, repo mismatch, etc.)") help="Apply auto-fixable issues (status drift, repo mismatch, etc.)")
parser.add_argument("--remote", action="store_true", parser.add_argument("--remote", action="store_true",
@@ -2115,6 +2167,25 @@ def main() -> None:
help="Output JSON instead of human-readable text") help="Output JSON instead of human-readable text")
args = parser.parse_args() args = parser.parse_args()
if args.renormalization_guide:
if args.as_json:
print(json.dumps({
"rules": [
{
"check_id": rule.check_id,
"invariant": rule.invariant,
"detection": rule.detection,
"repair": rule.repair,
"test_anchor": rule.test_anchor,
}
for rule in RENORMALIZATION_RULES
],
"add_next_guard": list(RENORMALIZATION_NEXT_GUARD_CHECKLIST),
}, indent=2))
else:
print(render_renormalization_guide())
sys.exit(0)
import os as _os import os as _os
no_wb = getattr(args, "no_writeback", False) no_wb = getattr(args, "no_writeback", False)
do_fix = args.fix or args.remote do_fix = args.fix or args.remote

View File

@@ -24,6 +24,7 @@ from consistency_check import (
ConsistencyReport, ConsistencyReport,
Issue, Issue,
FILE_TO_DB_WORKSTREAM_STATUS, FILE_TO_DB_WORKSTREAM_STATUS,
RENORMALIZATION_RULES,
STATUS_ORDER, STATUS_ORDER,
_BACKGROUND_CHECKS, _BACKGROUND_CHECKS,
_detect_behind_remote, _detect_behind_remote,
@@ -41,6 +42,7 @@ from consistency_check import (
parse_frontmatter, parse_frontmatter,
parse_task_blocks, parse_task_blocks,
render_text, render_text,
render_renormalization_guide,
report_to_dict, report_to_dict,
) )
from api.workplan_status import ready_review_status from api.workplan_status import ready_review_status
@@ -377,6 +379,20 @@ class TestRenderText:
assert "C-08" not in text assert "C-08" not in text
class TestRenormalizationGuide:
def test_registry_contains_c23_rule(self):
rule = next(rule for rule in RENORMALIZATION_RULES if rule.check_id == "C-23")
assert "Planning-state" in rule.invariant
assert "TestLifecycleRenormalization" in rule.test_anchor
def test_guide_points_to_next_guard_pattern(self):
text = render_renormalization_guide()
assert "C-23" in text
assert "Add The Next Guard" in text
assert "Add detection in check_repo" in text
assert "Add the repair branch in fix_repo" in text
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# report_to_dict # report_to_dict
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@@ -154,7 +154,7 @@ existing consistency sync loop.
```task ```task
id: STATE-WP-0047-T06 id: STATE-WP-0047-T06
status: todo status: done
priority: medium priority: medium
state_hub_task_id: "4b663fce-876c-4a52-955c-c754dbf44b0f" state_hub_task_id: "4b663fce-876c-4a52-955c-c754dbf44b0f"
``` ```
@@ -165,6 +165,11 @@ turn that pattern into a new invariant, test, or consistency repair.
Done when renormalization scaffolding has an explicit "add the next guard here" Done when renormalization scaffolding has an explicit "add the next guard here"
pattern instead of relying on ad hoc fixes. pattern instead of relying on ad hoc fixes.
Result 2026-05-23: added a renormalization rule registry and
`scripts/consistency_check.py --renormalization-guide`. The guide lists active
repair rules and the concise checklist for turning the next recognized drift
pattern into metadata, detection, repair, and tests.
## T07 - Regression Tests ## T07 - Regression Tests
```task ```task
@@ -191,6 +196,9 @@ Progress 2026-05-23: added consistency checker coverage for lifecycle
renormalization detection and repair, including a guard that C-23 takes renormalization detection and repair, including a guard that C-23 takes
precedence over generic C-04 status drift. precedence over generic C-04 status drift.
Progress 2026-05-23: added guide coverage so the drift-learning scaffold has a
stable test anchor.
## Acceptance Criteria ## Acceptance Criteria
- Starting task work deterministically activates the parent workstream. - Starting task work deterministically activates the parent workstream.