Skip to content

webui: dependency-layered graph and legible instrument chrome - #428

Merged
George-RD merged 3 commits into
mainfrom
webui/relations-layout-overhaul
Jul 18, 2026
Merged

webui: dependency-layered graph and legible instrument chrome#428
George-RD merged 3 commits into
mainfrom
webui/relations-layout-overhaul

Conversation

@George-RD

Copy link
Copy Markdown
Collaborator

What

Second-round webui overhaul driven by a grounded screenshot audit plus an adversarial vision review, addressing: relations not readable from a flat grid, tiny blueprint pane, crowded chrome, invisible findings/backlog, page not filling the viewport.

Graph

  • Dependency-layered layout: entry surfaces left, foundations right, isolated modules in a band below; kernel ownership frame; System node becomes the canvas title instead of a peer card.
  • Directional curved edges with arrowheads; selection highlights in/out edges and dims non-neighbours at a readable floor; fit-to-stage scaling; no auto-selection on first visit.
  • Cards: leaf-segment mono label, two-line clamped description, state via dot + keel only.

Evidence rail

  • Blueprint tab scopes to the selected node's declaration block with an expand-to-full toggle and a full-height pane (was a 52px scroll trap holding the whole blueprint).
  • Facts tab tightened; lineage restyled to one row per artefact; rail collapses when nothing is selected.

Chrome

  • Shell fills wide viewports (--ui-shell-max-width: min(100%, 1720px)).
  • Query rail: one search input + labelled Kind/State segmented groups (was 11 unlabelled chips with two ALLs).
  • Status bezel deduplicated to one severity-coded annunciator; channel bar rebuilt as compact severity-sorted colour-coded rows with a collapse toggle; findings/backlog finally legible.
  • Restrained motion pass, fully gated behind prefers-reduced-motion.

Second commit

Serves GitHub Pages from docs/ root (drops the landing redirect), updates gates/tests/references, plus the README/landing copy pass.

Verification

  • Headless Playwright against the embedded binary at 1568x962, 1440x900, 390px: no console errors, no raw copy keys, zero horizontal overflow, keyboard nav works.
  • Gates: fmt, clippy -D warnings, full cargo test, biome 2.4.4, file-size, design-token, a11y, cairn scan --strict all green.
  • Pre-submit review: simplification lens (dead code/orphan copy keys removed) and adversarial lens (one latent blueprint-scoping precedence bug found and fixed) both run.

Known deferred: style.css/components.css still duplicate ~40 component selectors with minor drift (flagged by review; needs a single-authority restructure, separate change).

Replace the index-order node grid with a dependency-layered architecture
layout (surfaces left, foundations right, isolated modules in a band below,
kernel ownership frame, directional edges with arrowheads, fit-to-stage
scaling). Selection highlights in/out edges and dims non-neighbours at a
readable floor; no auto-selection on first visit.

Evidence rail: blueprint tab scopes to the selected node's declaration with
an expand toggle and full-height pane; Facts kv grid tightened; lineage
restyled to one row per artefact. Chrome: shell fills wide viewports,
single labelled Kind/State filter groups, deduplicated status bezel with a
compact drift annunciator, channel bar rebuilt as compact severity-sorted
rows with collapse toggle and colour-coded severity. Restrained motion
section gated behind prefers-reduced-motion. app.js split into app-data,
canvas-nav, and graph-layout modules to hold the 500-line gate.

Verified headlessly at 1568x962, 1440x900, and 390px against the embedded
binary: no console errors, no raw copy keys, no overflow.
Move the landing page from docs/landing/index.html to docs/index.html
(and _headers alongside) so Pages serves it directly instead of through
a redirect. Update the a11y and design-token gates, their tests, and doc
references to the new path.

Also a copy pass on README and the landing page: removed the stale
"See it" screenshot section (to be re-added with a capture of the new
Graph Explorer), replaced telemetry-negation phrasing with plain
local-first wording, and reworked flagged template-cadence sentences.
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@George-RD, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1b4fba45-593e-4bb3-8f24-3c8f7e6256f1

📥 Commits

Reviewing files that changed from the base of the PR and between 86080c2 and 1e2d527.

📒 Files selected for processing (8)
  • docs/design-system/README.md
  • docs/design-system/components.css
  • docs/design-system/index.html
  • docs/design-system/tokens.css
  • harness/eval.mjs
  • src/ui_assets/app.js
  • src/ui_assets/channel-bar.js
  • src/ui_assets/style.css
📝 Walkthrough

Walkthrough

This PR replaces the landing-page redirect with a full marketing page, updates the design system, and refactors the WebUI around shared data, graph-layout, navigation, evidence, channel, and status components.

Changes

Landing surface

Layer / File(s) Summary
Standalone landing page and validation targets
docs/index.html, README.md, scripts/*, tests/*
Adds the full landing page with interactive tweaks, theme and hero variants, scroll reveals, content sections, and updated accessibility, token, asset, and metadata checks.

Design-system refresh

Layer / File(s) Summary
Tokens, copy, showcase, and component styling
docs/design-system/*, harness/fixtures/assets/copy.json
Updates layout and severity tokens, localized labels, showcase markup, responsive evidence/channel layouts, graph styling, and reduced-motion behavior.

Frontend data and graph pipeline

Layer / File(s) Summary
Asset loading, data orchestration, graph layout, and navigation
src/ui/mod.rs, src/ui/server.rs, src/ui_assets/app*.js, src/ui_assets/canvas-nav.js, src/ui_assets/graph*.js, src/ui_assets/utils.js
Embeds and serves new modules, centralizes bootstrap and artefact loading, adds layered graph layout and spatial keyboard navigation, and changes selection and blueprint highlighting behavior.

Instrument interaction surfaces

Layer / File(s) Summary
Status, query, evidence, channel, node, and workspace UI
src/ui_assets/{channel-bar,evidence-rail,node-module,query-rail,status-bezel,style}.js
Adds severity-aware status and channel rendering, collapsible channels, segmented filters, evidence previews, scoped blueprint expansion, simplified node cards, and corresponding visual states and responsive styling.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant QueryRail
  participant app
  participant GraphWorkspace
  participant EvidenceRail
  User->>QueryRail: change query or filter
  QueryRail->>app: invoke filter or clear handler
  app->>GraphWorkspace: update visible nodes and selection
  User->>GraphWorkspace: select graph node
  GraphWorkspace->>app: invoke onSelect
  app->>EvidenceRail: load and render node artefacts
Loading

Possibly related PRs

Poem

A rabbit maps the page with care,
New graph paths hop through the air.
Filters bloom and channels fold,
Evidence stories now unfold.
Less drift, more hops, a cleaner view—
The carrot trail is clear and true! 🐇


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Docstring Coverage ❌ Error Docstring coverage is 60.87% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main WebUI graph and chrome overhaul described in the changeset.
Description check ✅ Passed The description is clearly related to the PR and matches the graph, evidence rail, chrome, and docs changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch webui/relations-layout-overhaul

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Tap targets: query segments and the channel toggle meet --tap-min on
narrow viewports. Mobile keeps the ratified bounded shell; the channel
bar defaults collapsed on compact and its tab strip wraps to a 2x2 grid
under 640px instead of scrolling offscreen. Match count now reflects
selectable cards (containers render as frames, not cards), fixing the
palette scenario's count invariant. Harness actions updated for the
redesigned lineage rows and match indicator; dead mobile stage token
removed. ux_defect_score=0 across all 11 scenarios locally.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/_headers`:
- Around line 1-3: Remove the Referrer-Policy and X-Content-Type-Options entries
from docs/_headers and configure both headers in the GitHub Pages hosting layer
instead, using the repository’s existing deployment configuration.

In `@docs/design-system/components.css`:
- Around line 369-371: The shared dependency-arrow marker uses currentColor,
preventing arrowheads from inheriting each dependency link’s direction-specific
color. Update the marker path styling in docs/design-system/components.css lines
369-371 and src/ui_assets/style.css lines 121-123 to use context-stroke with the
required Safari fallback, or implement synchronized direction-specific marker
copies in both files.

In `@docs/design-system/tokens.css`:
- Around line 144-146: Update the token definitions in the dark root and the
[data-theme="light"] override so --error uses a distinct blocking accent instead
of resolving through --drift to --ci-amber, while --warning remains amber.
Ensure --error and --warning resolve to different colours in both themes,
preserving the existing severity consumer mappings.

In `@docs/index.html`:
- Around line 813-821: Update the reduced-motion handling and animation toggle
logic around the hero autoplay video, reveal effects, variant animations, and
the state.anim switch so reduced motion pauses the video and suppresses all
animation, while state.anim === "off" produces the same behavior instead of only
setting an unused data-anim attribute. Ensure the video is paused and
animation/reveal styles are disabled for either condition, while normal
animation remains unchanged otherwise.
- Around line 19-21: Remove the page-local design-system declarations from the
styles in docs/index.html, including typography, spacing, radii, motion,
buttons, cards, panels, and hardcoded shadows. Link and consume the canonical
docs/design-system/components.css and tokens.css instead, moving any genuinely
reusable definitions into those files. Update the design-system reference and
README to document the canonical additions, while preserving the page’s existing
visual behavior through shared tokens and components.
- Around line 931-969: Update the provenance and authority diagrams to preserve
the canonical Source → Research → Decision and Decision → Blueprint → Contract →
Code flows. Remove the “synthesis” node and reroute the research connection
directly to the Decision hinge, and replace any “layer” labels with the
established pluggable-domain/reconciler terminology in both diagram occurrences.
Do not introduce a flattened architectural stack.
- Around line 115-166: Update the responsive styles for nav.site so narrow
screens provide an accessible mobile layout instead of relying on a single
non-wrapping row clipped by body overflow. Adjust the navigation and its child
selectors, including .brand, .links, .version, and .cta, to fit or scroll within
the viewport while preserving access to all links, the CTA, and version
indicator.

In `@README.md`:
- Line 31: Update the exit-cleanup promise in the README paragraph to include
removing the pasted Cairn section from the agent instructions, alongside
deleting the listed repository files. Preserve the existing local-first and
cleanup guidance while ensuring all setup changes are covered.

In `@src/ui_assets/canvas-nav.js`:
- Around line 84-92: Update the source and candidate selection logic around the
current selection lookup so the first node is only used as a navigation target
when there is no active selection, not treated as the already-selected source.
Preserve the existing behavior for an active currentSelectionId while allowing
arrow-key navigation to include the first node and select it in both single-node
and larger graphs.

In `@src/ui_assets/evidence-rail.js`:
- Around line 125-144: Update LineagePlate so artefacts.sources remains a
distinct Sources/provenance section rather than being assigned to
lineage-authority or labeled as authority. Preserve the separate “Source →
Research → Decision” provenance chain and “Decision → Blueprint → Contract →
Code” authority chain; if authority data is needed, model and render it
independently instead of reusing authority.

In `@src/ui_assets/status-bezel.js`:
- Around line 32-44: Update DriftIndicator so the status summary is shown
whenever any finding count exists, including info-only findings, rather than
gating it solely on drift. Preserve the existing severity tone and clean-state
behavior for zero counts, and use the existing counts/parts values.

In `@src/ui_assets/style.css`:
- Around line 130-135: Update the min-height declaration in
.graph-stage.is-empty to use an existing defined spacing token from --s-1
through --s-12, or an appropriate fixed value, instead of the undefined --s-20.

In `@src/ui_assets/utils.js`:
- Around line 75-79: Update the copy() API in the surrounding utility to accept
a caller-provided fallback value, and return that fallback when copy loading has
not completed instead of always returning an empty string. Update the
runtime-not-ready error path in app.js to pass a non-empty fallback so critical
pre-load errors remain visible, while preserving existing behavior for loaded
copy and missing-key cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f93c88b-9bf8-44e2-8c4e-ecadeac3392d

📥 Commits

Reviewing files that changed from the base of the PR and between ef506ee and 86080c2.

📒 Files selected for processing (33)
  • AGENTS.md
  • README.md
  • docs/_headers
  • docs/design-system/README.md
  • docs/design-system/components.css
  • docs/design-system/copy.toml
  • docs/design-system/index.html
  • docs/design-system/tokens.css
  • docs/index.html
  • docs/landing/index.html
  • harness/fixtures/assets/copy.json
  • map.json
  • scripts/check-a11y.sh
  • scripts/check-design-tokens.sh
  • src/ui/mod.rs
  • src/ui/server.rs
  • src/ui_assets/app-data.js
  • src/ui_assets/app.js
  • src/ui_assets/canvas-nav.js
  • src/ui_assets/channel-bar.js
  • src/ui_assets/evidence-rail.js
  • src/ui_assets/graph-layout.js
  • src/ui_assets/graph-workspace.js
  • src/ui_assets/node-module.js
  • src/ui_assets/query-rail.js
  • src/ui_assets/status-bezel.js
  • src/ui_assets/style.css
  • src/ui_assets/utils.js
  • tests/check_a11y.rs
  • tests/check_design_tokens.rs
  • tests/graph_explorer.rs
  • tests/landing_assets.rs
  • tests/phase_7_7_ux_foundation.rs
💤 Files with no reviewable changes (1)
  • docs/landing/index.html

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 13

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/_headers`:
- Around line 1-3: Remove the Referrer-Policy and X-Content-Type-Options entries
from docs/_headers and configure both headers in the GitHub Pages hosting layer
instead, using the repository’s existing deployment configuration.

In `@docs/design-system/components.css`:
- Around line 369-371: The shared dependency-arrow marker uses currentColor,
preventing arrowheads from inheriting each dependency link’s direction-specific
color. Update the marker path styling in docs/design-system/components.css lines
369-371 and src/ui_assets/style.css lines 121-123 to use context-stroke with the
required Safari fallback, or implement synchronized direction-specific marker
copies in both files.

In `@docs/design-system/tokens.css`:
- Around line 144-146: Update the token definitions in the dark root and the
[data-theme="light"] override so --error uses a distinct blocking accent instead
of resolving through --drift to --ci-amber, while --warning remains amber.
Ensure --error and --warning resolve to different colours in both themes,
preserving the existing severity consumer mappings.

In `@docs/index.html`:
- Around line 813-821: Update the reduced-motion handling and animation toggle
logic around the hero autoplay video, reveal effects, variant animations, and
the state.anim switch so reduced motion pauses the video and suppresses all
animation, while state.anim === "off" produces the same behavior instead of only
setting an unused data-anim attribute. Ensure the video is paused and
animation/reveal styles are disabled for either condition, while normal
animation remains unchanged otherwise.
- Around line 19-21: Remove the page-local design-system declarations from the
styles in docs/index.html, including typography, spacing, radii, motion,
buttons, cards, panels, and hardcoded shadows. Link and consume the canonical
docs/design-system/components.css and tokens.css instead, moving any genuinely
reusable definitions into those files. Update the design-system reference and
README to document the canonical additions, while preserving the page’s existing
visual behavior through shared tokens and components.
- Around line 931-969: Update the provenance and authority diagrams to preserve
the canonical Source → Research → Decision and Decision → Blueprint → Contract →
Code flows. Remove the “synthesis” node and reroute the research connection
directly to the Decision hinge, and replace any “layer” labels with the
established pluggable-domain/reconciler terminology in both diagram occurrences.
Do not introduce a flattened architectural stack.
- Around line 115-166: Update the responsive styles for nav.site so narrow
screens provide an accessible mobile layout instead of relying on a single
non-wrapping row clipped by body overflow. Adjust the navigation and its child
selectors, including .brand, .links, .version, and .cta, to fit or scroll within
the viewport while preserving access to all links, the CTA, and version
indicator.

In `@README.md`:
- Line 31: Update the exit-cleanup promise in the README paragraph to include
removing the pasted Cairn section from the agent instructions, alongside
deleting the listed repository files. Preserve the existing local-first and
cleanup guidance while ensuring all setup changes are covered.

In `@src/ui_assets/canvas-nav.js`:
- Around line 84-92: Update the source and candidate selection logic around the
current selection lookup so the first node is only used as a navigation target
when there is no active selection, not treated as the already-selected source.
Preserve the existing behavior for an active currentSelectionId while allowing
arrow-key navigation to include the first node and select it in both single-node
and larger graphs.

In `@src/ui_assets/evidence-rail.js`:
- Around line 125-144: Update LineagePlate so artefacts.sources remains a
distinct Sources/provenance section rather than being assigned to
lineage-authority or labeled as authority. Preserve the separate “Source →
Research → Decision” provenance chain and “Decision → Blueprint → Contract →
Code” authority chain; if authority data is needed, model and render it
independently instead of reusing authority.

In `@src/ui_assets/status-bezel.js`:
- Around line 32-44: Update DriftIndicator so the status summary is shown
whenever any finding count exists, including info-only findings, rather than
gating it solely on drift. Preserve the existing severity tone and clean-state
behavior for zero counts, and use the existing counts/parts values.

In `@src/ui_assets/style.css`:
- Around line 130-135: Update the min-height declaration in
.graph-stage.is-empty to use an existing defined spacing token from --s-1
through --s-12, or an appropriate fixed value, instead of the undefined --s-20.

In `@src/ui_assets/utils.js`:
- Around line 75-79: Update the copy() API in the surrounding utility to accept
a caller-provided fallback value, and return that fallback when copy loading has
not completed instead of always returning an empty string. Update the
runtime-not-ready error path in app.js to pass a non-empty fallback so critical
pre-load errors remain visible, while preserving existing behavior for loaded
copy and missing-key cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f93c88b-9bf8-44e2-8c4e-ecadeac3392d

📥 Commits

Reviewing files that changed from the base of the PR and between ef506ee and 86080c2.

📒 Files selected for processing (33)
  • AGENTS.md
  • README.md
  • docs/_headers
  • docs/design-system/README.md
  • docs/design-system/components.css
  • docs/design-system/copy.toml
  • docs/design-system/index.html
  • docs/design-system/tokens.css
  • docs/index.html
  • docs/landing/index.html
  • harness/fixtures/assets/copy.json
  • map.json
  • scripts/check-a11y.sh
  • scripts/check-design-tokens.sh
  • src/ui/mod.rs
  • src/ui/server.rs
  • src/ui_assets/app-data.js
  • src/ui_assets/app.js
  • src/ui_assets/canvas-nav.js
  • src/ui_assets/channel-bar.js
  • src/ui_assets/evidence-rail.js
  • src/ui_assets/graph-layout.js
  • src/ui_assets/graph-workspace.js
  • src/ui_assets/node-module.js
  • src/ui_assets/query-rail.js
  • src/ui_assets/status-bezel.js
  • src/ui_assets/style.css
  • src/ui_assets/utils.js
  • tests/check_a11y.rs
  • tests/check_design_tokens.rs
  • tests/graph_explorer.rs
  • tests/landing_assets.rs
  • tests/phase_7_7_ux_foundation.rs
💤 Files with no reviewable changes (1)
  • docs/landing/index.html
🛑 Comments failed to post (13)
docs/_headers (1)

1-3: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

# Inspect whether the workflow explicitly processes docs/_headers.
fd -a -t f '^pages\.yml$' .github/workflows \
  --exec sh -c 'echo "### $1"; sed -n "1,240p" "$1"' sh {}

# Verify the actual deployed response.
curl -sSI https://cairn-framework.github.io/cairn/ |
  grep -iE '^(referrer-policy|x-content-type-options):' || true

Repository: cairn-framework/cairn

Length of output: 1030


Move these headers into the Pages hosting layer

docs/_headers is deployed as a static file here, so it won't set Referrer-Policy or X-Content-Type-Options on GitHub Pages responses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/_headers` around lines 1 - 3, Remove the Referrer-Policy and
X-Content-Type-Options entries from docs/_headers and configure both headers in
the GitHub Pages hosting layer instead, using the repository’s existing
deployment configuration.
docs/design-system/components.css (1)

369-371: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Which browsers support CSS context-stroke and context-fill for SVG marker fill?

💡 Result:

As of July 18, 2026, the CSS values context-fill and context-stroke are supported in the following major browser engines: - Chromium-based browsers (Chrome, Edge, Opera, etc.): Supported since version 124 (released in April 2024) [1][2][3]. - Firefox: Supported since version 111 (released in March 2023) [1][2][4]. These features are currently not supported in Safari (or any WebKit-based browser) [1][5][2]. The context-fill and context-stroke values are part of the SVG 2 specification [6][4]. They allow SVG elements—specifically those instantiated via markers or the use element—to dynamically inherit the fill and stroke properties from the element that references them, rather than requiring static or pre-defined colors [6][3][7]. This is particularly useful for creating scalable markers that automatically match the color of the path or shape they are attached to [6][8][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== relevant files ==\n'
git ls-files 'docs/design-system/components.css' 'src/ui_assets/style.css' 'src/ui_assets/*.js' 'src/ui_assets/*.ts' 'src/ui_assets/*.tsx' 'docs/design-system/*.html' 'docs/design-system/*.md' | sed 's#^`#-` #'

printf '\n== marker rules ==\n'
grep -RIn --line-number --context=2 'marker path' docs/design-system/components.css src/ui_assets/style.css || true

printf '\n== dependency arrow usage ==\n'
grep -RIn --line-number --context=3 '`#dependency-arrow`\|dependency-link\|context-stroke\|context-fill' src docs/design-system || true

Repository: cairn-framework/cairn

Length of output: 7666


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline src/ui_assets/graph-workspace.js --view expanded || true
printf '\n== marker defs and arrow usage ==\n'
sed -n '1,180p' src/ui_assets/graph-workspace.js | cat -n

Repository: cairn-framework/cairn

Length of output: 8928


Directional arrowheads use a single color. The shared #dependency-arrow marker is filled with currentColor, but it does not inherit each .dependency-link color, so is-out, is-in, and is-dimmed edges all reuse the same arrowhead color. Use context-stroke where supported, with a Safari fallback, or split the marker by direction and keep both CSS copies aligned.

  • docs/design-system/components.css#L369-L371
  • src/ui_assets/style.css#L121-L123
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 370-370: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

📍 Affects 2 files
  • docs/design-system/components.css#L369-L371 (this comment)
  • src/ui_assets/style.css#L121-L123
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design-system/components.css` around lines 369 - 371, The shared
dependency-arrow marker uses currentColor, preventing arrowheads from inheriting
each dependency link’s direction-specific color. Update the marker path styling
in docs/design-system/components.css lines 369-371 and src/ui_assets/style.css
lines 121-123 to use context-stroke with the required Safari fallback, or
implement synchronized direction-specific marker copies in both files.
docs/design-system/tokens.css (1)

144-146: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

--error and --warning resolve to the same colour, so severity coding is not distinguishable.

--error: var(--drift) and --drift: var(--ci-amber) (Line 136), while --warning: var(--ci-amber) (Line 145). Both therefore resolve to #d99a2b. Every consumer that pairs them (.status-severity.error/.warning, .channel-severity, .channel-item.tone-error/.tone-warning) uses the same base colour and identical opacities, so Error and Warning render pixel-identically and only the text label differentiates them. This undercuts the PR's severity-coding goal.

Consider mapping --error to the blocking accent so it reads as more urgent than the amber warning. Note the light theme also needs a distinct value because --block: var(--drift) there (Line 294), so error would still collapse onto warning under [data-theme="light"].

🎨 Proposed change (dark root) plus a light-theme override
-  --error: var(--drift);
+  --error: var(--block);
   --warning: var(--ci-amber);
   --info: var(--ink-aged);
 [data-theme="light"] {
   ...
   --block: var(--drift);
+  --error: color-mix(in oklab, var(--ci-amber) 70%, `#7a2f1a`);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design-system/tokens.css` around lines 144 - 146, Update the token
definitions in the dark root and the [data-theme="light"] override so --error
uses a distinct blocking accent instead of resolving through --drift to
--ci-amber, while --warning remains amber. Ensure --error and --warning resolve
to different colours in both themes, preserving the existing severity consumer
mappings.
docs/index.html (4)

19-21: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Do not create a page-local design system.

The page redeclares typography, spacing, radii, motion, buttons, cards, panels, and hardcoded shadows instead of consuming the canonical tokens and components. Move reusable definitions into tokens.css/components.css, link components.css, and update the design-system reference and README.

As per coding guidelines, UI changes must use tokens from docs/design-system/tokens.css, reuse components.css, and update the canonical design-system files and references when adding tokens or components.

Also applies to: 29-60, 225-258, 643-680, 717-774, 1274-1286

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/index.html` around lines 19 - 21, Remove the page-local design-system
declarations from the styles in docs/index.html, including typography, spacing,
radii, motion, buttons, cards, panels, and hardcoded shadows. Link and consume
the canonical docs/design-system/components.css and tokens.css instead, moving
any genuinely reusable definitions into those files. Update the design-system
reference and README to document the canonical additions, while preserving the
page’s existing visual behavior through shared tokens and components.

Source: Coding guidelines


115-166: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add a narrow-screen navigation layout.

The brand, five links, CTA, and version remain in one non-wrapping flex row while body clips horizontal overflow. On mobile, navigation items will extend beyond the viewport and become inaccessible.

Example responsive treatment
 nav.site .cta:hover { background: var(--stone-3); color: var(--ink-char); }
+
+@media (max-width: 760px) {
+  nav.site {
+    flex-wrap: wrap;
+    gap: var(--s-3);
+  }
+  nav.site .links {
+    order: 3;
+    width: 100%;
+    margin-left: 0;
+    flex-wrap: wrap;
+    gap: var(--s-4);
+  }
+  nav.site .version {
+    margin-left: auto;
+  }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

nav.site {
  padding: var(--s-5) 0;
  display: flex;
  align-items: center;
  gap: var(--s-6);
  font-family: var(--font-sans);
  font-size: var(--t-small);
  color: var(--ink-faded);
  letter-spacing: 0.03em;
}
nav.site .brand {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  color: var(--ink-char);
  font-weight: 500;
  letter-spacing: 0.01em;
}
nav.site .brand svg { display: block; }
nav.site .links {
  margin-left: auto;
  display: flex;
  gap: var(--s-6);
}
nav.site .links a {
  color: var(--ink-aged);
  text-transform: lowercase;
  border: 0;
  transition: color var(--fast) var(--ease);
}
nav.site .links a:hover { color: var(--ink-char); }
nav.site .version {
  color: var(--ink-mist);
  border: 1px solid var(--seam-thin);
  padding: 2px var(--s-2);
  border-radius: var(--r-edge);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.05em;
}
nav.site .cta {
  color: var(--ink-char);
  border: 1px solid var(--seam-clear);
  padding: 4px var(--s-3);
  border-radius: var(--r-edge);
  font-family: var(--font-sans);
  font-size: var(--t-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
nav.site .cta:hover { background: var(--stone-3); color: var(--ink-char); }

`@media` (max-width: 760px) {
  nav.site {
    flex-wrap: wrap;
    gap: var(--s-3);
  }
  nav.site .links {
    order: 3;
    width: 100%;
    margin-left: 0;
    flex-wrap: wrap;
    gap: var(--s-4);
  }
  nav.site .version {
    margin-left: auto;
  }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/index.html` around lines 115 - 166, Update the responsive styles for
nav.site so narrow screens provide an accessible mobile layout instead of
relying on a single non-wrapping row clipped by body overflow. Adjust the
navigation and its child selectors, including .brand, .links, .version, and
.cta, to fit or scroll within the viewport while preserving access to all links,
the CTA, and version indicator.

813-821: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Honor reduced motion for the autoplay video and make “off” functional.

The media query disables CSS animation only, so the looping hero video continues playing. The tweak switch merely sets an unused data-anim attribute. Pause the video and suppress reveal/variant animation when either reduced motion or state.anim === "off" applies.

Also applies to: 877-882, 1204-1206, 1252-1268

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/index.html` around lines 813 - 821, Update the reduced-motion handling
and animation toggle logic around the hero autoplay video, reveal effects,
variant animations, and the state.anim switch so reduced motion pauses the video
and suppresses all animation, while state.anim === "off" produces the same
behavior instead of only setting an unused data-anim attribute. Ensure the video
is paused and animation/reveal styles are disabled for either condition, while
normal animation remains unchanged otherwise.

931-969: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore the canonical provenance and domain terminology.

  • Remove synthesis and connect research directly to the Decision hinge.
  • Replace the “layer” labels with domain/reconciler terminology; these are pluggable domains, not a flattened architectural stack.

As per coding guidelines, preserve Source → Research → Decision and Decision → Blueprint → Contract → Code, and do not flatten Cairn’s model into layers.

Also applies to: 1099-1121

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/index.html` around lines 931 - 969, Update the provenance and authority
diagrams to preserve the canonical Source → Research → Decision and Decision →
Blueprint → Contract → Code flows. Remove the “synthesis” node and reroute the
research connection directly to the Decision hinge, and replace any “layer”
labels with the established pluggable-domain/reconciler terminology in both
diagram occurrences. Do not introduce a flattened architectural stack.

Source: Coding guidelines

README.md (1)

31-31: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include the agent-instruction cleanup in this exit promise.

Lines 86 and 92 establish that users must also remove the pasted Cairn section from their agent instructions. Without that step, the repository is not restored as claimed.

Proposed clarification
-If you ever want out, delete `.cairn/`, `cairn.blueprint`, `cairn.config.yaml`, and the generated map files, and your repo is back to how it was.
+If you ever want out, delete `.cairn/`, `cairn.blueprint`, `cairn.config.yaml`, and the generated map files, then remove the pasted Cairn section from your agent instructions.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

You do almost nothing. Your agent drafts the first map in one command, and keeps it in sync as part of the work you already asked for. The map and all of its notes are plain text in your repo, plus a blueprint file and a small config file. Cairn is local-first, so your project data stays on your machine. If you ever want out, delete `.cairn/`, `cairn.blueprint`, `cairn.config.yaml`, and the generated map files, then remove the pasted Cairn section from your agent instructions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 31, Update the exit-cleanup promise in the README
paragraph to include removing the pasted Cairn section from the agent
instructions, alongside deleting the listed repository files. Preserve the
existing local-first and cleanup guidance while ensuring all setup changes are
covered.
src/ui_assets/canvas-nav.js (1)

84-92: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Select the first node when the canvas has no active selection.

The app now starts unselected, but this fallback treats the first node as selected and immediately removes it from the candidates. A single-node graph therefore cannot be selected with arrow keys, while larger graphs skip the first option.

Proposed fix
-  const source = nodes.find((node) => node.id === currentSelectionId) || nodes.find((node) => node.id === visibleIds[0]) || nodes[0];
+  const source = nodes.find((node) => node.id === currentSelectionId);
   if (!source) {
-    return null;
+    return nodes.find((node) => node.id === visibleIds[0])?.id || nodes[0]?.id || null;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  const source = nodes.find((node) => node.id === currentSelectionId);
  if (!source) {
    return nodes.find((node) => node.id === visibleIds[0])?.id || nodes[0]?.id || null;
  }

  const candidates = nodes.filter((node) => node.id !== source.id);
  if (!candidates.length) {
    return null;
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui_assets/canvas-nav.js` around lines 84 - 92, Update the source and
candidate selection logic around the current selection lookup so the first node
is only used as a navigation target when there is no active selection, not
treated as the already-selected source. Preserve the existing behavior for an
active currentSelectionId while allowing arrow-key navigation to include the
first node and select it in both single-node and larger graphs.
src/ui_assets/evidence-rail.js (1)

125-144: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not present source artefacts as the authority chain.

artefacts.sources is provenance data, but it is renamed and displayed as lineage-authority. Label this section as Sources, or model the actual authority sequence separately.

As per coding guidelines, preserve the distinct provenance chain “Source → Research → Decision” and authority chain “Decision → Blueprint → Contract → Code”; do not flatten them into layers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui_assets/evidence-rail.js` around lines 125 - 144, Update LineagePlate
so artefacts.sources remains a distinct Sources/provenance section rather than
being assigned to lineage-authority or labeled as authority. Preserve the
separate “Source → Research → Decision” provenance chain and “Decision →
Blueprint → Contract → Code” authority chain; if authority data is needed, model
and render it independently instead of reusing authority.

Source: Coding guidelines

src/ui_assets/status-bezel.js (1)

32-44: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Show the summary for info-only findings.

parts includes informational counts, but the summary is gated on drift, so an info-only result displays “clean” with no count.

Proposed fix
-      ${drift ? html`<span class="status-annunciator-summary">${parts.join(" · ")}</span>` : null}
+      ${parts.length ? html`<span class="status-annunciator-summary">${parts.join(" · ")}</span>` : null}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

function DriftIndicator({ counts }) {
  const drift = counts.errors + counts.warnings;
  const parts = [];
  if (counts.errors) parts.push(`${counts.errors} ${copy("webui.findings-errors")}`);
  if (counts.warnings) parts.push(`${counts.warnings} ${copy("webui.findings-warnings")}`);
  if (counts.infos) parts.push(`${counts.infos} ${copy("webui.findings-infos")}`);

  const tone = drift ? (counts.errors ? "error" : "warning") : "info";
  return html`
    <p class="status-annunciator status-annunciator-chip" role="status">
      <${SeverityChip} tone=${tone}>${copy(drift ? "webui.status-drift" : "webui.status-clean")}</${SeverityChip}>
      ${parts.length ? html`<span class="status-annunciator-summary">${parts.join(" · ")}</span>` : null}
    </p>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui_assets/status-bezel.js` around lines 32 - 44, Update DriftIndicator so
the status summary is shown whenever any finding count exists, including
info-only findings, rather than gating it solely on drift. Preserve the existing
severity tone and clean-state behavior for zero counts, and use the existing
counts/parts values.
src/ui_assets/style.css (1)

130-135: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm --s-20 is never defined anywhere.
rg -n -- '--s-20\s*:' docs/design-system src/ui_assets || echo "no --s-20 definition found"

Repository: cairn-framework/cairn

Length of output: 186


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- tokens.css spacing definitions ---\n'
rg -n -- '--s-[0-9]+' docs/design-system/tokens.css

printf '\n--- graph-stage.is-empty usage ---\n'
rg -n -- 'graph-stage\.is-empty|--s-20' src/ui_assets/style.css src/ui_assets -n

Repository: cairn-framework/cairn

Length of output: 870


Replace --s-20 with a defined spacing token

src/ui_assets/style.css:134
tokens.css only defines --s-1 through --s-12, so var(--s-20) is invalid here and the empty-state min-height gets dropped. Use one of the existing spacing tokens or a fixed value instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui_assets/style.css` around lines 130 - 135, Update the min-height
declaration in .graph-stage.is-empty to use an existing defined spacing token
from --s-1 through --s-12, or an appropriate fixed value, instead of the
undefined --s-20.
src/ui_assets/utils.js (1)

75-79: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep critical pre-load errors non-empty.

copy() now returns "" before copy loading, so the runtime-not-ready branch in app.js sets an empty error and renders the normal empty workspace. Support a caller-provided fallback and use it for that branch.

Proposed copy API adjustment
-function copy(key) {
+function copy(key, pendingFallback = "") {
...
-      return "";
+      return pendingFallback;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui_assets/utils.js` around lines 75 - 79, Update the copy() API in the
surrounding utility to accept a caller-provided fallback value, and return that
fallback when copy loading has not completed instead of always returning an
empty string. Update the runtime-not-ready error path in app.js to pass a
non-empty fallback so critical pre-load errors remain visible, while preserving
existing behavior for loaded copy and missing-key cases.

@George-RD
George-RD merged commit cd6bae7 into main Jul 18, 2026
16 checks passed
@George-RD
George-RD deleted the webui/relations-layout-overhaul branch July 18, 2026 20:42
@George-RD George-RD mentioned this pull request Jul 19, 2026
George-RD added a commit that referenced this pull request Jul 19, 2026
Bump the workspace to 0.8.0 and write the changelog for the graph
explorer relayout round (PRs #428, #429). README: swap the release badge
to badgen (the shields.io dynamic release path renders broken), add
fresh graph-explorer screenshots (default map and node focus, captured
from the 0.8.0 binary at 2x), served via absolute raw URLs so the
crates.io render resolves them. copy-lint: 0 em-dashes, 0 AI-tier-1
terms, FK grade 6.5.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant