--- id: BRIDGE-WP-0002 type: workplan title: "OpsCatalog Extension" domain: infotech repo: ops-bridge status: completed owner: Bernd topic_slug: custodian state_hub_workstream_id: f38bfcdb-f115-4431-88b5-ce906a24199c created: "2026-03-11" updated: "2026-03-12" --- # BRIDGE-WP-0002 — OpsCatalog Extension **Scope:** Implement OpsCatalog as a Git-backed YAML knowledge repository and integrate it with the `bridge` CLI. **Depends on:** BRIDGE-WP-0001 complete (bridge CLI operational). **Out of scope:** Identity provider integration (FR-27–29, deferred indefinitely). --- ## Goal Deliver the OpsCatalog subsystem: a structured YAML catalog of operations domains, targets, bridges, and actor classes stored in a Git repository. OpsBridge loads the catalog at runtime to resolve bridge identifiers, orient operators, and expose the `bridge targets` and `bridge catalog` commands. --- ## Reference Documents | Document | Location | |---|---| | OpsCatalog Spec (PRD + FRS + Schemas) | `wiki/OpsCatalogSpecification.md` | | OpsBridge FRS (deferred FRs) | `wiki/OpsBridgeFrs.md` §5.8, §5.10 | | CLAUDE.md | `CLAUDE.md` | --- ## Architecture Summary ``` ~/.config/bridge/tunnels.yaml catalog_path: ~/ops-catalog # path to the OpsCatalog Git repo ops-catalog/ # separate Git repo, consumed by bridge domains/ / domain.yaml # type: domain targets/ .yaml # type: target bridges/ .yaml # type: bridge docs/ *.md # operations notes actors/ .yaml # type: actor schemas/ domain.schema.yaml target.schema.yaml bridge.schema.yaml actor.schema.yaml src/bridge/ catalog/ __init__.py loader.py # walk catalog_path, parse YAML files into typed objects models.py # CatalogDomain, CatalogTarget, CatalogBridge, ActorClass validator.py # validate catalog entries against schemas resolver.py # resolve tunnel name → CatalogBridge → TunnelConfig ``` **Integration points with existing bridge code:** - `config.py`: read `catalog_path` from `tunnels.yaml`; pass to catalog loader - `manager.py`: use `resolver.py` to look up bridge config from catalog when tunnel is not defined inline in `tunnels.yaml` - `cli.py`: add `bridge targets` and `bridge catalog` commands --- ## YAML Schemas ### domain.yaml ```yaml type: domain id: coulombcore name: CoulombCore Infrastructure description: Core infrastructure domain for operational services environment: production ``` ### target.yaml ```yaml type: target id: state-hub domain: coulombcore kind: service description: Infrastructure state coordination service reachable_via: - state-hub-coulombcore ``` ### bridge.yaml ```yaml type: bridge id: state-hub-coulombcore domain: coulombcore target: state-hub description: Operations bridge for state hub diagnostics access_method: ssh-reverse host: coulombcore.local remote_port: 18000 local_port: 8000 ssh_user: ubuntu ssh_key: ~/.ssh/id_ops actor: agent.claude-coulombcore health_check: url: http://127.0.0.1:18000/health interval_seconds: 30 timeout_seconds: 5 reconnect: max_attempts: 0 backoff_initial: 5 backoff_max: 60 ``` ### actor.yaml ```yaml type: actor id: agent.claude-remediator class: automation description: Automated remediation agent ``` --- ## Phase 1 — Catalog Data Models **Acceptance:** All catalog YAML types parse into typed Python objects. ### T01 — Define catalog dataclasses in catalog/models.py ```task id: BRIDGE-WP-0002-T01 state_hub_task_id: 21b90574-a27c-467c-8e9d-d4029a659171 status: done priority: high ``` Define `CatalogDomain`, `CatalogTarget`, `CatalogBridge`, `ActorClass` dataclasses. `CatalogBridge` must be mergeable with `TunnelConfig` (catalog supplies defaults; inline `tunnels.yaml` entries can override). --- ## Phase 2 — Catalog Loader (FR-14) **Acceptance:** `catalog.load(path)` returns a populated `Catalog` object from a directory tree; unknown `type:` values are skipped with a warning. ### T02 — Implement catalog/loader.py ```task id: BRIDGE-WP-0002-T02 state_hub_task_id: 782b5b4d-1f3f-4e5d-ad46-dc57b345bda3 status: done priority: high ``` Walk `catalog_path` recursively, parse every `*.yaml` file, dispatch on `type:` field. Build in-memory index: domains, targets, bridges, actors. ### T03 — Unit tests for catalog loader ```task id: BRIDGE-WP-0002-T03 state_hub_task_id: 41fed4f8-7818-4ca1-bb48-6ac1089220e8 status: done priority: medium ``` Test: full catalog directory fixture loads correctly; missing required field raises clear error; unknown type is skipped; empty catalog returns empty index. --- ## Phase 3 — Catalog Validation (FR-15) **Acceptance:** `bridge catalog validate` exits non-zero and prints all violations when the catalog contains invalid entries. ### T04 — Implement catalog/validator.py ```task id: BRIDGE-WP-0002-T04 state_hub_task_id: 32946d15-5516-4599-8f27-8c653dec6786 status: done priority: medium ``` Validate required fields per type. Cross-reference checks: target's `domain` must exist; target's `reachable_via` bridge IDs must exist; bridge's `target` and `domain` must exist; actor referenced by bridge must exist. ### T05 — Unit tests for catalog validation ```task id: BRIDGE-WP-0002-T05 state_hub_task_id: 6061a6eb-9966-4be9-aa5e-ea7edf7fd085 status: done priority: medium ``` Test: valid catalog passes; dangling `reachable_via` reference fails; missing required field fails. --- ## Phase 4 — Bridge Resolver (FR-2 integration) **Acceptance:** `bridge up state-hub-coulombcore` resolves the bridge config from the catalog when no inline entry exists in `tunnels.yaml`. ### T06 — Implement catalog/resolver.py ```task id: BRIDGE-WP-0002-T06 state_hub_task_id: a92d97c8-4eec-4dd5-9b90-d9c1cba813ac status: done priority: high ``` `resolve(name, catalog, inline_config) → TunnelConfig`. Lookup order: inline `tunnels.yaml` entry wins; fall back to catalog bridge by ID. Merge catalog bridge fields into `TunnelConfig`. Raise `BridgeNotFound` if neither source has the name. ### T07 — Integrate resolver into config.py and manager.py ```task id: BRIDGE-WP-0002-T07 state_hub_task_id: 23799377-64f2-4c13-aa72-364770d80f91 status: done priority: high ``` Read `catalog_path` from `tunnels.yaml` (optional; catalog disabled if absent). Pass resolved `TunnelConfig` to `TunnelManager` unchanged — manager stays catalog-unaware. ### T08 — Unit tests for resolver ```task id: BRIDGE-WP-0002-T08 state_hub_task_id: d2313182-975f-409f-9d4f-ebabf66b44df status: done priority: medium ``` Test: inline entry takes precedence; catalog fallback works; inline overrides catalog fields; missing name raises `BridgeNotFound`. --- ## Phase 5 — CLI: bridge targets (FR-21, FR-22, FR-23) **Acceptance:** `bridge targets` prints a table of domains, targets, and which bridges provide access to each target. ### T09 — CLI: bridge targets command ```task id: BRIDGE-WP-0002-T09 state_hub_task_id: f9e508db-a19f-42be-9437-b4bdeb00a534 status: done priority: medium ``` Table columns: `DOMAIN`, `TARGET`, `KIND`, `BRIDGES`. `--domain ` filter. `--json` flag for automation. Requires catalog to be configured; clear error if `catalog_path` not set. ### T10 — CLI: bridge targets show ```task id: BRIDGE-WP-0002-T10 state_hub_task_id: e288a1d3-d676-404a-a3eb-25dbb241502d status: done priority: low ``` Show full metadata for a single target: domain, kind, description, reachable_via bridges, and any operations notes from `docs/*.md` files in the domain directory. --- ## Phase 6 — CLI: bridge catalog commands **Acceptance:** Operators can inspect and validate the catalog from the CLI. ### T11 — CLI: bridge catalog list ```task id: BRIDGE-WP-0002-T11 state_hub_task_id: 73899b70-b0ac-4f48-b362-cc2455a66f41 status: done priority: medium ``` List all domains and a count of targets and bridges per domain. ### T12 — CLI: bridge catalog validate ```task id: BRIDGE-WP-0002-T12 state_hub_task_id: e091daa2-7c20-4169-b634-1fcc469513ea status: done priority: medium ``` Run `validator.py` and print all violations. Exit 0 if clean, 1 if violations found. Useful in CI pipelines for the catalog repo. ### T13 — CLI: bridge catalog show ```task id: BRIDGE-WP-0002-T13 state_hub_task_id: 9f5f4f30-bfe6-40fd-b178-2fbb396816ee status: done priority: low ``` Print full resolved bridge metadata including target and domain context. --- ## Phase 7 — Integration Tests **Acceptance:** `uv run pytest` passes cleanly with catalog fixtures. ### T14 — Integration test: catalog load and resolve ```task id: BRIDGE-WP-0002-T14 state_hub_task_id: 5ccb2b4b-7ea5-4c38-8246-d59b8f7d4419 status: done priority: medium ``` Fixture: minimal catalog directory with one domain, one target, one bridge. Test `bridge up ` resolves and starts tunnel. ### T15 — Integration test: bridge targets output ```task id: BRIDGE-WP-0002-T15 state_hub_task_id: 72c9f686-c474-46c4-a759-bfd47e2d4211 status: done priority: medium ``` Test `bridge targets` output matches catalog fixture. Test `--json` flag. ### T16 — Integration test: bridge catalog validate ```task id: BRIDGE-WP-0002-T16 state_hub_task_id: 83c0734e-0dc2-49ce-8b6a-a4d5e26ff33a status: done priority: medium ``` Test clean catalog exits 0; catalog with a dangling reference exits 1 with a clear message. --- ## FRS Traceability | FRS Requirement Group | Phase | |---|---| | FR-14 — Catalog retrieval | 2 | | FR-15 — Catalog validation | 3 | | FR-1 to FR-3 — Domain management | 2, 5 | | FR-4 to FR-6 — Target management | 2, 5 | | FR-7 to FR-9 — Bridge definition | 2, 4 | | FR-10 to FR-11 — Actor classification | 2 | | FR-12 to FR-13 — Operational annotations | 5 (docs/*.md) | | FR-21 to FR-23 — Infrastructure target discovery (OpsBridge FRS) | 5 | *FR-27–29 (identity integration) remain deferred — require external identity provider infrastructure.* --- ## Deferred - **FR-27–29** — Identity provider integration (privacyIDEA / SSH CA) — separate workplan when identity infrastructure is available. - **Operations notes search** — full-text search across `docs/*.md` files — nice to have, not required for MVP.