feat: bootstrap accountability-root fabric snapshot

This commit is contained in:
2026-05-24 10:41:29 +02:00
parent 7956415924
commit 735867392e
13 changed files with 9105 additions and 12 deletions

View File

@@ -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
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
roots should use `metadata_only` or `explicit_review`; adapters must never read
secret values or operational telemetry while building Fabric graph evidence.

View File

@@ -84,6 +84,28 @@ railiance-fabric discover-roots --delta \
--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:
- 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,
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
The next discovery/update-loop work should replace the baseline projection with

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,7 @@ discovery_roots:
type: state_hub_repo_inventory
status: active
fabric_id: fabric.railiance.primary
owner_actor_id: actor.railiance.king
owner_actor_id: actor.railiance.primary-lord
source:
base_url: http://127.0.0.1:8000
api_paths:
@@ -71,7 +71,7 @@ discovery_roots:
type: gitea_organization
status: active
fabric_id: fabric.railiance.primary
owner_actor_id: actor.railiance.king
owner_actor_id: actor.railiance.primary-lord
source:
url: ssh://git@92.205.130.254:30022/coulomb
organization: coulomb
@@ -88,7 +88,7 @@ discovery_roots:
type: registry_manifest
status: active
fabric_id: fabric.railiance.primary
owner_actor_id: actor.railiance.king
owner_actor_id: actor.railiance.primary-lord
source:
manifest_path: registry/local-repos.yaml
safe_discovery: local_files
@@ -105,7 +105,7 @@ discovery_roots:
type: host_path
status: active
fabric_id: fabric.railiance.primary
owner_actor_id: actor.railiance.king
owner_actor_id: actor.railiance.primary-lord
source:
path: /home/worsch
patterns:
@@ -146,7 +146,7 @@ discovery_roots:
type: deployment_automation
status: active
fabric_id: fabric.railiance.primary
owner_actor_id: actor.railiance.king
owner_actor_id: actor.railiance.primary-lord
source:
path: /home/worsch
patterns:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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
}
}

View File

@@ -634,7 +634,7 @@ def _identity_from_evidence(root: dict[str, Any], item: dict[str, Any]) -> dict[
"subfabric_id": subfabric_id,
"owner_actor_id": owner_actor_id,
"evidence_ids": evidence_ids,
"aliases": [path, Path(path).stem],
"aliases": [path],
"attributes": {**attributes, "source_evidence_type": evidence_type},
"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}")]
matches: list[Path] = []
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:
break
evidence = [
@@ -1117,6 +1121,26 @@ def _glob_root_evidence(root: dict[str, Any], evidence_type: str, *, max_items:
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]]:
source = _source(root)
if not include_remote:

View File

@@ -1,6 +1,7 @@
from __future__ import annotations
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
@@ -67,8 +68,7 @@ def financial_export_from_legacy(
],
"unresolved": [],
}
if legacy_graph.get("generated_at"):
graph["generated_at"] = legacy_graph["generated_at"]
graph["generated_at"] = legacy_graph.get("generated_at") or _utc_now()
materialized = materialize_financial_graph_export(graph)
errors = financial_graph_errors(materialized)
if errors:
@@ -135,3 +135,7 @@ def _has_value(value: Any) -> bool:
if isinstance(value, list):
return any(_has_value(item) for item in value)
return value not in (None, "")
def _utc_now() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")

View File

@@ -63,6 +63,85 @@ def test_identity_projection_is_stable_and_reviewable(tmp_path: Path) -> None:
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:
manifest_path = _fixture_manifest(tmp_path)
manifest = load_accountability_root_manifest(manifest_path)
@@ -381,3 +460,46 @@ refresh:
encoding="utf-8",
)
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

View File

@@ -329,6 +329,7 @@ def test_current_graph_projects_to_financial_baseline() -> None:
assert list(validator.iter_errors(financial_graph)) == []
assert financial_graph["netkingdom"]["id"] == "railiance.netkingdom"
assert financial_graph["fabrics"][0]["id"] == "fabric.railiance.primary"
assert financial_graph["generated_at"].endswith("Z")
assert financial_graph["unresolved"] == []
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"])

View File

@@ -4,11 +4,11 @@ type: workplan
title: "Accountability Root Discovery And Update Loop"
domain: railiance
repo: railiance-fabric
status: active
status: finished
owner: codex
topic_slug: railiance
created: "2026-05-23"
updated: "2026-05-23"
updated: "2026-05-24"
state_hub_workstream_id: "651185b5-83fe-4aef-b29d-617b2bc48c7a"
---
@@ -280,7 +280,7 @@ Result:
```task
id: RAIL-FAB-WP-0018-T06
status: todo
status: done
priority: high
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`;
- 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
- Fabric discovery starts from accountability roots and deployment automation.

View File

@@ -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.