generated from coulomb/repo-seed
Document graph explorer adapter boundary
This commit is contained in:
@@ -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
|
filter state, and copied state URLs include a state blob so manual overrides can
|
||||||
be shared without a profile service.
|
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
|
||||||
|
|
||||||
Refresh this checkout into the running registry:
|
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
|
- registered-only repo projection
|
||||||
- `railiance-fabric export --format graph-explorer`
|
- `railiance-fabric export --format graph-explorer`
|
||||||
- registry API routes for manifest, payload, and UI
|
- registry API routes for manifest, payload, and UI
|
||||||
- static UI wiring for profile controls, orientation text, and shared-state
|
- static UI wiring for profile controls, rule controls, orientation text, and
|
||||||
support
|
shared-state support
|
||||||
|
|
||||||
Manual smoke checks for a local registry:
|
Manual smoke checks for a local registry:
|
||||||
|
|
||||||
@@ -108,7 +121,8 @@ The extracted repository should own:
|
|||||||
|
|
||||||
- `GraphExplorerManifest` and `GraphExplorerPayload` schemas
|
- `GraphExplorerManifest` and `GraphExplorerPayload` schemas
|
||||||
- static graph explorer assets and mount/bootstrap code
|
- 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
|
- browser-local profile handling, URL state, copied state blobs, and profile UI
|
||||||
- Cytoscape rendering, layouts, hover popups, detail panels, focus modes, and
|
- Cytoscape rendering, layouts, hover popups, detail panels, focus modes, and
|
||||||
manual override controls
|
manual override controls
|
||||||
@@ -117,11 +131,57 @@ The extracted repository should own:
|
|||||||
Host repositories should keep:
|
Host repositories should keep:
|
||||||
|
|
||||||
- graph projection and domain metadata enrichment
|
- 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
|
- host-side profile persistence, when shared/team profiles are required
|
||||||
- authentication, authorization, and deployment
|
- authentication, authorization, and deployment
|
||||||
- domain-specific modes and deep links
|
- domain-specific modes and deep links
|
||||||
- registry or analysis service APIs
|
- 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:
|
Suggested public API:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from typing import Any
|
|||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
|
||||||
DISPLAY_STATES = ("show", "blur", "hide")
|
DISPLAY_STATES = ("show", "blur", "hide", "highlight", "remove")
|
||||||
LAYER_ORDER = (
|
LAYER_ORDER = (
|
||||||
"repository",
|
"repository",
|
||||||
"server",
|
"server",
|
||||||
@@ -377,8 +377,13 @@ def fabric_graph_explorer_payload(
|
|||||||
"rules": [],
|
"rules": [],
|
||||||
"manual_overrides": {},
|
"manual_overrides": {},
|
||||||
"orphaned_overrides": [],
|
"orphaned_overrides": [],
|
||||||
"precedence": "later rules override earlier rules; manual overrides win last",
|
"precedence": (
|
||||||
"connected_edge_behavior": "edges connected to hidden nodes are hidden",
|
"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,
|
"elements": elements,
|
||||||
"hidden_elements": [],
|
"hidden_elements": [],
|
||||||
|
|||||||
@@ -136,6 +136,8 @@ properties:
|
|||||||
- show
|
- show
|
||||||
- blur
|
- blur
|
||||||
- hide
|
- hide
|
||||||
|
- highlight
|
||||||
|
- remove
|
||||||
fields:
|
fields:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
|||||||
@@ -208,6 +208,8 @@ $defs:
|
|||||||
- show
|
- show
|
||||||
- blur
|
- blur
|
||||||
- hide
|
- hide
|
||||||
|
- highlight
|
||||||
|
- remove
|
||||||
profile:
|
profile:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ def test_graph_explorer_manifest_and_payload_validate() -> None:
|
|||||||
|
|
||||||
assert manifest["profile_persistence"] == "local"
|
assert manifest["profile_persistence"] == "local"
|
||||||
assert manifest["shareable_state"]["profile_id"] is True
|
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"}
|
assert {layer["id"] for layer in manifest["layers"]} >= {"server", "deployment"}
|
||||||
filter_labels = {field["id"]: field["label"] for field in manifest["filter"]["fields"]}
|
filter_labels = {field["id"]: field["label"] for field in manifest["filter"]["fields"]}
|
||||||
assert filter_labels["layer"] == "Node Type"
|
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"]["server_node_count"] >= 1
|
||||||
assert payload["metrics"]["registered_repo_count"] == 2
|
assert payload["metrics"]["registered_repo_count"] == 2
|
||||||
assert payload["metrics"]["unresolved_count"] == 0
|
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:
|
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"},
|
"manual_overrides": {"feature:1": "show"},
|
||||||
},
|
},
|
||||||
"filter": {
|
"filter": {
|
||||||
"rules": [],
|
"rules": [
|
||||||
|
{"action": "highlight", "match": {"layer": "fact", "reviewState": "candidate"}},
|
||||||
|
{"action": "remove", "match": {"layer": "stale_fact"}},
|
||||||
|
],
|
||||||
"manual_overrides": {},
|
"manual_overrides": {},
|
||||||
"orphaned_overrides": [],
|
"orphaned_overrides": [],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ Acceptance notes:
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: RAIL-FAB-WP-0009-T06
|
id: RAIL-FAB-WP-0009-T06
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "934ea4d9-0d36-414d-9ed7-10f39410da8d"
|
state_hub_task_id: "934ea4d9-0d36-414d-9ed7-10f39410da8d"
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user