From d9f1f3f71a7f615ba844908ce32b7518d523a407 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 24 May 2026 14:08:40 +0200 Subject: [PATCH] feat: canonicalize duplicate repo identities --- docs/registry-onboarding.md | 7 + ...-05-24-railiance-bootstrap-identities.json | 225 +++++++----------- ...-railiance-bootstrap-ownership-review.json | 138 ++++------- ...5-24-railiance-bootstrap-update-delta.json | 10 +- railiance_fabric/accountability_roots.py | 92 ++++++- registry/local-repos.yaml | 2 + tests/test_accountability_root_adapters.py | 53 +++++ ...19-duplicate-repository-identity-review.md | 23 +- 8 files changed, 309 insertions(+), 241 deletions(-) diff --git a/docs/registry-onboarding.md b/docs/registry-onboarding.md index 949c624..ab85019 100644 --- a/docs/registry-onboarding.md +++ b/docs/registry-onboarding.md @@ -81,3 +81,10 @@ repo responsible for the whole external fabric relation model. from active State Hub repo records whose local path exists on the current WSL host. It registers cross-domain local repos into the Fabric registry while only syncing graph snapshots for repos that already have Fabric declarations. + +When two active repo records point at the same checkout path, accountability +root discovery treats them as aliases of one repository identity. The canonical +identity can be declared with `canonical_slug`; otherwise discovery prefers the +slug matching the checkout directory name and then the entry with a remote URL. +Use `split_identity: true` only when shared paths are intentional and should +remain separate review candidates. diff --git a/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-identities.json b/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-identities.json index 972f961..1323943 100644 --- a/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-identities.json +++ b/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-identities.json @@ -794,22 +794,13 @@ "review_state": "candidate", "subfabric_id": "" }, - { - "fabric_id": "fabric.railiance.primary", - "id": "identity:repository:railiance-hosts", - "kind": "Repository", - "label": "railiance-hosts", - "owner_actor_id": "actor.railiance.primary-lord", - "review_state": "needs_review", - "subfabric_id": "" - }, { "fabric_id": "fabric.railiance.primary", "id": "identity:repository:railiance-infra", "kind": "Repository", "label": "railiance-infra", "owner_actor_id": "actor.railiance.primary-lord", - "review_state": "needs_review", + "review_state": "candidate", "subfabric_id": "" }, { @@ -854,16 +845,7 @@ "kind": "Repository", "label": "vergabe-teilnahme", "owner_actor_id": "actor.railiance.primary-lord", - "review_state": "needs_review", - "subfabric_id": "" - }, - { - "fabric_id": "fabric.railiance.primary", - "id": "identity:repository:vergabe_teilnahme", - "kind": "Repository", - "label": "vergabe_teilnahme", - "owner_actor_id": "actor.railiance.primary-lord", - "review_state": "needs_review", + "review_state": "candidate", "subfabric_id": "" }, { @@ -878,11 +860,11 @@ ] }, "evidence_run": { - "generated_at": "2026-05-24T08:24:26Z", + "generated_at": "2026-05-24T12:02:38Z", "manifest_fingerprint": "dd279b655d68222a99671c1c875afdaa70ae2f907fd526e6673ddf756bc90dae", "manifest_id": "railiance.accountability-roots" }, - "generated_at": "2026-05-24T08:24:56Z", + "generated_at": "2026-05-24T12:03:24Z", "identity_candidates": [ { "aliases": [ @@ -925,13 +907,13 @@ "attributes": { "exists": true, "path_type": "file", - "sha256": "c77fe4edf370a1f17cacecae25b3075af45d19714dc016046c27e69fe2a45f49", - "size_bytes": 5751, + "sha256": "3177ab34c6e6c95d1b3a44cdd31a820a2b6e1097b6dfe0c6cf907a4debd98817", + "size_bytes": 8827, "source_evidence_type": "backup_recovery" }, "confidence": 0.65, "evidence_ids": [ - "evidence:root.backup-recovery.metadata:99d0713b16e64003" + "evidence:root.backup-recovery.metadata:409952d6b266c8f5" ], "fabric_id": "fabric.railiance.primary", "graph_id": "docs/financial-fabric-operator-guide.md", @@ -969,13 +951,13 @@ "attributes": { "exists": true, "path_type": "file", - "sha256": "4699908886d6867115631e16f1deef8fed605cf8a6aa3648c4e8e32f5530d67f", - "size_bytes": 8217, + "sha256": "b8dd44d083c05f3806148fe5fd78f4937d17fe2acc158268afc8104800236283", + "size_bytes": 8291, "source_evidence_type": "registry_manifest" }, "confidence": 0.6, "evidence_ids": [ - "evidence:root.registry.local-repos:42f1d77e4f940f08" + "evidence:root.registry.local-repos:8823b8ff493ed5d3" ], "fabric_id": "fabric.railiance.primary", "graph_id": "registry_manifest", @@ -2163,6 +2145,7 @@ "/home/worsch/activity-core" ], "attributes": { + "declared_repo_slug": "activity-core", "default_branch": "main", "domain": "custodian", "has_local_path": true, @@ -2173,7 +2156,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:8cee6f6ee4eb63a1" + "evidence:root.registry.local-repos:22237205182108df" ], "fabric_id": "fabric.railiance.primary", "graph_id": "activity-core", @@ -2190,6 +2173,7 @@ "gitea-remote:coulomb/artifact-store.git" ], "attributes": { + "declared_repo_slug": "artifact-store", "default_branch": "main", "domain": "stack", "has_local_path": true, @@ -2200,7 +2184,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:8cd7cecfc6d233d5" + "evidence:root.registry.local-repos:81956b1f27ab7ba7" ], "fabric_id": "fabric.railiance.primary", "graph_id": "artifact-store", @@ -2216,6 +2200,7 @@ "/home/worsch/domain-tree" ], "attributes": { + "declared_repo_slug": "domain-tree", "default_branch": "main", "domain": "netkingdom", "has_local_path": true, @@ -2226,7 +2211,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:9bb313ace63f7355" + "evidence:root.registry.local-repos:834b7cba9ec4a38b" ], "fabric_id": "fabric.railiance.primary", "graph_id": "domain-tree", @@ -2242,6 +2227,7 @@ "/home/worsch/flex-auth" ], "attributes": { + "declared_repo_slug": "flex-auth", "default_branch": "main", "domain": "netkingdom", "has_local_path": true, @@ -2252,7 +2238,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:6f4d5102aa3d4f73" + "evidence:root.registry.local-repos:4a54bd51cf87e3dc" ], "fabric_id": "fabric.railiance.primary", "graph_id": "flex-auth", @@ -2269,6 +2255,7 @@ "gitea-remote:coulomb/guide-board.git" ], "attributes": { + "declared_repo_slug": "guide-board", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2279,7 +2266,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:e0181f99c192f303" + "evidence:root.registry.local-repos:c15451d5df0dcff5" ], "fabric_id": "fabric.railiance.primary", "graph_id": "guide-board", @@ -2296,6 +2283,7 @@ "gitea-remote:coulomb/helix-forge.git" ], "attributes": { + "declared_repo_slug": "helix-forge", "default_branch": "main", "domain": "helix_forge", "has_local_path": true, @@ -2306,7 +2294,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:4decf44c598b4a57" + "evidence:root.registry.local-repos:80c9877733858532" ], "fabric_id": "fabric.railiance.primary", "graph_id": "helix-forge", @@ -2322,6 +2310,7 @@ "/home/worsch/ihp-railiance-probe" ], "attributes": { + "declared_repo_slug": "ihp-railiance-probe", "default_branch": "main", "domain": "stack", "has_local_path": true, @@ -2332,7 +2321,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:bb23149605740e9a" + "evidence:root.registry.local-repos:1eb8d1b6ad1614b6" ], "fabric_id": "fabric.railiance.primary", "graph_id": "ihp-railiance-probe", @@ -2349,6 +2338,7 @@ "http://92.205.130.254:32166/coulomb/infospace-bench.git" ], "attributes": { + "declared_repo_slug": "infospace-bench", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2359,7 +2349,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:27d6f12c7f495b31" + "evidence:root.registry.local-repos:0080e417339c8125" ], "fabric_id": "fabric.railiance.primary", "graph_id": "infospace-bench", @@ -2376,6 +2366,7 @@ "ssh://git@92.205.130.254:30022/coulomb/inter-hub.git" ], "attributes": { + "declared_repo_slug": "inter-hub", "default_branch": "main", "domain": "custodian", "has_local_path": true, @@ -2386,7 +2377,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:efeeaf25f0ab8aeb" + "evidence:root.registry.local-repos:56bf796ca4cf9dc9" ], "fabric_id": "fabric.railiance.primary", "graph_id": "inter-hub", @@ -2402,6 +2393,7 @@ "/home/worsch/issue-core" ], "attributes": { + "declared_repo_slug": "issue-core", "default_branch": "main", "domain": "custodian", "has_local_path": true, @@ -2412,7 +2404,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:d09d1c836c4e36bf" + "evidence:root.registry.local-repos:f75ad974f570b054" ], "fabric_id": "fabric.railiance.primary", "graph_id": "issue-core", @@ -2428,6 +2420,7 @@ "/home/worsch/key-cape" ], "attributes": { + "declared_repo_slug": "key-cape", "default_branch": "main", "domain": "netkingdom", "has_local_path": true, @@ -2438,7 +2431,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:43caac3f3cfa4ad7" + "evidence:root.registry.local-repos:325240be91c8f467" ], "fabric_id": "fabric.railiance.primary", "graph_id": "key-cape", @@ -2455,6 +2448,7 @@ "http://92.205.130.254:32166/coulomb/kontextual-engine.git" ], "attributes": { + "declared_repo_slug": "kontextual-engine", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2465,7 +2459,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:ead9ecf1b49cf3cc" + "evidence:root.registry.local-repos:bb569c6a5861ae35" ], "fabric_id": "fabric.railiance.primary", "graph_id": "kontextual-engine", @@ -2482,6 +2476,7 @@ "http://92.205.130.254:32166/coulomb/llm-connect.git" ], "attributes": { + "declared_repo_slug": "llm-connect", "default_branch": "main", "domain": "custodian", "has_local_path": true, @@ -2492,7 +2487,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:106e66cb38d653c2" + "evidence:root.registry.local-repos:454b64bf8f305715" ], "fabric_id": "fabric.railiance.primary", "graph_id": "llm-connect", @@ -2508,6 +2503,7 @@ "/home/worsch/markitect-filter" ], "attributes": { + "declared_repo_slug": "markitect-filter", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2518,7 +2514,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:61b9d69ccca5dc40" + "evidence:root.registry.local-repos:b5beebf1b581ab3d" ], "fabric_id": "fabric.railiance.primary", "graph_id": "markitect-filter", @@ -2534,6 +2530,7 @@ "/home/worsch/markitect-main" ], "attributes": { + "declared_repo_slug": "markitect-project", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2544,7 +2541,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:01ac8b9b2057660b" + "evidence:root.registry.local-repos:a1c3588865f46885" ], "fabric_id": "fabric.railiance.primary", "graph_id": "markitect-project", @@ -2560,6 +2557,7 @@ "/home/worsch/markitect-quarkdown" ], "attributes": { + "declared_repo_slug": "markitect-quarkdown", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2570,7 +2568,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:49b6aaf12dfa6b08" + "evidence:root.registry.local-repos:984290ee72d834a6" ], "fabric_id": "fabric.railiance.primary", "graph_id": "markitect-quarkdown", @@ -2586,6 +2584,7 @@ "/home/worsch/markitect-tool" ], "attributes": { + "declared_repo_slug": "markitect-tool", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2596,7 +2595,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:de4f013ef9bb3778" + "evidence:root.registry.local-repos:ca424cfd48266d16" ], "fabric_id": "fabric.railiance.primary", "graph_id": "markitect-tool", @@ -2613,6 +2612,7 @@ "http://92.205.130.254:32166/coulomb/net-kingdom.git" ], "attributes": { + "declared_repo_slug": "net-kingdom", "default_branch": "main", "domain": "netkingdom", "has_local_path": true, @@ -2623,7 +2623,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:909804aaf07ec7e6" + "evidence:root.registry.local-repos:03758b81b89c7629" ], "fabric_id": "fabric.railiance.primary", "graph_id": "net-kingdom", @@ -2640,6 +2640,7 @@ "gitea-remote:coulomb/open-cmis-tck.git" ], "attributes": { + "declared_repo_slug": "open-cmis-tck", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2650,7 +2651,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:fdf5bfa0d976ccaf" + "evidence:root.registry.local-repos:3bf7fb8f32253dd0" ], "fabric_id": "fabric.railiance.primary", "graph_id": "open-cmis-tck", @@ -2666,6 +2667,7 @@ "/home/worsch/open-reuse" ], "attributes": { + "declared_repo_slug": "open-reuse", "default_branch": "main", "domain": "stack", "has_local_path": true, @@ -2676,7 +2678,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:79a1e537db736d46" + "evidence:root.registry.local-repos:31b9f4afdb3530bf" ], "fabric_id": "fabric.railiance.primary", "graph_id": "open-reuse", @@ -2692,6 +2694,7 @@ "/home/worsch/ops-bridge" ], "attributes": { + "declared_repo_slug": "ops-bridge", "default_branch": "main", "domain": "custodian", "has_local_path": true, @@ -2702,7 +2705,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:20c8f1ccfa973270" + "evidence:root.registry.local-repos:e470d6f2649b79b0" ], "fabric_id": "fabric.railiance.primary", "graph_id": "ops-bridge", @@ -2719,6 +2722,7 @@ "gitea-remote:coulomb/ops-warden.git" ], "attributes": { + "declared_repo_slug": "ops-warden", "default_branch": "main", "domain": "custodian", "has_local_path": true, @@ -2729,7 +2733,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:b26cfafde568e482" + "evidence:root.registry.local-repos:35e344dd95996ddc" ], "fabric_id": "fabric.railiance.primary", "graph_id": "ops-warden", @@ -2745,6 +2749,7 @@ "/home/worsch/phase-memory" ], "attributes": { + "declared_repo_slug": "phase-memory", "default_branch": "main", "domain": "markitect", "has_local_path": true, @@ -2755,7 +2760,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:f630bbd62ffec78b" + "evidence:root.registry.local-repos:ea9d5a0e78e8e4a6" ], "fabric_id": "fabric.railiance.primary", "graph_id": "phase-memory", @@ -2772,6 +2777,7 @@ "http://92.205.130.254:32166/coulomb/railiance-apps.git" ], "attributes": { + "declared_repo_slug": "railiance-apps", "default_branch": "main", "domain": "railiance", "has_local_path": true, @@ -2782,7 +2788,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:40beb96ec0c2498c" + "evidence:root.registry.local-repos:36c141e579a5ce37" ], "fabric_id": "fabric.railiance.primary", "graph_id": "railiance-apps", @@ -2799,6 +2805,7 @@ "http://92.205.130.254:32166/coulomb/railiance-cluster.git" ], "attributes": { + "declared_repo_slug": "railiance-cluster", "default_branch": "main", "domain": "railiance", "has_local_path": true, @@ -2809,7 +2816,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:981e44cb11973e44" + "evidence:root.registry.local-repos:5f18d3c61d94023c" ], "fabric_id": "fabric.railiance.primary", "graph_id": "railiance-cluster", @@ -2826,6 +2833,7 @@ "http://92.205.130.254:32166/coulomb/railiance-enablement.git" ], "attributes": { + "declared_repo_slug": "railiance-enablement", "default_branch": "main", "domain": "railiance", "has_local_path": true, @@ -2836,7 +2844,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:eacbd7a28e121081" + "evidence:root.registry.local-repos:754c6a30f9ff3671" ], "fabric_id": "fabric.railiance.primary", "graph_id": "railiance-enablement", @@ -2854,13 +2862,14 @@ ], "attributes": { "branch": "main", + "declared_repo_slug": "railiance-fabric", "default_branch": "main", "domain": "railiance", "has_fabric_dir": true, "has_git_dir": true, "has_local_path": true, "has_remote_url": false, - "head": "355b7be66aad8a766716395585c937e3be5ce2b0", + "head": "ea2fa1203b856269855ea92fa3fa477d8bef27a3", "name": "railiance-fabric", "path_exists": true, "remote_origin": "gitea-remote:coulomb/railiance-fabric.git", @@ -2870,8 +2879,8 @@ }, "confidence": 0.9, "evidence_ids": [ - "evidence:root.registry.local-repos:f09103b02c4ae9fc", - "evidence:root.railiance-fabric.checkout:964745ce3b6dd491" + "evidence:root.registry.local-repos:deace4d5a96fc0d6", + "evidence:root.railiance-fabric.checkout:c9cb1d35bb99f2e0" ], "fabric_id": "fabric.railiance.primary", "graph_id": "railiance-fabric", @@ -2881,63 +2890,36 @@ "review_state": "candidate", "stable_key": "identity:repository:railiance-fabric" }, - { - "aliases": [ - "railiance-hosts", - "/home/worsch/railiance-infra" - ], - "attributes": { - "ambiguous_aliases": [ - "home-worsch-railiance-infra" - ], - "default_branch": "main", - "domain": "railiance", - "has_local_path": true, - "has_remote_url": false, - "name": "railiance-hosts", - "source_evidence_type": "registered_repository", - "state_hub_repo_id": "2db668cf-6c52-4965-951f-0d5e617de9e3" - }, - "confidence": 0.85, - "evidence_ids": [ - "evidence:root.registry.local-repos:f54177132294798c" - ], - "fabric_id": "fabric.railiance.primary", - "graph_id": "railiance-hosts", - "identity_type": "Repository", - "label": "railiance-hosts", - "owner_actor_id": "actor.railiance.primary-lord", - "review_state": "needs_review", - "stable_key": "identity:repository:railiance-hosts" - }, { "aliases": [ "railiance-infra", + "railiance-hosts", "/home/worsch/railiance-infra", "http://92.205.130.254:32166/coulomb/railiance-infra.git" ], "attributes": { - "ambiguous_aliases": [ - "home-worsch-railiance-infra" - ], + "canonical_slug": "railiance-infra", + "declared_repo_slug": "railiance-infra", "default_branch": "main", "domain": "railiance", "has_local_path": true, "has_remote_url": true, + "identity_resolution": "duplicate_path_alias", "name": "railiance-infra", "source_evidence_type": "registered_repository", "state_hub_repo_id": "485187c0-4fad-42f7-984c-5e317a66c5de" }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:c960c3a821576795" + "evidence:root.registry.local-repos:fbff10063021cf4b", + "evidence:root.registry.local-repos:495887a2047c9f53" ], "fabric_id": "fabric.railiance.primary", "graph_id": "railiance-infra", "identity_type": "Repository", "label": "railiance-infra", "owner_actor_id": "actor.railiance.primary-lord", - "review_state": "needs_review", + "review_state": "candidate", "stable_key": "identity:repository:railiance-infra" }, { @@ -2947,6 +2929,7 @@ "http://92.205.130.254:32166/coulomb/railiance-platform.git" ], "attributes": { + "declared_repo_slug": "railiance-platform", "default_branch": "main", "domain": "railiance", "has_local_path": true, @@ -2957,7 +2940,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:a12e96e6d9418b3d" + "evidence:root.registry.local-repos:d8765e0f7c1e10f8" ], "fabric_id": "fabric.railiance.primary", "graph_id": "railiance-platform", @@ -2974,6 +2957,7 @@ "gitea-remote:coulomb/repo-scoping.git" ], "attributes": { + "declared_repo_slug": "repo-scoping", "default_branch": "main", "domain": "capabilities", "has_local_path": true, @@ -2984,7 +2968,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:6addadec236dd62f" + "evidence:root.registry.local-repos:c12e989814b1c70c" ], "fabric_id": "fabric.railiance.primary", "graph_id": "repo-scoping", @@ -3001,6 +2985,7 @@ "gitea-remote:coulomb/state-hub.git" ], "attributes": { + "declared_repo_slug": "state-hub", "default_branch": "main", "domain": "custodian", "has_local_path": true, @@ -3011,7 +2996,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:720808c98666e541" + "evidence:root.registry.local-repos:89431ff6a8ef2996" ], "fabric_id": "fabric.railiance.primary", "graph_id": "state-hub", @@ -3028,6 +3013,7 @@ "http://gitea.local/worsch/the-custodian" ], "attributes": { + "declared_repo_slug": "the-custodian", "default_branch": "main", "domain": "custodian", "has_local_path": true, @@ -3038,7 +3024,7 @@ }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:100a6d70bb2f298a" + "evidence:root.registry.local-repos:8feba7a45d2d751d" ], "fabric_id": "fabric.railiance.primary", "graph_id": "the-custodian", @@ -3052,60 +3038,33 @@ "aliases": [ "vergabe-teilnahme", "/home/worsch/vergabe-teilnahme", - "gitea-remote:coulomb/vergabe-teilnahme.git" + "gitea-remote:coulomb/vergabe-teilnahme.git", + "vergabe_teilnahme" ], "attributes": { - "ambiguous_aliases": [ - "home-worsch-vergabe-teilnahme" - ], - "default_branch": "main", - "domain": "coulomb_social", - "has_local_path": true, - "has_remote_url": true, - "name": "vergabe-teilnahme", - "source_evidence_type": "registered_repository", - "state_hub_repo_id": "ff7b0754-9d54-4abf-a752-d342870f5993" - }, - "confidence": 0.85, - "evidence_ids": [ - "evidence:root.registry.local-repos:e3fae7fb0c8a6867" - ], - "fabric_id": "fabric.railiance.primary", - "graph_id": "vergabe-teilnahme", - "identity_type": "Repository", - "label": "vergabe-teilnahme", - "owner_actor_id": "actor.railiance.primary-lord", - "review_state": "needs_review", - "stable_key": "identity:repository:vergabe-teilnahme" - }, - { - "aliases": [ - "vergabe_teilnahme", - "/home/worsch/vergabe-teilnahme" - ], - "attributes": { - "ambiguous_aliases": [ - "home-worsch-vergabe-teilnahme" - ], + "canonical_slug": "vergabe-teilnahme", + "declared_repo_slug": "vergabe_teilnahme", "default_branch": "main", "domain": "vergabe_teilnahme", "has_local_path": true, "has_remote_url": false, + "identity_resolution": "duplicate_path_alias", "name": "vergabe-teilnahme", "source_evidence_type": "registered_repository", "state_hub_repo_id": "46d9338e-d079-4953-b174-0ea64761be5c" }, "confidence": 0.85, "evidence_ids": [ - "evidence:root.registry.local-repos:0703300ec1742fe1" + "evidence:root.registry.local-repos:a0331bbcdaf39a23", + "evidence:root.registry.local-repos:ff7fa8110f6c7097" ], "fabric_id": "fabric.railiance.primary", - "graph_id": "vergabe_teilnahme", + "graph_id": "vergabe-teilnahme", "identity_type": "Repository", - "label": "vergabe_teilnahme", + "label": "vergabe-teilnahme", "owner_actor_id": "actor.railiance.primary-lord", - "review_state": "needs_review", - "stable_key": "identity:repository:vergabe_teilnahme" + "review_state": "candidate", + "stable_key": "identity:repository:vergabe-teilnahme" }, { "aliases": [ diff --git a/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-ownership-review.json b/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-ownership-review.json index 0d7db39..af3c0f5 100644 --- a/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-ownership-review.json +++ b/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-ownership-review.json @@ -1,11 +1,11 @@ { "apiVersion": "railiance.fabric/v1alpha2", "evidence_run": { - "generated_at": "2026-05-24T08:25:15Z", + "generated_at": "2026-05-24T12:03:30Z", "manifest_fingerprint": "dd279b655d68222a99671c1c875afdaa70ae2f907fd526e6673ddf756bc90dae", "manifest_id": "railiance.accountability-roots" }, - "generated_at": "2026-05-24T08:25:48Z", + "generated_at": "2026-05-24T12:04:18Z", "items": [ { "blockers": [], @@ -51,7 +51,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.backup-recovery.metadata:99d0713b16e64003" + "evidence:root.backup-recovery.metadata:409952d6b266c8f5" ], "identity_type": "BackupRecoveryRoot", "label": "financial-fabric-operator-guide.md", @@ -91,7 +91,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:42f1d77e4f940f08" + "evidence:root.registry.local-repos:8823b8ff493ed5d3" ], "identity_type": "CatalogRoot", "label": "None", @@ -1207,7 +1207,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:8cee6f6ee4eb63a1" + "evidence:root.registry.local-repos:22237205182108df" ], "identity_type": "Repository", "label": "activity-core", @@ -1227,7 +1227,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:8cd7cecfc6d233d5" + "evidence:root.registry.local-repos:81956b1f27ab7ba7" ], "identity_type": "Repository", "label": "artifact-store", @@ -1247,7 +1247,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:9bb313ace63f7355" + "evidence:root.registry.local-repos:834b7cba9ec4a38b" ], "identity_type": "Repository", "label": "domain-tree", @@ -1267,7 +1267,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:6f4d5102aa3d4f73" + "evidence:root.registry.local-repos:4a54bd51cf87e3dc" ], "identity_type": "Repository", "label": "flex-auth", @@ -1287,7 +1287,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:e0181f99c192f303" + "evidence:root.registry.local-repos:c15451d5df0dcff5" ], "identity_type": "Repository", "label": "guide-board", @@ -1307,7 +1307,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:4decf44c598b4a57" + "evidence:root.registry.local-repos:80c9877733858532" ], "identity_type": "Repository", "label": "helix-forge", @@ -1327,7 +1327,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:bb23149605740e9a" + "evidence:root.registry.local-repos:1eb8d1b6ad1614b6" ], "identity_type": "Repository", "label": "ihp-railiance-probe", @@ -1347,7 +1347,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:27d6f12c7f495b31" + "evidence:root.registry.local-repos:0080e417339c8125" ], "identity_type": "Repository", "label": "infospace-bench", @@ -1367,7 +1367,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:efeeaf25f0ab8aeb" + "evidence:root.registry.local-repos:56bf796ca4cf9dc9" ], "identity_type": "Repository", "label": "inter-hub", @@ -1387,7 +1387,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:d09d1c836c4e36bf" + "evidence:root.registry.local-repos:f75ad974f570b054" ], "identity_type": "Repository", "label": "issue-core", @@ -1407,7 +1407,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:43caac3f3cfa4ad7" + "evidence:root.registry.local-repos:325240be91c8f467" ], "identity_type": "Repository", "label": "key-cape", @@ -1427,7 +1427,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:ead9ecf1b49cf3cc" + "evidence:root.registry.local-repos:bb569c6a5861ae35" ], "identity_type": "Repository", "label": "kontextual-engine", @@ -1447,7 +1447,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:106e66cb38d653c2" + "evidence:root.registry.local-repos:454b64bf8f305715" ], "identity_type": "Repository", "label": "llm-connect", @@ -1467,7 +1467,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:61b9d69ccca5dc40" + "evidence:root.registry.local-repos:b5beebf1b581ab3d" ], "identity_type": "Repository", "label": "markitect-filter", @@ -1487,7 +1487,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:01ac8b9b2057660b" + "evidence:root.registry.local-repos:a1c3588865f46885" ], "identity_type": "Repository", "label": "markitect-project", @@ -1507,7 +1507,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:49b6aaf12dfa6b08" + "evidence:root.registry.local-repos:984290ee72d834a6" ], "identity_type": "Repository", "label": "markitect-quarkdown", @@ -1527,7 +1527,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:de4f013ef9bb3778" + "evidence:root.registry.local-repos:ca424cfd48266d16" ], "identity_type": "Repository", "label": "markitect-tool", @@ -1547,7 +1547,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:909804aaf07ec7e6" + "evidence:root.registry.local-repos:03758b81b89c7629" ], "identity_type": "Repository", "label": "net-kingdom", @@ -1567,7 +1567,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:fdf5bfa0d976ccaf" + "evidence:root.registry.local-repos:3bf7fb8f32253dd0" ], "identity_type": "Repository", "label": "open-cmis-tck", @@ -1587,7 +1587,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:79a1e537db736d46" + "evidence:root.registry.local-repos:31b9f4afdb3530bf" ], "identity_type": "Repository", "label": "open-reuse", @@ -1607,7 +1607,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:20c8f1ccfa973270" + "evidence:root.registry.local-repos:e470d6f2649b79b0" ], "identity_type": "Repository", "label": "ops-bridge", @@ -1627,7 +1627,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:b26cfafde568e482" + "evidence:root.registry.local-repos:35e344dd95996ddc" ], "identity_type": "Repository", "label": "ops-warden", @@ -1647,7 +1647,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:f630bbd62ffec78b" + "evidence:root.registry.local-repos:ea9d5a0e78e8e4a6" ], "identity_type": "Repository", "label": "phase-memory", @@ -1667,7 +1667,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:40beb96ec0c2498c" + "evidence:root.registry.local-repos:36c141e579a5ce37" ], "identity_type": "Repository", "label": "railiance-apps", @@ -1687,7 +1687,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:981e44cb11973e44" + "evidence:root.registry.local-repos:5f18d3c61d94023c" ], "identity_type": "Repository", "label": "railiance-cluster", @@ -1707,7 +1707,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:eacbd7a28e121081" + "evidence:root.registry.local-repos:754c6a30f9ff3671" ], "identity_type": "Repository", "label": "railiance-enablement", @@ -1727,8 +1727,8 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:f09103b02c4ae9fc", - "evidence:root.railiance-fabric.checkout:964745ce3b6dd491" + "evidence:root.registry.local-repos:deace4d5a96fc0d6", + "evidence:root.railiance-fabric.checkout:c9cb1d35bb99f2e0" ], "identity_type": "Repository", "label": "railiance-fabric", @@ -1741,38 +1741,15 @@ "stable_key": "identity:repository:railiance-fabric" }, { - "blockers": [ - "ambiguous_identity" - ], + "blockers": [], "containment": { "fabric_id": "fabric.railiance.primary", "status": "resolved", "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:f54177132294798c" - ], - "identity_type": "Repository", - "label": "railiance-hosts", - "ownership": { - "owner_actor_id": "actor.railiance.primary-lord", - "owner_role": "lord", - "resolution": "explicit" - }, - "review_state": "needs_review", - "stable_key": "identity:repository:railiance-hosts" - }, - { - "blockers": [ - "ambiguous_identity" - ], - "containment": { - "fabric_id": "fabric.railiance.primary", - "status": "resolved", - "subfabric_id": "" - }, - "evidence_ids": [ - "evidence:root.registry.local-repos:c960c3a821576795" + "evidence:root.registry.local-repos:fbff10063021cf4b", + "evidence:root.registry.local-repos:495887a2047c9f53" ], "identity_type": "Repository", "label": "railiance-infra", @@ -1781,7 +1758,7 @@ "owner_role": "lord", "resolution": "explicit" }, - "review_state": "needs_review", + "review_state": "candidate", "stable_key": "identity:repository:railiance-infra" }, { @@ -1792,7 +1769,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:a12e96e6d9418b3d" + "evidence:root.registry.local-repos:d8765e0f7c1e10f8" ], "identity_type": "Repository", "label": "railiance-platform", @@ -1812,7 +1789,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:6addadec236dd62f" + "evidence:root.registry.local-repos:c12e989814b1c70c" ], "identity_type": "Repository", "label": "repo-scoping", @@ -1832,7 +1809,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:720808c98666e541" + "evidence:root.registry.local-repos:89431ff6a8ef2996" ], "identity_type": "Repository", "label": "state-hub", @@ -1852,7 +1829,7 @@ "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:100a6d70bb2f298a" + "evidence:root.registry.local-repos:8feba7a45d2d751d" ], "identity_type": "Repository", "label": "the-custodian", @@ -1865,16 +1842,15 @@ "stable_key": "identity:repository:the-custodian" }, { - "blockers": [ - "ambiguous_identity" - ], + "blockers": [], "containment": { "fabric_id": "fabric.railiance.primary", "status": "resolved", "subfabric_id": "" }, "evidence_ids": [ - "evidence:root.registry.local-repos:e3fae7fb0c8a6867" + "evidence:root.registry.local-repos:a0331bbcdaf39a23", + "evidence:root.registry.local-repos:ff7fa8110f6c7097" ], "identity_type": "Repository", "label": "vergabe-teilnahme", @@ -1883,31 +1859,9 @@ "owner_role": "lord", "resolution": "explicit" }, - "review_state": "needs_review", + "review_state": "candidate", "stable_key": "identity:repository:vergabe-teilnahme" }, - { - "blockers": [ - "ambiguous_identity" - ], - "containment": { - "fabric_id": "fabric.railiance.primary", - "status": "resolved", - "subfabric_id": "" - }, - "evidence_ids": [ - "evidence:root.registry.local-repos:0703300ec1742fe1" - ], - "identity_type": "Repository", - "label": "vergabe_teilnahme", - "ownership": { - "owner_actor_id": "actor.railiance.primary-lord", - "owner_role": "lord", - "resolution": "explicit" - }, - "review_state": "needs_review", - "stable_key": "identity:repository:vergabe_teilnahme" - }, { "blockers": [], "containment": { @@ -1933,8 +1887,8 @@ "summary": { "accepted": 0, "ambiguous_containment": 0, - "needs_review": 4, - "total": 96, + "needs_review": 0, + "total": 94, "unresolved_ownership": 0 } } diff --git a/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-update-delta.json b/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-update-delta.json index 049c17c..1c9e065 100644 --- a/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-update-delta.json +++ b/fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-update-delta.json @@ -7,7 +7,7 @@ "review_state": [] }, "current": { - "generated_at": "2026-05-24T08:26:01Z", + "generated_at": "2026-05-24T12:04:20Z", "manifest_fingerprint": "dd279b655d68222a99671c1c875afdaa70ae2f907fd526e6673ddf756bc90dae", "manifest_id": "railiance.accountability-roots" }, @@ -19,7 +19,7 @@ "removed": [], "unchanged": [] }, - "generated_at": "2026-05-24T08:26:31Z", + "generated_at": "2026-05-24T12:04:59Z", "kind": "AccountabilityUpdateDelta", "node_delta": { "added": [ @@ -110,14 +110,12 @@ "identity:repository:railiance-cluster", "identity:repository:railiance-enablement", "identity:repository:railiance-fabric", - "identity:repository:railiance-hosts", "identity:repository:railiance-infra", "identity:repository:railiance-platform", "identity:repository:repo-scoping", "identity:repository:state-hub", "identity:repository:the-custodian", "identity:repository:vergabe-teilnahme", - "identity:repository:vergabe_teilnahme", "identity:secret-root:fabric-services-railiance-platform-openbao.yaml" ], "changed": [], @@ -130,8 +128,8 @@ "edges_changed": 0, "edges_removed": 0, "edges_unchanged": 0, - "meaningful_change_count": 97, - "nodes_added": 96, + "meaningful_change_count": 95, + "nodes_added": 94, "nodes_changed": 0, "nodes_removed": 0, "nodes_unchanged": 0, diff --git a/railiance_fabric/accountability_roots.py b/railiance_fabric/accountability_roots.py index 219f3f7..4a7a9b6 100644 --- a/railiance_fabric/accountability_roots.py +++ b/railiance_fabric/accountability_roots.py @@ -611,17 +611,28 @@ def _identity_from_evidence(root: dict[str, Any], item: dict[str, Any]) -> dict[ owner_actor_id = str(root.get("owner_actor_id") or "") if evidence_type in {"registered_repository", "repository_checkout"}: - label = str(source.get("repo_slug") or attributes.get("repo_slug") or Path(str(source.get("path") or "")).name) + declared_slug = str(source.get("repo_slug") or attributes.get("repo_slug") or "") + identity_slug = str( + source.get("identity_slug") + or attributes.get("canonical_slug") + or attributes.get("canonical_repo_slug") + or declared_slug + or Path(str(source.get("path") or "")).name + ) return { "identity_type": "Repository", - "label": label, - "graph_id": label, + "label": identity_slug, + "graph_id": identity_slug, "fabric_id": fabric_id, "subfabric_id": subfabric_id, "owner_actor_id": owner_actor_id, "evidence_ids": evidence_ids, - "aliases": [label, str(source.get("path") or ""), str(source.get("remote_url") or "")], - "attributes": {**attributes, "source_evidence_type": evidence_type}, + "aliases": [identity_slug, declared_slug, str(source.get("path") or ""), str(source.get("remote_url") or "")], + "attributes": { + **attributes, + "declared_repo_slug": declared_slug, + "source_evidence_type": evidence_type, + }, "confidence": 0.9 if evidence_type == "repository_checkout" else 0.85, } if evidence_type in {"deployment_automation", "infrastructure_manifest"}: @@ -1029,13 +1040,17 @@ def _registry_manifest_evidence(root: dict[str, Any], *, max_items: int) -> list evidence: list[dict[str, Any]] = [ _file_evidence(root, manifest_path, "registry_manifest", summary=f"Registry manifest with {len(repositories)} repositories.") ] + canonical_slug_by_index = _registry_canonical_slugs_by_index(repositories) for index, repo in enumerate(repositories[:max_items]): if not isinstance(repo, dict): continue + repo_slug = str(repo.get("slug") or "") + identity_slug = canonical_slug_by_index.get(index) or _repository_identity_slug(repo) or repo_slug repo_source = { "manifest_path": _display_path(manifest_path), "json_pointer": f"/repositories/{index}", - "repo_slug": repo.get("slug", ""), + "repo_slug": repo_slug, + "identity_slug": identity_slug, "path": repo.get("path", ""), "remote_url": repo.get("remote_url", ""), } @@ -1046,6 +1061,8 @@ def _registry_manifest_evidence(root: dict[str, Any], *, max_items: int) -> list "state_hub_repo_id": repo.get("state_hub_repo_id", ""), "has_local_path": bool(repo.get("path")), "has_remote_url": bool(repo.get("remote_url")), + "canonical_slug": identity_slug if identity_slug != repo_slug else "", + "identity_resolution": "duplicate_path_alias" if identity_slug != repo_slug else "", } evidence.append( _evidence_item( @@ -1062,6 +1079,69 @@ def _registry_manifest_evidence(root: dict[str, Any], *, max_items: int) -> list return evidence +def _registry_canonical_slugs_by_index(repositories: list[object]) -> dict[int, str]: + explicit: dict[int, str] = {} + path_groups: dict[str, list[tuple[int, dict[str, Any]]]] = {} + for index, repo in enumerate(repositories): + if not isinstance(repo, dict): + continue + explicit_slug = _repository_identity_slug(repo) + if explicit_slug: + explicit[index] = explicit_slug + if _boolish(repo.get("split_identity")): + continue + path = str(repo.get("path") or "").strip() + slug = str(repo.get("slug") or "").strip() + if path and slug: + path_groups.setdefault(path, []).append((index, repo)) + + canonical_by_index = dict(explicit) + for path, group in path_groups.items(): + if len(group) < 2: + continue + explicit_group_slugs = [ + explicit_slug + for index, _repo in group + if (explicit_slug := explicit.get(index)) + ] + canonical_slug = explicit_group_slugs[0] if explicit_group_slugs else _select_canonical_repo_slug(path, group) + for index, _repo in group: + canonical_by_index.setdefault(index, canonical_slug) + return canonical_by_index + + +def _repository_identity_slug(repo: dict[str, Any]) -> str: + return str( + repo.get("canonical_slug") + or repo.get("canonical_repo_slug") + or repo.get("identity_slug") + or repo.get("identity_repo_slug") + or "" + ).strip() + + +def _select_canonical_repo_slug(path: str, group: list[tuple[int, dict[str, Any]]]) -> str: + path_name = normalize_identity_part(Path(path).name) + + def sort_key(item: tuple[int, dict[str, Any]]) -> tuple[int, int, str]: + _index, repo = item + slug = str(repo.get("slug") or "") + return ( + 0 if normalize_identity_part(slug) == path_name else 1, + 0 if repo.get("remote_url") else 1, + normalize_identity_part(slug), + ) + + _index, canonical = sorted(group, key=sort_key)[0] + return str(canonical.get("slug") or "").strip() + + +def _boolish(value: object) -> bool: + if isinstance(value, bool): + return value + return str(value or "").strip().lower() in {"1", "true", "yes", "on"} + + def _repository_checkout_evidence(root: dict[str, Any]) -> list[dict[str, Any]]: source = _source(root) checkout = _resolve_path(source.get("path")) diff --git a/registry/local-repos.yaml b/registry/local-repos.yaml index 4733950..dcbb1c3 100644 --- a/registry/local-repos.yaml +++ b/registry/local-repos.yaml @@ -220,6 +220,7 @@ repositories: name: railiance-hosts domain: railiance path: /home/worsch/railiance-infra + canonical_slug: railiance-infra default_branch: main state_hub_repo_id: 2db668cf-6c52-4965-951f-0d5e617de9e3 @@ -265,5 +266,6 @@ repositories: name: vergabe-teilnahme domain: vergabe_teilnahme path: /home/worsch/vergabe-teilnahme + canonical_slug: vergabe-teilnahme default_branch: main state_hub_repo_id: 46d9338e-d079-4953-b174-0ea64761be5c diff --git a/tests/test_accountability_root_adapters.py b/tests/test_accountability_root_adapters.py index f91fe95..e071ecf 100644 --- a/tests/test_accountability_root_adapters.py +++ b/tests/test_accountability_root_adapters.py @@ -99,6 +99,59 @@ def test_deployable_identity_ignores_generic_filename_alias_ambiguity(tmp_path: assert all("ambiguous_aliases" not in candidate.get("attributes", {}) for candidate in deployables) +def test_repository_identity_canonicalizes_duplicate_manifest_paths(tmp_path: Path) -> None: + repo = tmp_path / "canonical-repo" + repo.mkdir() + registry_manifest = tmp_path / "local-repos.yaml" + registry_manifest.write_text( + """ +apiVersion: railiance.fabric/v1alpha1 +kind: RegistryOnboardingManifest +repositories: + - slug: stale-repo + name: stale-repo + path: {repo} + default_branch: main + - slug: canonical-repo + name: canonical-repo + path: {repo} + default_branch: main + remote_url: gitea-remote:coulomb/canonical-repo.git +""".format(repo=repo), + encoding="utf-8", + ) + manifest_path = _minimal_manifest( + tmp_path, + """ + - id: root.fixture.registry + type: registry_manifest + status: active + fabric_id: fabric.fixture.primary + owner_actor_id: actor.fixture.lord + source: + manifest_path: {registry_manifest} + safe_discovery: local_files + evidence_scope: + - repo_inventory + - repository_identity +""".format(registry_manifest=registry_manifest), + ) + manifest = load_accountability_root_manifest(manifest_path) + + projection = build_identity_projection(collect_accountability_root_evidence(manifest_path), manifest) + repository_candidates = [ + candidate + for candidate in projection["identity_candidates"] + if candidate["identity_type"] == "Repository" + ] + + assert [candidate["stable_key"] for candidate in repository_candidates] == [ + "identity:repository:canonical-repo" + ] + assert "stale-repo" in repository_candidates[0]["aliases"] + assert "ambiguous_aliases" not in repository_candidates[0].get("attributes", {}) + + def test_deployment_evidence_skips_dependency_cache_noise(tmp_path: Path) -> None: workspace = tmp_path / "workspace" (workspace / "service").mkdir(parents=True) diff --git a/workplans/RAIL-FAB-WP-0019-duplicate-repository-identity-review.md b/workplans/RAIL-FAB-WP-0019-duplicate-repository-identity-review.md index 103d3a7..fccd8a1 100644 --- a/workplans/RAIL-FAB-WP-0019-duplicate-repository-identity-review.md +++ b/workplans/RAIL-FAB-WP-0019-duplicate-repository-identity-review.md @@ -4,7 +4,7 @@ type: workplan title: "Duplicate Repository Identity Review" domain: railiance repo: railiance-fabric -status: ready +status: finished owner: codex topic_slug: railiance created: "2026-05-24" @@ -28,7 +28,7 @@ local checkout path. ```task id: RAIL-FAB-WP-0019-T01 -status: todo +status: done priority: high state_hub_task_id: "63c5ee1e-6c9f-4d63-b0b4-61308e833ac2" ``` @@ -43,11 +43,17 @@ Review the duplicate path evidence for: Done when the intended canonical repo identity, alias, or split-identity rule is known for `/home/worsch/railiance-infra` and `/home/worsch/vergabe-teilnahme`. +Result: `/home/worsch/railiance-infra` is canonicalized as +`railiance-infra`; `railiance-hosts` remains an alias while its State Hub record +still carries older goal context. `/home/worsch/vergabe-teilnahme` is +canonicalized as `vergabe-teilnahme`; `vergabe_teilnahme` is an alias/stale +registration. + ## T02 - Encode Canonical Identity Decisions ```task id: RAIL-FAB-WP-0019-T02 -status: todo +status: done priority: high state_hub_task_id: "bd0496f2-9a95-4487-9620-eabbf0e78c6b" ``` @@ -64,11 +70,16 @@ Candidate options include: Done when duplicate repo path evidence no longer produces ambiguous repository identity blockers. +Result: accountability-root discovery now supports `canonical_slug` and +automatic duplicate-path canonicalization for registered repositories. Current +aliases are annotated in `registry/local-repos.yaml`, and the live ownership +review reports no `ambiguous_identity` blockers. + ## T03 - Refresh Bootstrap Review Artifacts ```task id: RAIL-FAB-WP-0019-T03 -status: todo +status: done priority: medium state_hub_task_id: "2c90c5c1-46be-41b7-8854-602eea0e3aaf" ``` @@ -79,3 +90,7 @@ snapshot artifacts. Done when `fabric/discovery/snapshots/*bootstrap-ownership-review.json` shows zero ambiguous repository identity blockers, or documents any remaining blocker as intentionally unresolved. + +Result: bootstrap identity, ownership-review, and update-delta snapshots were +refreshed on 2026-05-24. The saved ownership review reports `needs_review: 0` +and no blockers.