content-explorer/workplans/evidence/CONTENT-WP-0004-ImplementationNotes.md
tegwick b5b6f6bc00 Add a bounded relation graph and knowledge journeys.
CONTENT-WP-0004 keeps outgoing and incoming labels apart, limits the graph to two hops, and stores focus and detail in the URL.

Assistant: grok
Assistant-Session: 01a1531b-6698-7732-8587-f28ea604bcdf
2026-10-19 10:00:20 +02:00

4.4 KiB
Raw Permalink Blame History

CONTENT-WP-0004 implementation notes

Date: 2026-10-19

Hop bound: 2 (HOP_BOUND in ui/lib/graph.ts). A requested distance above 2 is clamped. Neighborhood walks are undirected. Each edge keeps its stored from, to, predicate, and id.

Journey fields did not require a schema bump. Manifest and snapshot 0.1 still set additionalProperties: false at the document root, and entity properties is already an open object. Collections, related ids, reading paths, glossary text, and link previews live there. Today's fixtures still validate.

T01 — outgoing and incoming blocks

partitionIncident places every relation that touches an entity in exactly one block. Outgoing wording is the manifest forward label. Incoming wording is the inverse label. A self-loop is outgoing. EntityDetail renders the two blocks separately. When a caller lists only part of the incident set, relationRemainder is shown as “N more relations are not listed.”

Systems-thinking Feedback uses forward “Represented by” on represented-by. The incoming models edge uses “Modeled by”, which is not the forward string “Models”.

T02 and T05 — bounded graph

selectNeighborhood returns the focus plus nodes within the hop bound. Cycles stop because each node is visited once. Parallel edges stay as separate adjacency records (id, predicate, from, to, direction: "out"). Both parents of a node stay. A node with no edges remains in the graph model and can be focused. The route is /{dataspace}/graph?focus=<entity id>&detail=<entity id>&hops=1. Hop 2 omits hops. The page shows the excluded-relation count.

A declared properties.collection lists member links. properties.related lists related objects. properties.readingPath is rendered in that order. The crumb for the open entity is the current step. Each earlier crumb is a link to that step.

Feedback declares the collection “Ways to draw feedback”, a related link to Emergence, and the path System boundary → Feedback → Causal loop diagram.

T04 — glossary and previews

RecordLink shows a glossary tooltip only when the target has properties.glossary, and a preview only when properties.preview has both a title and a summary. Causal loop diagram has both. Stock and flow model has neither: its link still resolves, and the page does not render a tooltip or preview for it. System boundary has no journey properties and still renders.

T06 — nothing incident is silent

The Feedback page lists every incident relation id from the snapshot in exactly one block. The graph page prints the count of relations outside the neighborhood.

T07 — focus and detail in the URL

GraphExplorer reads and writes focus and detail through the same history helper the page uses in the browser. Reload passes the query back in as initialSearch. Back and forward restore the previous focus and detail. Covered in ui/components/GraphExplorer.history.test.tsx.

T08 — synthetic fixture

ui/fixtures/large-graph.json is a chain n0–n8 (8 edges) plus an isolated node solo. It is a test fixture, not a published dataspace, so validate_fixtures.py still checks only the two example dataspaces.

From n0 at hop bound 2, the neighborhood is n0, n1, and n2. n8 is outside. excludedCount is 6, equal to the edges that do not have both ends inside that neighborhood. Focusing solo returns that node and excludes all 8 edges.

Checks

Durable tests:

  • ui/lib/graph.test.ts
  • ui/components/EntityDetail.test.tsx
  • ui/components/GraphExplorer.history.test.tsx

Captured on 2026-10-19 under /tmp/grok-goal-aedf5fe6975e/implementer/:

  • ui-test-1.txt and ui-test-2.txt: npm test in ui/, 11 files, 32 tests, both exit 0.
  • typecheck.txt: npm run typecheck in ui/, exit 0.
  • fixtures.txt: python3 scripts/validate_fixtures.py, python3 -m unittest tests.test_contract tests.test_ingest, and python3 scripts/check_ui_boundary.py, exit 0.
  • launch-1.html and launch-2.html: HTTP 200 for /systems-thinking/graph?focus=urn:ce:systems-thinking:concept:feedback. Both bodies contain “Represented by” and “Modeled by”.
  • graph.png: headless Chromium loaded that URL with no page error. Focusing Causal loop diagram changed the neighborhood and the focus query. The screenshot is not blank.

No R6 deviation. The safeguard is the hop bound of 2, not a graph database or a draw-everything view.