Skip to content

feat(etch): port-aware layout, orthogonal routing, interactive HTML#36

Closed
avrabe wants to merge 20 commits intomainfrom
feat/rendering-quality
Closed

feat(etch): port-aware layout, orthogonal routing, interactive HTML#36
avrabe wants to merge 20 commits intomainfrom
feat/rendering-quality

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented Mar 17, 2026

Summary

Major etch rendering upgrade with three new capabilities:

Port-aware layout (RENDER-REQ-002)

  • PortInfo, PortSide, PortDirection, PortType data model
  • Ports positioned along node sides, auto-resolution by direction
  • Node height grows for port count
  • Edges snap to specific ports when source_port/target_port set
  • SVG rendering: circles, direction triangles, type colors, labels

Orthogonal edge routing (RENDER-REQ-001)

  • New ortho.rs module with visibility-graph-based A* router
  • Obstacle avoidance around node rectangles
  • Bend penalty to minimize unnecessary corners
  • EdgeRouting::Orthogonal (default) vs CubicBezier (legacy)
  • SVG uses L (line-to) commands for orthogonal paths

Interactive HTML wrapper (RENDER-REQ-003, 005, 006)

  • New html.rs module producing self-contained HTML
  • Pan (click-drag), zoom (wheel around cursor), selection (click/Ctrl+click)
  • Group highlighting for containers
  • URL parameter ?highlight=ID for deep linking
  • Semantic zoom CSS classes at low zoom levels
  • Zero external dependencies

Backward compatibility

  • NodeInfo::ports defaults to empty vec
  • EdgeInfo::source_port/target_port default to None
  • All rivet callers updated with empty ports
  • 56 tests pass (16 new)

Trace: skip

🤖 Generated with Claude Code

Test and others added 20 commits March 15, 2026 12:15
Add recursive bottom-up Sugiyama layout for compound (hierarchical) graphs.
Containers are sized to fit their children, with padding and header labels.
SVG rendering draws containers as dashed-border background boxes with
lightened fill colors.

- NodeInfo gains `parent: Option<String>` for containment hierarchy
- LayoutNode gains `is_container: bool` flag
- LayoutOptions gains `container_padding` and `container_header` settings
- layout() auto-detects compound graphs and delegates to layout_compound()
- Variable-size node support in coordinate assignment
- Container SVG: dashed border, bold header label, lightened fill
- 8 new tests (6 layout + 2 SVG) covering 1-level, 2-level, sibling,
  mixed, and size constraint scenarios

Trace: skip

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
petgraph 0.7 is API-compatible for our usage. Upgrading ensures
downstream consumers (spar-render) don't need dual petgraph versions.

Trace: skip

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 3 workstream integration:
- LSP server (rivet lsp) with diagnostics, hover, go-to-definition
- SCORE metamodel schema (18 artifact types, ASPICE-aligned rules)
- Kani proof harnesses (10 bounded model checking proofs)
- Extended STPA safety analysis (H-9..H-15, SC-11..SC-17, 45 UCAs/CCs)
- Extended AADL schema (4 artifact types)
- Phase 3 design plans (6 workstream documents)

Documentation and CI audit fixes:
- Fix MSRV 1.85 → 1.89 in verification.md and clippy.toml
- Fix schema type counts (ASPICE 14→13, Cybersecurity 10→8)
- Add undocumented modules to architecture.md (lsp, proofs, embedded,
  externals, lifecycle, commits, docs, schema_cmd)
- Replace missing .github/actions/compliance with inline steps
- Standardize checkout@v6 → v4 across release workflow
- Add anti-pattern guidance: do not hardcode counts in documentation
- Fix FEAT-050/051 phase-4 → future (schema validation warning)
- Add FEAT-052 (LSP) and FEAT-053 (SCORE schema) artifacts
- Remove stale hardcoded artifact counts from getting-started.md
- Fix clippy collapsible_if and map_entry warnings
- Fix pre-commit hooks: +stable for clippy, remove invalid --strict flag
- cargo fmt clean, 216 tests pass, validate PASS (0 warnings)

Implements: FEAT-052
Implements: FEAT-053
Implements: FEAT-049
Refs: REQ-007
Refs: REQ-010
Refs: REQ-030

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
10-task plan covering:
- Reusable UI components (filter bar, sortable table, collapsible tree, pagination)
- serve.rs split into serve/ module directory
- URL-persisted view state across all views
- Graph scalability (spawn_blocking, node budget, dynamic SVG buffer)
- Rich STPA/traceability views with fold/unfold and link drill-down
- Print mode (?print=1) for clean printable output
- Comprehensive Playwright E2E test suite (16 spec files)
- STPA scalability coverage (H-13, SC-15)
- CI integration for Playwright

Refs: FEAT-052

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dashboard graph view can become unresponsive when artifact count exceeds
the Sugiyama layout engine's capacity. H-13 captures this as a hazard
leading to L-4 (productivity) and L-5 (safety assurance). SC-15 requires
graceful degradation with node budgets and spawn_blocking. UCA-D-3 and
CC-D-3 constrain the dashboard controller.

Refs: H-13

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract monolithic serve.rs into 5 focused files:
- mod.rs (934 lines) — AppState, router, middleware, utility handlers
- styles.rs (580 lines) — CSS constant
- js.rs (1027 lines) — GRAPH_JS, SEARCH_JS, AADL_JS constants
- layout.rs (200 lines) — page_layout() with CSS/JS imports
- views.rs (4846 lines) — all 30+ view handlers and param structs

No behavioral changes. All routes, HTML output, and tests identical.

Implements: FEAT-052
Refs: DD-005

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…alysis

REQ-037 through REQ-042: source-code traceability scanning, artifact
revision staleness, deep recursive coverage, variant/product-line
filtering, overlay files, and structured export for external viz tools.
All draft status with upstream-ref noting competitive origin.

Implements: REQ-004

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…amic SVG buffer

- Add max_nodes budget to LayoutOptions; returns sentinel node when exceeded
- Move graph layout + SVG render to tokio::spawn_blocking (unblocks async runtime)
- Dynamic SVG String capacity (~500 bytes/node + ~200 bytes/edge)
- Default budget: 300 nodes, configurable via ?budget=N (max 1000)
- Pre-collect owned NodeInfo before spawn_blocking (Send + 'static)

Fixes: H-13
Implements: SC-15
Implements: CC-D-3

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FEAT-054: rivet add --link (create with links in one command)
FEAT-055: rivet batch (apply multiple mutations from file)
FEAT-056: JSON output for validate/coverage/context/diff
FEAT-057: rivet graph <id> --depth N (local link neighborhood)
FEAT-058: rivet scaffold --chain (generate traceability skeleton)

From intensive AI agent testing session — the theme is: reduce
round-trips (batch operations, links-on-create) and produce
machine-readable output (JSON everywhere) for agent workflows.

Refs: REQ-031
Refs: REQ-042

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add port-aware types to etch's public API:
- PortInfo, PortSide, PortDirection, PortType — define ports on nodes
- LayoutPort — positioned port in layout output
- NodeInfo gains `ports: Vec<PortInfo>` (empty = backward compat)
- EdgeInfo gains `source_port`/`target_port: Option<String>`
- LayoutNode gains `ports: Vec<LayoutPort>`
- LayoutEdge gains `source_port`/`target_port: Option<String>`

All existing callers (rivet, etch tests) updated with empty ports.
40 tests pass, full backward compatibility preserved.

Satisfies: RENDER-REQ-002

Trace: skip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- position_ports() computes LayoutPort coordinates from PortInfo + node bounds
- PortSide::Auto resolves In→Left, Out/InOut→Right
- Node height grows for port count (12px/port + 8px padding)
- Edge waypoints snap to port (x,y) when source_port/target_port set
- 4 new tests: side positioning, auto resolution, height growth, edge snapping

Satisfies: RENDER-REQ-002

Trace: skip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New components.rs module with:
- ViewParams: URL query param struct shared across all views (types,
  status, tags, q, sort, dir, page, per_page, open, print)
- FilterBar: type checkboxes, status dropdown, text search with HTMX
  debounce (300ms), all wired to URL params
- SortableTable: clickable column headers toggle sort direction in URL
- CollapsibleTree: <details>/<summary> with URL-persisted open IDs,
  Expand All / Collapse All buttons
- Pagination: page controls with « ‹ N of M › » navigation
- paginate() helper for slicing data by page

Also fixes etch port fields (ports, source_port, target_port) added
by spar integration work — updates all NodeInfo/EdgeInfo constructors
in serve views and etch tests.

12 unit tests for all components.

Implements: FEAT-052
Refs: DD-005

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Port circles, direction triangles, labels, and CSS type classes.

Satisfies: RENDER-REQ-002
Trace: skip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- FilterBar with 10 STPA type checkboxes + UCA subtype dropdown
- URL-persisted fold state (?open=H-1,H-2,...) — defaults to losses+hazards open
- Expand All / Collapse All buttons updating URL
- Type filtering (?types=loss,hazard) for both hierarchy and UCA table
- Text search (?q=firmware) across ID and title, case-insensitive
- UCA subtype filter (?status=not-providing) for tree and table
- Empty-state messages when filters yield no results

Implements: FEAT-052
Refs: REQ-002

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EdgeRouting::Orthogonal (default) and CubicBezier (legacy).
New LayoutOptions fields: bend_penalty, edge_separation, port_stub_length.
Orthogonal falls back to CubicBezier until ortho.rs is implemented.

Satisfies: RENDER-REQ-001
Trace: skip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- print_layout() renders content without nav, context bar, HTMX, or JS
- Print CSS: hides interactive elements, sets max-width, @media print
- Middleware detects ?print=1 query param and uses print_layout
- Print button added to context bar (opens current URL + ?print=1 in new tab)
- Works on all routes: /stpa?print=1, /artifacts?print=1, etc.

Implements: FEAT-052
Refs: DD-005

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New ortho.rs module implements visibility-graph-based orthogonal router:
- Padded obstacle rectangles from all nodes
- A* pathfinding with bend penalty on candidate waypoints
- Direct routing for axis-aligned pairs, L-shaped fallback
- 5 new tests (direct, L-shaped, obstacle avoidance, multi-node)

Layout dispatches on EdgeRouting::Orthogonal vs CubicBezier.

Satisfies: RENDER-REQ-001
Trace: skip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detect axis-aligned waypoints and emit L (line-to) commands instead of
C (cubic bezier). Non-orthogonal paths still use bezier curves.

Trace: skip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ination

- FilterBar with type checkboxes, status dropdown, text search
- SortableTable with clickable column headers (ID, Type, Title, Status, Links)
- Pagination (default 50/page) with URL-persisted page state
- All filter/sort/page state in URL: /artifacts?types=requirement&sort=id&dir=asc&page=2
- Removed old client-side filterTable() JS — server-side filtering replaces it

Implements: FEAT-052
Refs: DD-005

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New html module wraps SVG in self-contained HTML with embedded JS:
- Pan: click-drag on background
- Zoom: mouse wheel around cursor, viewBox manipulation
- Selection: click node to select, Ctrl+click for multi-select
- Group highlight: click container to select
- CustomEvent emission (etch-select, etch-container-select)
- URL parameter ?highlight=ID for deep linking
- Semantic zoom CSS classes (zoom-low, zoom-overview)
- 5 new tests

Satisfies: RENDER-REQ-003, RENDER-REQ-005, RENDER-REQ-006
Trace: skip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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