From cb60a00ee1e7ed191331d6440b0072a86bf7b56e Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 11 Sep 2026 16:53:59 +0200 Subject: [PATCH] Preserve closed Railiance item bindings during routine sync Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc --- src/repo_manager/identifiers.py | 13 +++++++- tests/test_historical_sync_validation.py | 39 ++++++++++++++++++++++++ workplans/ADHOC-2026-09-11.md | 39 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 workplans/ADHOC-2026-09-11.md diff --git a/src/repo_manager/identifiers.py b/src/repo_manager/identifiers.py index f658ca2..45ae24a 100644 --- a/src/repo_manager/identifiers.py +++ b/src/repo_manager/identifiers.py @@ -862,7 +862,18 @@ def ensure_missing_work_record_identifiers( for number, task in enumerate(parsed.tasks, start=1): if task.status not in {"done", "cancel"} or not task.state_hub_task_id: continue - if not re.fullmatch(re.escape(parsed.id) + r"-T[0-9]{2}[a-z]", task.id or ""): + # RAILIANCE-WP-0004 is a closed historical workplan whose source + # used the old I01..I07 item labels. These exact, UUID-bound + # labels are preserved like the older suffix records below; they + # must not become a general I-suffix grandfather rule. + legacy_item = ( + parsed.id == "RAILIANCE-WP-0004" + and re.fullmatch(r"RAILIANCE-WP-0004-I(?:0[1-7])", task.id or "") + ) + if not ( + re.fullmatch(re.escape(parsed.id) + r"-T[0-9]{2}[a-z]", task.id or "") + or legacy_item + ): continue try: uuid.UUID(task.state_hub_task_id) diff --git a/tests/test_historical_sync_validation.py b/tests/test_historical_sync_validation.py index 1c32032..7e23077 100644 --- a/tests/test_historical_sync_validation.py +++ b/tests/test_historical_sync_validation.py @@ -61,3 +61,42 @@ def test_historical_collision_still_blocks(tmp_path): with pytest.raises(ValueError, match='collisions='): ensure_missing_work_record_identifiers(tmp_path) assert live.read_bytes() == before + + +def _railiance_item(root: Path, *, parent="RAILIANCE-WP-0004", status="finished", + task_status="done", task_id="RAILIANCE-WP-0004-I01", + binding="44444444-4444-4444-8444-444444444444") -> Path: + path = root / "workplans" / "archived" / "railiance.md" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text( + f'---\nid: {parent}\nstatus: {status}\n' + 'state_hub_workstream_id: "33333333-3333-4333-8333-333333333333"\n---\n' + f'\n```task\nid: {task_id}\nstatus: {task_status}\n' + f'state_hub_task_id: "{binding}"\n```\n', + encoding="utf-8", + ) + return path + + +def test_railiance_historical_item_labels_preserve_uuid(tmp_path: Path) -> None: + path = _railiance_item(tmp_path) + before = path.read_bytes() + result = ensure_missing_work_record_identifiers(tmp_path, execute=False) + assert result["assignments"] == [] + assert result["files_changed"] == [] + assert path.read_bytes() == before + + +@pytest.mark.parametrize("kwargs", [ + {"status": "active"}, + {"task_status": "todo"}, + {"parent": "RAILIANCE-WP-0005", "task_id": "RAILIANCE-WP-0005-I01"}, + {"binding": ""}, + {"binding": "not-a-uuid"}, + {"task_id": "RAILIANCE-WP-0004-I08"}, + {"task_id": "OTHER-WP-0004-I01"}, +]) +def test_railiance_item_label_preservation_is_bounded(tmp_path: Path, kwargs) -> None: + _railiance_item(tmp_path, **kwargs) + with pytest.raises(ValueError, match="identities are not safe"): + ensure_missing_work_record_identifiers(tmp_path) diff --git a/workplans/ADHOC-2026-09-11.md b/workplans/ADHOC-2026-09-11.md new file mode 100644 index 0000000..b35903f --- /dev/null +++ b/workplans/ADHOC-2026-09-11.md @@ -0,0 +1,39 @@ +--- +id: RMGR-WP-ADHOC-2026-09-11 +type: workplan +title: "Preserve seven bound historical Railiance task labels during routine sync" +domain: infotech +repo: repo-manager +status: finished +owner: codex +topic_slug: infotech +created: "2026-09-11" +updated: "2026-09-11" +--- + +## Preserve closed historical item labels without identity migration + +```task +id: RMGR-WP-ADHOC-2026-09-11-T01 +status: done +priority: medium +``` + +Routine Railiance Apps sync stopped on seven completed, UUID-bound historical +RAILIANCE-WP-0004-I01..I07 tasks while publishing the invited Vergabe pilot +contract. Extend the existing historical-binding preservation path for exactly +this closed parent and these labels. Conformance reporting remains strict; +active/unfinished tasks, unbound or invalid UUIDs, other parents and I labels +outside the observed set still refuse. Preserve existing source IDs, UUIDs, +statuses and historical references. New records continue to require canonical +IDs. No Hub identity mutation or sealed UUID migration is involved. + +The attempted label-only normalization was withdrawn before commit/projection: +ordinary sync does not rename nonblank task record IDs, and changing the UUIDs +would enlarge the task without improving the pilot. This explicit historical +preservation resolves the routine sync blocker. Canonical migration remains the +existing RMGR-WP-0005 programme, not a new pilot prerequisite. + +Validation: targeted historical/identity regression suite and Ruff, plus an +actual Railiance Apps dry run preserving all seven bindings without assigning +new UUIDs to them. Parent return is RAPPS-WP-0014 and HFACT-WP-0001.