From 5777c283f7891541b16bd1b16828748ec6559c31 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 19 May 2026 01:27:53 +0200 Subject: [PATCH] Document graph explorer adapter boundary --- docs/graph-explorer-operations.md | 66 ++++++++++++++++++- railiance_fabric/graph_explorer.py | 11 +++- schemas/graph-explorer-manifest.schema.yaml | 2 + schemas/graph-explorer-payload.schema.yaml | 2 + tests/test_graph_explorer.py | 7 +- ...AB-WP-0009-graph-explorer-ui-refinement.md | 2 +- 6 files changed, 82 insertions(+), 8 deletions(-) diff --git a/docs/graph-explorer-operations.md b/docs/graph-explorer-operations.md index ea9d4ee..3150fb6 100644 --- a/docs/graph-explorer-operations.md +++ b/docs/graph-explorer-operations.md @@ -31,6 +31,19 @@ views are stored in browser `localStorage`, URL query parameters carry normal filter state, and copied state URLs include a state blob so manual overrides can be shared without a profile service. +The refined rule panel is also browser-local. Rules target either nodes or +edges, optionally narrow by type and by attributes present on the currently +available elements, and then apply one of five modifiers: + +- `show`, `hide`, and `blur` are visibility states. +- `highlight` keeps an element visible and adds visual emphasis. +- `remove` hides matching elements and excludes them from the next layout run. + +Rules are applied top to bottom. Later matching rules refine earlier matching +rules. Manual overrides win after rules except for `remove`, which is treated as +stronger because it changes the layout membership. Edges connected to hidden +nodes are hidden; edges connected to removed nodes are removed. + ## Refresh Refresh this checkout into the running registry: @@ -69,8 +82,8 @@ The automated coverage for this slice lives in `tests/test_graph_explorer.py`: - registered-only repo projection - `railiance-fabric export --format graph-explorer` - registry API routes for manifest, payload, and UI -- static UI wiring for profile controls, orientation text, and shared-state - support +- static UI wiring for profile controls, rule controls, orientation text, and + shared-state support Manual smoke checks for a local registry: @@ -108,7 +121,8 @@ The extracted repository should own: - `GraphExplorerManifest` and `GraphExplorerPayload` schemas - static graph explorer assets and mount/bootstrap code -- display-state evaluation helpers for hosts that want client-side rules +- display-state and rule evaluation helpers for hosts that want client-side + rules, including `show`, `hide`, `blur`, `highlight`, and `remove` - browser-local profile handling, URL state, copied state blobs, and profile UI - Cytoscape rendering, layouts, hover popups, detail panels, focus modes, and manual override controls @@ -117,11 +131,57 @@ The extracted repository should own: Host repositories should keep: - graph projection and domain metadata enrichment +- host vocabulary and manifest labels for node types, edge types, rule fields, + modes, and orientation terms - host-side profile persistence, when shared/team profiles are required - authentication, authorization, and deployment - domain-specific modes and deep links - registry or analysis service APIs +## Adapter Readiness Notes + +The refined shell is closer to extraction, but these Fabric-specific assumptions +should be made manifest-driven or host-adapted before `graph-explorer-engine` +becomes a separate repo: + +- The shell still expects the internal element type field to be named `layer`. + User-facing text now says nodes and node types, but the shared contract should + either rename this to `nodeType` or declare the field through + `manifest.identity`. +- Node shapes are hardcoded against Fabric node type ids such as `repository`, + `service`, `deployment`, `server`, `capability`, `interface`, `dependency`, + `binding`, and `library`. +- Rule-builder attributes are derived from a fixed allowlist. Repo-scoping can + use the model, but the allowlist should move to manifest filter fields so a + host can expose facts, evidence, confidence, freshness, scope, ability, or + other domain attributes without changing engine code. +- Mode behavior is hardcoded for Fabric's `onboarding-gaps`, `unresolved`, + `selected-path`, and `neighborhood` semantics. The reusable engine should + either provide generic selectors or let hosts define mode predicates. +- The orientation panel still contains Fabric-specific renderers for + repositories, services, interfaces, dependencies, and capabilities. This + should stay host-owned or become a manifest-provided details adapter. +- The default detail rows know about `source`, `target`, `edgeType`, `strength`, + deep links, and source references. That is acceptable as a shared baseline, + but host-specific row ordering should be manifest-driven. + +The current rule-panel data model is compatible with repo-scoping if repo-scoping +maps its graph elements into the same basic shape: + +```json +{ + "target": "node", + "type": "fact", + "attribute": "reviewState", + "value": "candidate", + "action": "blur" +} +``` + +For extraction, prefer repo-scoping adapter parity as the next workplan. One +more Fabric-side polish pass is still useful for orientation workflows, but it +does not need to block proving the second host. + Suggested public API: ```ts diff --git a/railiance_fabric/graph_explorer.py b/railiance_fabric/graph_explorer.py index f3025b2..4a9bdf1 100644 --- a/railiance_fabric/graph_explorer.py +++ b/railiance_fabric/graph_explorer.py @@ -6,7 +6,7 @@ from typing import Any from urllib.parse import urlparse -DISPLAY_STATES = ("show", "blur", "hide") +DISPLAY_STATES = ("show", "blur", "hide", "highlight", "remove") LAYER_ORDER = ( "repository", "server", @@ -377,8 +377,13 @@ def fabric_graph_explorer_payload( "rules": [], "manual_overrides": {}, "orphaned_overrides": [], - "precedence": "later rules override earlier rules; manual overrides win last", - "connected_edge_behavior": "edges connected to hidden nodes are hidden", + "precedence": ( + "later rules override earlier rules; remove excludes matching elements " + "from layout and wins over manual overrides; otherwise manual overrides win last" + ), + "connected_edge_behavior": ( + "edges connected to hidden nodes are hidden; edges connected to removed nodes are removed" + ), }, "elements": elements, "hidden_elements": [], diff --git a/schemas/graph-explorer-manifest.schema.yaml b/schemas/graph-explorer-manifest.schema.yaml index 0feab19..19e3cbe 100644 --- a/schemas/graph-explorer-manifest.schema.yaml +++ b/schemas/graph-explorer-manifest.schema.yaml @@ -136,6 +136,8 @@ properties: - show - blur - hide + - highlight + - remove fields: type: array items: diff --git a/schemas/graph-explorer-payload.schema.yaml b/schemas/graph-explorer-payload.schema.yaml index 9f45a0d..e318412 100644 --- a/schemas/graph-explorer-payload.schema.yaml +++ b/schemas/graph-explorer-payload.schema.yaml @@ -208,6 +208,8 @@ $defs: - show - blur - hide + - highlight + - remove profile: type: object additionalProperties: true diff --git a/tests/test_graph_explorer.py b/tests/test_graph_explorer.py index d8a2067..39e1f9e 100644 --- a/tests/test_graph_explorer.py +++ b/tests/test_graph_explorer.py @@ -41,6 +41,7 @@ def test_graph_explorer_manifest_and_payload_validate() -> None: assert manifest["profile_persistence"] == "local" assert manifest["shareable_state"]["profile_id"] is True + assert set(manifest["filter"]["actions"]) >= {"show", "hide", "blur", "highlight", "remove"} assert {layer["id"] for layer in manifest["layers"]} >= {"server", "deployment"} filter_labels = {field["id"]: field["label"] for field in manifest["filter"]["fields"]} assert filter_labels["layer"] == "Node Type" @@ -68,6 +69,7 @@ def test_graph_explorer_manifest_and_payload_validate() -> None: assert payload["metrics"]["server_node_count"] >= 1 assert payload["metrics"]["registered_repo_count"] == 2 assert payload["metrics"]["unresolved_count"] == 0 + assert "removed nodes are removed" in payload["filter"]["connected_edge_behavior"] def test_cli_exports_graph_explorer_payload(capsys) -> None: @@ -101,7 +103,10 @@ def test_graph_explorer_payload_accepts_repo_scoping_shape() -> None: "manual_overrides": {"feature:1": "show"}, }, "filter": { - "rules": [], + "rules": [ + {"action": "highlight", "match": {"layer": "fact", "reviewState": "candidate"}}, + {"action": "remove", "match": {"layer": "stale_fact"}}, + ], "manual_overrides": {}, "orphaned_overrides": [], }, diff --git a/workplans/RAIL-FAB-WP-0009-graph-explorer-ui-refinement.md b/workplans/RAIL-FAB-WP-0009-graph-explorer-ui-refinement.md index eb4982d..75bd6f8 100644 --- a/workplans/RAIL-FAB-WP-0009-graph-explorer-ui-refinement.md +++ b/workplans/RAIL-FAB-WP-0009-graph-explorer-ui-refinement.md @@ -202,7 +202,7 @@ Acceptance notes: ```task id: RAIL-FAB-WP-0009-T06 -status: todo +status: done priority: medium state_hub_task_id: "934ea4d9-0d36-414d-9ed7-10f39410da8d" ```