diff --git a/canon/standards/work-record-types.yaml b/canon/standards/work-record-types.yaml index 29c4f97..c48ccc5 100644 --- a/canon/standards/work-record-types.yaml +++ b/canon/standards/work-record-types.yaml @@ -6,7 +6,7 @@ status: active kinds: - kind: workplan id_patterns: - - "^[A-Z]+-WP-[0-9]{4}$" + - "^[A-Z][A-Z0-9]*(?:-[A-Z][A-Z0-9]*)*-WP-[0-9]{4}$" lifecycle: [proposed, ready, active, blocked, backlog, finished, archived] legacy_patterns: - pattern: "^MASON-0001$" @@ -14,7 +14,7 @@ kinds: grandfathered: true - kind: task id_patterns: - - "^[A-Z]+-WP-[0-9]{4}-T[0-9]{2,3}$" + - "^[A-Z][A-Z0-9]*(?:-[A-Z][A-Z0-9]*)*-WP-[0-9]{4}-T[0-9]{2,3}$" - "^ADHOC-[0-9]{4}-[0-9]{2}-[0-9]{2}-T[0-9]{2}$" lifecycle: [wait, todo, progress, done, cancel] legacy_patterns: diff --git a/canon/standards/work-record-types_v0.1.md b/canon/standards/work-record-types_v0.1.md index 83fe1ea..802a344 100644 --- a/canon/standards/work-record-types_v0.1.md +++ b/canon/standards/work-record-types_v0.1.md @@ -6,7 +6,7 @@ domain: custodian status: active version: "0.1" created: "2026-07-20" -updated: "2026-08-22" +updated: "2026-08-23" scope: fleet related_workplans: - CUST-WP-0060 @@ -67,16 +67,28 @@ attention_cost, …) extend the spine; they never replace it. Rules: -1. **The registry is closed.** An id pattern not registered in +1. **Prefix grammar:** `{PREFIX}` is one or more uppercase alphanumeric + segments, each beginning with a letter, joined by hyphens. Both compact + prefixes such as `CUST` and repository-derived prefixes such as + `RAPP-OPENBAO` or `RAIL-K8S` are canonical. The final `-WP-` token remains + the unambiguous kind separator. +2. **The registry is closed.** An id pattern not registered in `work-record-types.yaml` fails CI and `fix-consistency` (the *sidetrack detector*). New kinds/prefixes are added by canon PR — deliberately cheap (one YAML entry + one schema) so the legitimate path is easier than any workaround. -2. **Grandfathering:** existing ids are never renamed. Legacy prefixes map +3. **Grandfathering:** existing ids are never renamed. Legacy prefixes map to kinds in the registry; retirement of a *scheme* (never of records) follows the legacy-meter discipline of `workplan-terminology-fleet_v0.1.md`. +Compatibility correction 2026-08-23: State Hub message +`24504ce7-4f73-47da-a150-629b07b37073` exposed that the machine pattern did +not accept the already owner-registered `RAPP-OPENBAO-WP` prefix. The grammar +now matches ADR-007's repository-derived prefix model; prefix ownership and +uniqueness remain separate registry checks and are not weakened by this syntax +correction. + Risk Nexus findings (`RISK-F-*`) and regulatory records (`RISK-REG-*`) are registered as variants of `register-entry`, not as parallel work kinds. Their lifecycles remain owned by the register convention. `RISK-N-*` notes are diff --git a/tests/test_validate_work_records.py b/tests/test_validate_work_records.py index 9f7ec04..9d24d6b 100644 --- a/tests/test_validate_work_records.py +++ b/tests/test_validate_work_records.py @@ -52,6 +52,13 @@ class TestClassify: assert classify("CUST-WP-0060", kinds) == "workplan" assert classify("CUST-WP-0060-T01", kinds) == "task" + def test_repository_derived_multi_segment_workplan_prefix(self, kinds): + assert classify("RAPP-OPENBAO-WP-0002", kinds) == "workplan" + assert classify("RAPP-OPENBAO-WP-0002-T01", kinds) == "task" + assert classify("RAIL-K8S-WP-0001", kinds) == "workplan" + assert classify("RAPP--OPENBAO-WP-0002", kinds) is None + assert classify("1RAPP-OPENBAO-WP-0002", kinds) is None + def test_new_kind_ids(self, kinds): assert classify("BINKY-IN-0001", kinds) == "intake" assert classify("BINKY-DEC-2026-001", kinds) == "decision"