generated from coulomb/repo-seed
feat: bootstrap accountability-root fabric snapshot
This commit is contained in:
@@ -70,6 +70,12 @@ fabric by adding a tenant actor, a `Subfabric`, and subfabric-scoped discovery
|
|||||||
roots. This does not change the root fabric criterion: the fabric boundary
|
roots. This does not change the root fabric criterion: the fabric boundary
|
||||||
still rests on financial and operational accountability.
|
still rests on financial and operational accountability.
|
||||||
|
|
||||||
|
`owner_actor_id` on a discovery root describes the default owner to attach to
|
||||||
|
identity candidates discovered through that root. For ordinary repositories,
|
||||||
|
deployment files, and host-path evidence this should be the lord who pays for
|
||||||
|
the fabric. King authority remains modeled on the netkingdom and on roots that
|
||||||
|
represent recovery, secret, backup, or termination authority.
|
||||||
|
|
||||||
Discovery roots should state `safe_discovery` explicitly. Secret and backup
|
Discovery roots should state `safe_discovery` explicitly. Secret and backup
|
||||||
roots should use `metadata_only` or `explicit_review`; adapters must never read
|
roots should use `metadata_only` or `explicit_review`; adapters must never read
|
||||||
secret values or operational telemetry while building Fabric graph evidence.
|
secret values or operational telemetry while building Fabric graph evidence.
|
||||||
|
|||||||
@@ -84,6 +84,28 @@ railiance-fabric discover-roots --delta \
|
|||||||
--previous-ownership-review previous-ownership.json
|
--previous-ownership-review previous-ownership.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The current bootstrap artifacts live at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-identities.json
|
||||||
|
fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-ownership-review.json
|
||||||
|
fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-update-delta.json
|
||||||
|
exports/state-hub/2026-05-24-railiance-financial-fabric-v1.json
|
||||||
|
```
|
||||||
|
|
||||||
|
To refresh the same artifact set:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
railiance-fabric discover-roots --include-remote --max-items-per-root 200 \
|
||||||
|
--identity-projection > fabric/discovery/snapshots/YYYY-MM-DD-railiance-bootstrap-identities.json
|
||||||
|
railiance-fabric discover-roots --include-remote --max-items-per-root 200 \
|
||||||
|
--ownership-review > fabric/discovery/snapshots/YYYY-MM-DD-railiance-bootstrap-ownership-review.json
|
||||||
|
railiance-fabric discover-roots --include-remote --max-items-per-root 200 \
|
||||||
|
--delta > fabric/discovery/snapshots/YYYY-MM-DD-railiance-bootstrap-update-delta.json
|
||||||
|
railiance-fabric export --format financial . \
|
||||||
|
> exports/state-hub/YYYY-MM-DD-railiance-financial-fabric-v1.json
|
||||||
|
```
|
||||||
|
|
||||||
The financial export must satisfy these invariants:
|
The financial export must satisfy these invariants:
|
||||||
|
|
||||||
- every accepted node has resolvable ownership;
|
- every accepted node has resolvable ownership;
|
||||||
@@ -144,6 +166,25 @@ Hub graph import. The importer must preserve netkingdom, actors, fabrics,
|
|||||||
containment, ownership, accounting attribution, cross-boundary utility context,
|
containment, ownership, accounting attribution, cross-boundary utility context,
|
||||||
and unresolved gaps.
|
and unresolved gaps.
|
||||||
|
|
||||||
|
To import a saved financial export into State Hub:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST \
|
||||||
|
"http://127.0.0.1:8000/fabric/graph-exports?source_repo_slug=railiance-fabric" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data-binary @exports/state-hub/2026-05-24-railiance-financial-fabric-v1.json
|
||||||
|
```
|
||||||
|
|
||||||
|
If the `/fabric/graph-exports` endpoints return `500` while ordinary State Hub
|
||||||
|
routes work, run the State Hub migrations and retry:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/state-hub
|
||||||
|
make migrate
|
||||||
|
# or, when uv is not on PATH:
|
||||||
|
.venv/bin/alembic upgrade head
|
||||||
|
```
|
||||||
|
|
||||||
## Discovery Work Handoff
|
## Discovery Work Handoff
|
||||||
|
|
||||||
The next discovery/update-loop work should replace the baseline projection with
|
The next discovery/update-loop work should replace the baseline projection with
|
||||||
|
|||||||
3572
exports/state-hub/2026-05-24-railiance-financial-fabric-v1.json
Normal file
3572
exports/state-hub/2026-05-24-railiance-financial-fabric-v1.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,7 @@ discovery_roots:
|
|||||||
type: state_hub_repo_inventory
|
type: state_hub_repo_inventory
|
||||||
status: active
|
status: active
|
||||||
fabric_id: fabric.railiance.primary
|
fabric_id: fabric.railiance.primary
|
||||||
owner_actor_id: actor.railiance.king
|
owner_actor_id: actor.railiance.primary-lord
|
||||||
source:
|
source:
|
||||||
base_url: http://127.0.0.1:8000
|
base_url: http://127.0.0.1:8000
|
||||||
api_paths:
|
api_paths:
|
||||||
@@ -71,7 +71,7 @@ discovery_roots:
|
|||||||
type: gitea_organization
|
type: gitea_organization
|
||||||
status: active
|
status: active
|
||||||
fabric_id: fabric.railiance.primary
|
fabric_id: fabric.railiance.primary
|
||||||
owner_actor_id: actor.railiance.king
|
owner_actor_id: actor.railiance.primary-lord
|
||||||
source:
|
source:
|
||||||
url: ssh://git@92.205.130.254:30022/coulomb
|
url: ssh://git@92.205.130.254:30022/coulomb
|
||||||
organization: coulomb
|
organization: coulomb
|
||||||
@@ -88,7 +88,7 @@ discovery_roots:
|
|||||||
type: registry_manifest
|
type: registry_manifest
|
||||||
status: active
|
status: active
|
||||||
fabric_id: fabric.railiance.primary
|
fabric_id: fabric.railiance.primary
|
||||||
owner_actor_id: actor.railiance.king
|
owner_actor_id: actor.railiance.primary-lord
|
||||||
source:
|
source:
|
||||||
manifest_path: registry/local-repos.yaml
|
manifest_path: registry/local-repos.yaml
|
||||||
safe_discovery: local_files
|
safe_discovery: local_files
|
||||||
@@ -105,7 +105,7 @@ discovery_roots:
|
|||||||
type: host_path
|
type: host_path
|
||||||
status: active
|
status: active
|
||||||
fabric_id: fabric.railiance.primary
|
fabric_id: fabric.railiance.primary
|
||||||
owner_actor_id: actor.railiance.king
|
owner_actor_id: actor.railiance.primary-lord
|
||||||
source:
|
source:
|
||||||
path: /home/worsch
|
path: /home/worsch
|
||||||
patterns:
|
patterns:
|
||||||
@@ -146,7 +146,7 @@ discovery_roots:
|
|||||||
type: deployment_automation
|
type: deployment_automation
|
||||||
status: active
|
status: active
|
||||||
fabric_id: fabric.railiance.primary
|
fabric_id: fabric.railiance.primary
|
||||||
owner_actor_id: actor.railiance.king
|
owner_actor_id: actor.railiance.primary-lord
|
||||||
source:
|
source:
|
||||||
path: /home/worsch
|
path: /home/worsch
|
||||||
patterns:
|
patterns:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,140 @@
|
|||||||
|
{
|
||||||
|
"apiVersion": "railiance.fabric/v1alpha2",
|
||||||
|
"change_sets": {
|
||||||
|
"blockers": [],
|
||||||
|
"containment": [],
|
||||||
|
"ownership": [],
|
||||||
|
"review_state": []
|
||||||
|
},
|
||||||
|
"current": {
|
||||||
|
"generated_at": "2026-05-24T08:26:01Z",
|
||||||
|
"manifest_fingerprint": "dd279b655d68222a99671c1c875afdaa70ae2f907fd526e6673ddf756bc90dae",
|
||||||
|
"manifest_id": "railiance.accountability-roots"
|
||||||
|
},
|
||||||
|
"edge_delta": {
|
||||||
|
"added": [
|
||||||
|
"candidate-edge:d448183c64bba5c0"
|
||||||
|
],
|
||||||
|
"changed": [],
|
||||||
|
"removed": [],
|
||||||
|
"unchanged": []
|
||||||
|
},
|
||||||
|
"generated_at": "2026-05-24T08:26:31Z",
|
||||||
|
"kind": "AccountabilityUpdateDelta",
|
||||||
|
"node_delta": {
|
||||||
|
"added": [
|
||||||
|
"identity:actor:actor.railiance.king",
|
||||||
|
"identity:actor:actor.railiance.primary-lord",
|
||||||
|
"identity:backup-recovery-root:docs-financial-fabric-operator-guide.md",
|
||||||
|
"identity:catalog-root:gitea_organization",
|
||||||
|
"identity:catalog-root:registry_manifest",
|
||||||
|
"identity:deployable:home-worsch-.config-systemd-user-custodian-sync.service",
|
||||||
|
"identity:deployable:home-worsch-activity-core-dockerfile",
|
||||||
|
"identity:deployable:home-worsch-go-src-crypto-internal-boring-dockerfile",
|
||||||
|
"identity:deployable:home-worsch-go-src-crypto-internal-nistec-fiat-dockerfile",
|
||||||
|
"identity:deployable:home-worsch-key-cape-dockerfile",
|
||||||
|
"identity:deployable:home-worsch-repo-scoping-var-checkouts-key-cape-a01db9828dd4-dockerfile",
|
||||||
|
"identity:deployable:home-worsch-state-hub-dockerfile",
|
||||||
|
"identity:deployable:home-worsch-the-custodian-infra-build-machines-haskell-files-build-agent.service",
|
||||||
|
"identity:deployable:home-worsch-vergabe-teilnahme-dockerfile",
|
||||||
|
"identity:fabric:fabric.railiance.primary",
|
||||||
|
"identity:host-path:fabric",
|
||||||
|
"identity:host-path:git",
|
||||||
|
"identity:host-path:home-worsch-.nvm-.git",
|
||||||
|
"identity:host-path:home-worsch-activity-core-.git",
|
||||||
|
"identity:host-path:home-worsch-artifact-store-.git",
|
||||||
|
"identity:host-path:home-worsch-can-you-assist-.git",
|
||||||
|
"identity:host-path:home-worsch-domain-tree-.git",
|
||||||
|
"identity:host-path:home-worsch-flex-auth-.git",
|
||||||
|
"identity:host-path:home-worsch-guide-board-.git",
|
||||||
|
"identity:host-path:home-worsch-helix-forge-.git",
|
||||||
|
"identity:host-path:home-worsch-ihp-railiance-probe-.git",
|
||||||
|
"identity:host-path:home-worsch-info-tech-canon-.git",
|
||||||
|
"identity:host-path:home-worsch-infospace-bench-.git",
|
||||||
|
"identity:host-path:home-worsch-inter-hub-.git",
|
||||||
|
"identity:host-path:home-worsch-issue-core-.git",
|
||||||
|
"identity:host-path:home-worsch-key-cape-.git",
|
||||||
|
"identity:host-path:home-worsch-kontextual-engine-.git",
|
||||||
|
"identity:host-path:home-worsch-llm-connect-.git",
|
||||||
|
"identity:host-path:home-worsch-markitect-filter-.git",
|
||||||
|
"identity:host-path:home-worsch-markitect-main-.git",
|
||||||
|
"identity:host-path:home-worsch-markitect-quarkdown-.git",
|
||||||
|
"identity:host-path:home-worsch-markitect-tool-.git",
|
||||||
|
"identity:host-path:home-worsch-net-kingdom-.git",
|
||||||
|
"identity:host-path:home-worsch-open-cmis-tck-.git",
|
||||||
|
"identity:host-path:home-worsch-open-reuse-.git",
|
||||||
|
"identity:host-path:home-worsch-ops-bridge-.git",
|
||||||
|
"identity:host-path:home-worsch-ops-warden-.git",
|
||||||
|
"identity:host-path:home-worsch-phase-memory-.git",
|
||||||
|
"identity:host-path:home-worsch-railiance-apps-.git",
|
||||||
|
"identity:host-path:home-worsch-railiance-cluster-.git",
|
||||||
|
"identity:host-path:home-worsch-railiance-enablement-.git",
|
||||||
|
"identity:host-path:home-worsch-railiance-infra-.git",
|
||||||
|
"identity:host-path:home-worsch-railiance-platform-.git",
|
||||||
|
"identity:host-path:home-worsch-repo-scoping-.git",
|
||||||
|
"identity:host-path:home-worsch-repo-seed-.git",
|
||||||
|
"identity:host-path:home-worsch-shard-wiki-.git",
|
||||||
|
"identity:host-path:home-worsch-state-hub-.git",
|
||||||
|
"identity:host-path:home-worsch-tegwick-control-.git",
|
||||||
|
"identity:host-path:home-worsch-the-custodian-.git",
|
||||||
|
"identity:host-path:home-worsch-user-engine-.git",
|
||||||
|
"identity:host-path:home-worsch-vantage-point-.git",
|
||||||
|
"identity:host-path:home-worsch-vergabe-teilnahme-.git",
|
||||||
|
"identity:host-path:home-worsch-whynot-control-.git",
|
||||||
|
"identity:host-path:home-worsch-whynot-design-.git",
|
||||||
|
"identity:netkingdom:railiance.netkingdom",
|
||||||
|
"identity:repository:activity-core",
|
||||||
|
"identity:repository:artifact-store",
|
||||||
|
"identity:repository:domain-tree",
|
||||||
|
"identity:repository:flex-auth",
|
||||||
|
"identity:repository:guide-board",
|
||||||
|
"identity:repository:helix-forge",
|
||||||
|
"identity:repository:ihp-railiance-probe",
|
||||||
|
"identity:repository:infospace-bench",
|
||||||
|
"identity:repository:inter-hub",
|
||||||
|
"identity:repository:issue-core",
|
||||||
|
"identity:repository:key-cape",
|
||||||
|
"identity:repository:kontextual-engine",
|
||||||
|
"identity:repository:llm-connect",
|
||||||
|
"identity:repository:markitect-filter",
|
||||||
|
"identity:repository:markitect-project",
|
||||||
|
"identity:repository:markitect-quarkdown",
|
||||||
|
"identity:repository:markitect-tool",
|
||||||
|
"identity:repository:net-kingdom",
|
||||||
|
"identity:repository:open-cmis-tck",
|
||||||
|
"identity:repository:open-reuse",
|
||||||
|
"identity:repository:ops-bridge",
|
||||||
|
"identity:repository:ops-warden",
|
||||||
|
"identity:repository:phase-memory",
|
||||||
|
"identity:repository:railiance-apps",
|
||||||
|
"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": [],
|
||||||
|
"removed": [],
|
||||||
|
"unchanged": []
|
||||||
|
},
|
||||||
|
"previous": {},
|
||||||
|
"summary": {
|
||||||
|
"edges_added": 1,
|
||||||
|
"edges_changed": 0,
|
||||||
|
"edges_removed": 0,
|
||||||
|
"edges_unchanged": 0,
|
||||||
|
"meaningful_change_count": 97,
|
||||||
|
"nodes_added": 96,
|
||||||
|
"nodes_changed": 0,
|
||||||
|
"nodes_removed": 0,
|
||||||
|
"nodes_unchanged": 0,
|
||||||
|
"promotion_needed": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -634,7 +634,7 @@ def _identity_from_evidence(root: dict[str, Any], item: dict[str, Any]) -> dict[
|
|||||||
"subfabric_id": subfabric_id,
|
"subfabric_id": subfabric_id,
|
||||||
"owner_actor_id": owner_actor_id,
|
"owner_actor_id": owner_actor_id,
|
||||||
"evidence_ids": evidence_ids,
|
"evidence_ids": evidence_ids,
|
||||||
"aliases": [path, Path(path).stem],
|
"aliases": [path],
|
||||||
"attributes": {**attributes, "source_evidence_type": evidence_type},
|
"attributes": {**attributes, "source_evidence_type": evidence_type},
|
||||||
"confidence": 0.75,
|
"confidence": 0.75,
|
||||||
}
|
}
|
||||||
@@ -1096,7 +1096,11 @@ def _glob_root_evidence(root: dict[str, Any], evidence_type: str, *, max_items:
|
|||||||
return [_declared_evidence(root, f"{evidence_type}_missing", "unavailable", f"Root path missing: {base}")]
|
return [_declared_evidence(root, f"{evidence_type}_missing", "unavailable", f"Root path missing: {base}")]
|
||||||
matches: list[Path] = []
|
matches: list[Path] = []
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
matches.extend(sorted(base.glob(str(pattern))))
|
matches.extend(
|
||||||
|
path
|
||||||
|
for path in sorted(base.glob(str(pattern)))
|
||||||
|
if not _is_noise_match(evidence_type, path)
|
||||||
|
)
|
||||||
if len(matches) >= max_items:
|
if len(matches) >= max_items:
|
||||||
break
|
break
|
||||||
evidence = [
|
evidence = [
|
||||||
@@ -1117,6 +1121,26 @@ def _glob_root_evidence(root: dict[str, Any], evidence_type: str, *, max_items:
|
|||||||
return evidence
|
return evidence
|
||||||
|
|
||||||
|
|
||||||
|
def _is_noise_match(evidence_type: str, path: Path) -> bool:
|
||||||
|
if evidence_type not in {"deployment_automation", "infrastructure_manifest", "service_config", "endpoint_contract"}:
|
||||||
|
return False
|
||||||
|
parts = path.parts
|
||||||
|
noisy_parts = {
|
||||||
|
".cache",
|
||||||
|
".mypy_cache",
|
||||||
|
".nvm",
|
||||||
|
".pytest_cache",
|
||||||
|
".tox",
|
||||||
|
".venv",
|
||||||
|
"__pycache__",
|
||||||
|
"node_modules",
|
||||||
|
"site-packages",
|
||||||
|
}
|
||||||
|
if any(part in noisy_parts for part in parts):
|
||||||
|
return True
|
||||||
|
return any(parts[index : index + 3] == ("go", "pkg", "mod") for index in range(max(len(parts) - 2, 0)))
|
||||||
|
|
||||||
|
|
||||||
def _state_hub_evidence(root: dict[str, Any], *, include_remote: bool) -> list[dict[str, Any]]:
|
def _state_hub_evidence(root: dict[str, Any], *, include_remote: bool) -> list[dict[str, Any]]:
|
||||||
source = _source(root)
|
source = _source(root)
|
||||||
if not include_remote:
|
if not include_remote:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -67,8 +68,7 @@ def financial_export_from_legacy(
|
|||||||
],
|
],
|
||||||
"unresolved": [],
|
"unresolved": [],
|
||||||
}
|
}
|
||||||
if legacy_graph.get("generated_at"):
|
graph["generated_at"] = legacy_graph.get("generated_at") or _utc_now()
|
||||||
graph["generated_at"] = legacy_graph["generated_at"]
|
|
||||||
materialized = materialize_financial_graph_export(graph)
|
materialized = materialize_financial_graph_export(graph)
|
||||||
errors = financial_graph_errors(materialized)
|
errors = financial_graph_errors(materialized)
|
||||||
if errors:
|
if errors:
|
||||||
@@ -135,3 +135,7 @@ def _has_value(value: Any) -> bool:
|
|||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
return any(_has_value(item) for item in value)
|
return any(_has_value(item) for item in value)
|
||||||
return value not in (None, "")
|
return value not in (None, "")
|
||||||
|
|
||||||
|
|
||||||
|
def _utc_now() -> str:
|
||||||
|
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
||||||
|
|||||||
@@ -63,6 +63,85 @@ def test_identity_projection_is_stable_and_reviewable(tmp_path: Path) -> None:
|
|||||||
assert first["candidate_graph"]["edges"]
|
assert first["candidate_graph"]["edges"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_deployable_identity_ignores_generic_filename_alias_ambiguity(tmp_path: Path) -> None:
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
for name in ("service-a", "service-b"):
|
||||||
|
service = workspace / name
|
||||||
|
service.mkdir(parents=True)
|
||||||
|
(service / "Dockerfile").write_text("FROM python:3.12-slim\n", encoding="utf-8")
|
||||||
|
manifest_path = _minimal_manifest(
|
||||||
|
tmp_path,
|
||||||
|
"""
|
||||||
|
- id: root.fixture.deployables
|
||||||
|
type: deployment_automation
|
||||||
|
status: active
|
||||||
|
fabric_id: fabric.fixture.primary
|
||||||
|
owner_actor_id: actor.fixture.lord
|
||||||
|
source:
|
||||||
|
path: {workspace}
|
||||||
|
patterns:
|
||||||
|
- "*/Dockerfile"
|
||||||
|
safe_discovery: local_files
|
||||||
|
evidence_scope:
|
||||||
|
- deployment_topology
|
||||||
|
""".format(workspace=workspace),
|
||||||
|
)
|
||||||
|
manifest = load_accountability_root_manifest(manifest_path)
|
||||||
|
|
||||||
|
projection = build_identity_projection(collect_accountability_root_evidence(manifest_path), manifest)
|
||||||
|
|
||||||
|
deployables = [
|
||||||
|
candidate
|
||||||
|
for candidate in projection["identity_candidates"]
|
||||||
|
if candidate["identity_type"] == "Deployable"
|
||||||
|
]
|
||||||
|
assert len(deployables) == 2
|
||||||
|
assert all("ambiguous_aliases" not in candidate.get("attributes", {}) for candidate in deployables)
|
||||||
|
|
||||||
|
|
||||||
|
def test_deployment_evidence_skips_dependency_cache_noise(tmp_path: Path) -> None:
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
(workspace / "service").mkdir(parents=True)
|
||||||
|
(workspace / "service" / "Dockerfile").write_text("FROM python:3.12-slim\n", encoding="utf-8")
|
||||||
|
(workspace / ".venv" / "lib" / "python3.12" / "site-packages" / "pkg").mkdir(parents=True)
|
||||||
|
(workspace / ".venv" / "lib" / "python3.12" / "site-packages" / "pkg" / "Dockerfile").write_text(
|
||||||
|
"FROM ignored\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
(workspace / "go" / "pkg" / "mod" / "example").mkdir(parents=True)
|
||||||
|
(workspace / "go" / "pkg" / "mod" / "example" / "Dockerfile").write_text(
|
||||||
|
"FROM ignored\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
manifest_path = _minimal_manifest(
|
||||||
|
tmp_path,
|
||||||
|
"""
|
||||||
|
- id: root.fixture.deployables
|
||||||
|
type: deployment_automation
|
||||||
|
status: active
|
||||||
|
fabric_id: fabric.fixture.primary
|
||||||
|
owner_actor_id: actor.fixture.lord
|
||||||
|
source:
|
||||||
|
path: {workspace}
|
||||||
|
patterns:
|
||||||
|
- "**/Dockerfile"
|
||||||
|
safe_discovery: local_files
|
||||||
|
evidence_scope:
|
||||||
|
- deployment_topology
|
||||||
|
""".format(workspace=workspace),
|
||||||
|
)
|
||||||
|
|
||||||
|
evidence = collect_accountability_root_evidence(manifest_path, max_items_per_root=20)
|
||||||
|
paths = [
|
||||||
|
item["source"]["path"]
|
||||||
|
for root in evidence["roots"]
|
||||||
|
for item in root["evidence"]
|
||||||
|
if item["evidence_type"] == "deployment_automation"
|
||||||
|
]
|
||||||
|
|
||||||
|
assert paths == [str(workspace / "service" / "Dockerfile")]
|
||||||
|
|
||||||
|
|
||||||
def test_evidence_store_persists_runs_items_and_identities(tmp_path: Path) -> None:
|
def test_evidence_store_persists_runs_items_and_identities(tmp_path: Path) -> None:
|
||||||
manifest_path = _fixture_manifest(tmp_path)
|
manifest_path = _fixture_manifest(tmp_path)
|
||||||
manifest = load_accountability_root_manifest(manifest_path)
|
manifest = load_accountability_root_manifest(manifest_path)
|
||||||
@@ -381,3 +460,46 @@ refresh:
|
|||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
return manifest
|
return manifest
|
||||||
|
|
||||||
|
|
||||||
|
def _minimal_manifest(tmp_path: Path, discovery_roots: str) -> Path:
|
||||||
|
manifest = tmp_path / "minimal-accountability-roots.yaml"
|
||||||
|
manifest.write_text(
|
||||||
|
"""
|
||||||
|
apiVersion: railiance.fabric/v1alpha2
|
||||||
|
kind: AccountabilityRootManifest
|
||||||
|
metadata:
|
||||||
|
id: fixture.minimal-accountability-roots
|
||||||
|
name: Fixture Minimal Accountability Roots
|
||||||
|
netkingdom:
|
||||||
|
id: fixture.netkingdom
|
||||||
|
name: Fixture Netkingdom
|
||||||
|
king_actor_id: actor.fixture.king
|
||||||
|
actors:
|
||||||
|
- id: actor.fixture.king
|
||||||
|
role: king
|
||||||
|
name: Fixture King
|
||||||
|
- id: actor.fixture.lord
|
||||||
|
role: lord
|
||||||
|
name: Fixture Lord
|
||||||
|
fabrics:
|
||||||
|
- id: fabric.fixture.primary
|
||||||
|
kind: Fabric
|
||||||
|
name: Fixture Primary Fabric
|
||||||
|
netkingdom_id: fixture.netkingdom
|
||||||
|
lord_actor_id: actor.fixture.lord
|
||||||
|
parent_fabric_id: null
|
||||||
|
status: active
|
||||||
|
boundary:
|
||||||
|
boundary_type: fabric
|
||||||
|
criterion: financial_and_operational_accountability
|
||||||
|
discovery_roots:
|
||||||
|
{discovery_roots}
|
||||||
|
refresh:
|
||||||
|
cadence: manual
|
||||||
|
triggers:
|
||||||
|
- operator_request
|
||||||
|
""".format(discovery_roots=discovery_roots.rstrip()),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
return manifest
|
||||||
|
|||||||
@@ -329,6 +329,7 @@ def test_current_graph_projects_to_financial_baseline() -> None:
|
|||||||
assert list(validator.iter_errors(financial_graph)) == []
|
assert list(validator.iter_errors(financial_graph)) == []
|
||||||
assert financial_graph["netkingdom"]["id"] == "railiance.netkingdom"
|
assert financial_graph["netkingdom"]["id"] == "railiance.netkingdom"
|
||||||
assert financial_graph["fabrics"][0]["id"] == "fabric.railiance.primary"
|
assert financial_graph["fabrics"][0]["id"] == "fabric.railiance.primary"
|
||||||
|
assert financial_graph["generated_at"].endswith("Z")
|
||||||
assert financial_graph["unresolved"] == []
|
assert financial_graph["unresolved"] == []
|
||||||
assert all(node["containment"]["fabric_id"] == "fabric.railiance.primary" for node in financial_graph["nodes"])
|
assert all(node["containment"]["fabric_id"] == "fabric.railiance.primary" for node in financial_graph["nodes"])
|
||||||
assert all(node["ownership"]["owner_actor_id"] == "actor.railiance.primary-lord" for node in financial_graph["nodes"])
|
assert all(node["ownership"]["owner_actor_id"] == "actor.railiance.primary-lord" for node in financial_graph["nodes"])
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ type: workplan
|
|||||||
title: "Accountability Root Discovery And Update Loop"
|
title: "Accountability Root Discovery And Update Loop"
|
||||||
domain: railiance
|
domain: railiance
|
||||||
repo: railiance-fabric
|
repo: railiance-fabric
|
||||||
status: active
|
status: finished
|
||||||
owner: codex
|
owner: codex
|
||||||
topic_slug: railiance
|
topic_slug: railiance
|
||||||
created: "2026-05-23"
|
created: "2026-05-23"
|
||||||
updated: "2026-05-23"
|
updated: "2026-05-24"
|
||||||
state_hub_workstream_id: "651185b5-83fe-4aef-b29d-617b2bc48c7a"
|
state_hub_workstream_id: "651185b5-83fe-4aef-b29d-617b2bc48c7a"
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ Result:
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: RAIL-FAB-WP-0018-T06
|
id: RAIL-FAB-WP-0018-T06
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "0d05ee40-0823-473f-9c87-0ed964e8900c"
|
state_hub_task_id: "0d05ee40-0823-473f-9c87-0ed964e8900c"
|
||||||
```
|
```
|
||||||
@@ -302,6 +302,33 @@ Done when:
|
|||||||
- State Hub can import the resulting export after `STATE-WP-0051`;
|
- State Hub can import the resulting export after `STATE-WP-0051`;
|
||||||
- operator docs explain how to rerun the rebuild and update loop.
|
- operator docs explain how to rerun the rebuild and update loop.
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
- Tightened deployable identity normalization so generic filenames such as
|
||||||
|
`Dockerfile` no longer create false ambiguous identity blockers.
|
||||||
|
- Filtered dependency-cache deployment matches from accountability-root
|
||||||
|
deployable/config evidence.
|
||||||
|
- Aligned repository inventory, Gitea, host-path, and deployment roots to use
|
||||||
|
`actor.railiance.primary-lord` as the default financial owner for discovered
|
||||||
|
candidates.
|
||||||
|
- Saved the 2026-05-24 bootstrap artifacts:
|
||||||
|
`fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-identities.json`,
|
||||||
|
`fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-ownership-review.json`,
|
||||||
|
`fabric/discovery/snapshots/2026-05-24-railiance-bootstrap-update-delta.json`,
|
||||||
|
and `exports/state-hub/2026-05-24-railiance-financial-fabric-v1.json`.
|
||||||
|
- The bootstrap ownership review produced 96 candidates, zero unresolved
|
||||||
|
ownership items, zero ambiguous containment items, and four explicit
|
||||||
|
duplicate repository identity blockers.
|
||||||
|
- Created `RAIL-FAB-WP-0019` to resolve those duplicate repository identities
|
||||||
|
instead of hiding them.
|
||||||
|
- Imported the financial Fabric export into State Hub after applying the
|
||||||
|
`STATE-WP-0051` migration; State Hub accepted the export as valid with 2
|
||||||
|
actors, 1 fabric, 49 nodes, 58 edges, and 0 unresolved items.
|
||||||
|
- Added `generated_at` stamping for financial bridge exports so saved snapshot
|
||||||
|
files carry export time.
|
||||||
|
- Verified with focused accountability/registry tests, artifact schema
|
||||||
|
validation, State Hub import/readback, and full `python3 -m pytest`.
|
||||||
|
|
||||||
## Acceptance
|
## Acceptance
|
||||||
|
|
||||||
- Fabric discovery starts from accountability roots and deployment automation.
|
- Fabric discovery starts from accountability roots and deployment automation.
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
id: RAIL-FAB-WP-0019
|
||||||
|
type: workplan
|
||||||
|
title: "Duplicate Repository Identity Review"
|
||||||
|
domain: railiance
|
||||||
|
repo: railiance-fabric
|
||||||
|
status: ready
|
||||||
|
owner: codex
|
||||||
|
topic_slug: railiance
|
||||||
|
created: "2026-05-24"
|
||||||
|
updated: "2026-05-24"
|
||||||
|
state_hub_workstream_id: "bc69549c-3cbc-4a7d-8766-b84added1133"
|
||||||
|
---
|
||||||
|
|
||||||
|
# RAIL-FAB-WP-0019 - Duplicate Repository Identity Review
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Resolve the duplicate repository identity blockers found during the
|
||||||
|
`RAIL-FAB-WP-0018` bootstrap run.
|
||||||
|
|
||||||
|
The 2026-05-24 accountability-root ownership review resolved ownership and
|
||||||
|
containment for all candidates, but flagged four repository identities as
|
||||||
|
ambiguous because registry inventory evidence maps two repo slugs onto the same
|
||||||
|
local checkout path.
|
||||||
|
|
||||||
|
## T01 - Inspect Duplicate Repo Path Evidence
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: RAIL-FAB-WP-0019-T01
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "63c5ee1e-6c9f-4d63-b0b4-61308e833ac2"
|
||||||
|
```
|
||||||
|
|
||||||
|
Review the duplicate path evidence for:
|
||||||
|
|
||||||
|
- `identity:repository:railiance-hosts`
|
||||||
|
- `identity:repository:railiance-infra`
|
||||||
|
- `identity:repository:vergabe-teilnahme`
|
||||||
|
- `identity:repository:vergabe_teilnahme`
|
||||||
|
|
||||||
|
Done when the intended canonical repo identity, alias, or split-identity rule is
|
||||||
|
known for `/home/worsch/railiance-infra` and `/home/worsch/vergabe-teilnahme`.
|
||||||
|
|
||||||
|
## T02 - Encode Canonical Identity Decisions
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: RAIL-FAB-WP-0019-T02
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "bd0496f2-9a95-4487-9620-eabbf0e78c6b"
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply the chosen resolution in the durable source of truth.
|
||||||
|
|
||||||
|
Candidate options include:
|
||||||
|
|
||||||
|
- fix duplicate entries in `registry/local-repos.yaml`;
|
||||||
|
- add explicit repository alias/canonicalization support to the identity
|
||||||
|
projection;
|
||||||
|
- persist review decisions for the affected stable identity keys.
|
||||||
|
|
||||||
|
Done when duplicate repo path evidence no longer produces ambiguous repository
|
||||||
|
identity blockers.
|
||||||
|
|
||||||
|
## T03 - Refresh Bootstrap Review Artifacts
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: RAIL-FAB-WP-0019-T03
|
||||||
|
status: todo
|
||||||
|
priority: medium
|
||||||
|
state_hub_task_id: "2c90c5c1-46be-41b7-8854-602eea0e3aaf"
|
||||||
|
```
|
||||||
|
|
||||||
|
Rerun the accountability-root bootstrap review and update the saved discovery
|
||||||
|
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.
|
||||||
Reference in New Issue
Block a user