Skip to content

IM-4c: unified Wall layout authority + Focus lens + propose-hypothesis-from-finding#258

Merged
jukka-matti merged 13 commits into
mainfrom
im-4c-unified-wall-layout
May 31, 2026
Merged

IM-4c: unified Wall layout authority + Focus lens + propose-hypothesis-from-finding#258
jukka-matti merged 13 commits into
mainfrom
im-4c-unified-wall-layout

Conversation

@jukka-matti
Copy link
Copy Markdown
Owner

IM-4c — the last layout half of IM-4 §8.6

Plan: docs/superpowers/plans/2026-05-31-im-4c-unified-wall-layout-focus-lens.md.
Spec: docs/superpowers/specs/2026-05-30-investigation-wall-unified-canvas-design.md §8.6.
Follows IM-4a (#256) + IM-4b (#257). After this, only IM-6 remains.

Single-Opus implementer (per the IM-4b TDD-pipeline trial verdict — integration-heavy UI needs one implementer that wires through the production seam + runs real builds).

What ships

  1. computeWallLayout — one position authority. Pure/deterministic; returns hub/finding/factor positions + scope anchor + edges. WallCanvas renders from it (data-wall-node-id/data-x/data-y); the old inline hub/finding math is gone.
  2. Minimap + both apps' pan-to-node consume the authority (buildWallLayoutArgs) — the 3× duplicated layout formula is killed.
  3. Orphan-finding lane + propose-hypothesis CTA. Orphan findings (linked to no hub) get a home; FindingChip gains onProposeHypothesis. Wired per-app through the rendered-hubs path (Azure → hypothesesState.createHub+connectFinding; PWA → createHubFromFinding) — verified render-through, not a store-call spy.
  4. Focus lens — degree-of-interest dimming pinned to a single viewStore.focusedWallEntityId (read by WallCanvas AND Minimap); click-to-focus / empty-canvas-clear; pure wallDegreeOfInterest BFS. Dimming only — never touches CanvasLevel/LOD.
  5. setHubStatus orphan documented/removed (spec §10 chore(deps): bump pnpm/action-setup from 4.1.0 to 4.4.0 #1 — status is derived).

Factor model (settled this PR's design conversation)

Factors render as the scope-level contributing-factors band (correct for V1). A cause's factors are a derived projection, never stored — the adversarial review's "factors aren't coordinate-space nodes" is by design; the computeWallLayout factor-positioning is the forward hook for the V-next model-builder (vital-few best-subset UX, R²adj + p, full analyst control). Pinned in ADR-086 Amendment (2026-05-31) + the decision-log. The Focus lens never moves model metrics ("in the model" ≠ "is the cause").

Verification

  • Full gate (bash scripts/pr-ready-check.sh) green: full turbo test, lint, level-boundaries, both app vite builds + dist-integrity. (Earlier run's only red was a missing-frontmatter docs:check on the plan doc — fixed on main.)
  • Focused adversarial review: all 5 layout/focus/orphan/propose features reachable through the production seam; seam tests non-vacuous (render the real WallCanvas/AnalyzeWorkspace, assert real data-*/opacity/positions). Its two "majors" both resolved: factors-as-band is the settled V1 model (deferred per ADR amendment); the active-IP-scope render-through limitation is pre-existing (mirrors the existing create-hub composer) — logged, not introduced here.

🤖 Generated with Claude Code

jukka-matti and others added 13 commits May 31, 2026 00:56
…thesis-from-finding

The remainder of IM-4 spec §8.6 (deferred from PR #257's IM-4b sub-slice):
one Wall coordinate space via a single computeWallLayout authority (kills the
3x duplicated Minimap/pan-to-node math), the Focus lens (degree-of-interest
dimming pinned to viewStore), an orphan-finding home + the descoped
createHubFromFinding CTA, and the setHubStatus orphan deletion (§10). Bakes in
the anti-green-but-dead seam-test discipline from the IM-4b trial. Linked from
the master plan IM-4 section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Single deterministic source of truth for hub / finding / factor positions +
tethers on the Investigation Wall, in the fixed 2000x1400 user-space. Lifts the
inline placement math out of WallCanvas verbatim (linear + tributary hub rows,
support/counter chip columns at hubX+/-130, scope anchor, factor band) and adds
a left-gutter orphan lane for findings linked to no hub. Minimap + both apps'
pan-to-node will consume this in later tasks (kills the 3x duplicated math).

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

WallCanvas now derives hub anchors, evidence-chip columns, the orphan-finding
lane, and tether endpoints from computeWallLayout instead of inline math —
killing the source-of-truth duplication that let Minimap + pan-to-node drift.
Each node carries data-wall-node-id + data-x/data-y so consumers + seam tests
read the SAME coordinates the DOM rendered. Adds the WallCanvas.layout seam test
pinning hub/orphan positions + the LOUD counts-against guard + tether-to-anchor
guard. FindingChip gains the (Task-4-wired) onProposeHypothesis affordance, shown
only on orphan chips. All 357 AnalyzeWall tests stay green (collab seam included).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds buildWallLayoutArgs — the single owner of the tributary-bucketing rule
(mirrors WallCanvas's tributaryGroups memo) — so WallCanvas, Minimap, and both
apps' pan-to-node all feed the SAME inputs into computeWallLayout. Minimap now
positions its dots from the authority (correct under tributary grouping, where
the old linear-row duplicate silently drifted off the cards); both apps' command-
palette pan-to-node centers on the authority hub position. Minimap seam test
proves dot x === authority x under tributary grouping and != the linear column.

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

Orphan findings (linked to no hub) get a home on the Wall: a left-gutter lane
that keeps the SVG body mounted even with zero hubs (EmptyState now only shows
when there are neither hubs NOR orphans). FindingChip renders the
'Propose suspected mechanism from this finding' affordance on orphan chips.

createHubFromFinding trap — wired per app's rendered-hubs source of truth:
- Azure renders hypothesesState.hubs (useHypotheses hook) → propose routes
  through createHub + connectFinding on that hook (NOT analyzeStore, a different
  collection that would not re-render the Wall).
- PWA renders useAnalyzeStore.hypotheses reactively → propose calls
  createHubFromFinding, which re-renders the Wall directly.

Seam tests: WallCanvas.proposeHypothesis.seam proves render-through (a NEW hub
card appears via a stateful harness mirroring the apps); the ideasSection descope
guard is FLIPPED to the positive assertion; AnalyzeWorkspace.mapwall proves the
Azure wiring hits createHub + connectFinding, not a bare store call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds viewStore.focusedWallEntityId (+ setFocusedWallEntity) as the SINGLE focus
source (ADR-086 — WallCanvas AND Minimap read this one field, no per-renderer
useState). New pure wallFocus.ts: wallDegreeOfInterest = undirected BFS over the
WallLayout tethers (focused=0, adjacent=1, distant>=2; null focus = 0 = no
dimming), focusOpacity maps distance to vivid/mid/dim tiers. WallCanvas applies
the opacity + data-doi to every hub/finding/orphan node, focuses on node click,
clears on empty-canvas click (a back rect). Minimap dims its dots from the SAME
store field + the same authority edges. Focus is dimming ONLY — never touches
CanvasLevel/LOD (spec §9). Focus seam test asserts real opacity + click gestures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
setHubStatus had zero production callers and no action/interface definition
anywhere in packages/ or apps/ (spec §10 #1 — a manual status override
contradicts the derived-status model). Removed the stale comment reference that
implied the action still existed on main; the comment now documents that status
is derived via deriveHypothesisStatus + the disconfirmation gesture, with the
stored-vs-derived reader migration deferred to IM-6.

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

The PWA threads groupByTributary=Boolean(processMap && wallGroupByTributary),
and some fixtures supply a processMap with no `tributaries` array. The Minimap's
new computeWallLayout call then hit `processMap.tributaries.map` on undefined.
Guard with Array.isArray so a tributaries-less processMap falls back to the
linear layout instead of throwing. Surfaced by AnalyzeView.mapwall.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The plan committed in 68c0608 lacked the SDD frontmatter block, failing
pnpm docs:check (and pr-ready-check) on main + every branch off it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…model-builder is V-next

Captures the settled factor-cause model from the IM-4c design conversation
(two grounded 5-lens explorations):
- A cause's factors = a DERIVED projection (deriveBranchColumns ∪ findings'
  columns ∪ CausalLink), never a stored Hypothesis.factorIds field. Factors are
  scope-level; the finding is the bridge.
- The typed factor→hypothesis bipartite edge is deferred (CausalLink stays
  factor→factor); the Evidence Map stays the separate cross-scope overview.
- The parsimony engine (computeBestSubsets) already exists; the vital-few
  model-builder (R²adj + p only, full analyst control with snap-back) is a
  ~90%-UI V-next increment. Selection-stability bootstrap deferred one increment.
- Focus lens never moves the model metrics ("in the model" ≠ "is the cause").

Amends ADR-086 (records the original heavier bipartite reading as superseded)
+ pins a Replayed-Decision in the log so the next implementer doesn't add a
stored factor field. IM-4c merges as the scope-band Wall.

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

computeWallLayout positions factors + emits factor edges, but WallCanvas does
not pass `factors` in V1 (the band still renders via TributaryFooter), so
factorPositions is intentionally empty in production. Comment points to the
ADR-086 Amendment + decision-log so the empty map reads as a deferred hook, not
a bug — factors stay scope-level; a cause's factors are derived, never stored.

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mean-beoynd-lite-pwa Building Building Preview, Comment May 31, 2026 5:15am
variscout_website Building Building Preview, Comment May 31, 2026 5:15am

@jukka-matti jukka-matti merged commit a47c473 into main May 31, 2026
1 of 3 checks passed
@jukka-matti jukka-matti deleted the im-4c-unified-wall-layout branch May 31, 2026 05:16
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