Skip to content

feat: retire Flow model; typed cross-scope Connections (#62)#68

Merged
CuriouslyCory merged 2 commits into
mainfrom
feat/retire-flow-typed-connections
Jun 2, 2026
Merged

feat: retire Flow model; typed cross-scope Connections (#62)#68
CuriouslyCory merged 2 commits into
mainfrom
feat/retire-flow-typed-connections

Conversation

@CuriouslyCory

@CuriouslyCory CuriouslyCory commented Jun 1, 2026

Copy link
Copy Markdown
Owner

Closes #62. The foundational re-founding slice of the #62#67 epic: a Connection becomes a directed, typed Edge that may link any two Components at any scope. Retires the Flow capability model.

After this slice an agent can create a typed cross-scope connection through the tRPC/MCP path; the web client compiles; same-Canvas connections still render — as plain lines (typed arrowheads → #65, cross-scope rendering → #63).

Model

  • Drop Flow / FlowRoute; rename the 1:1 spec row FlowSpecSpec, pointing at derived child Components via Node.sourceSpecId + Node.specKey.
  • FlowInteractionInteraction enum, gaining ASSOCIATION (the default).
  • Edge drops canvasNodeId (scope is derived from endpoint ancestry — Cross-scope connections: derived rendering in getCanvas #63) and gains interaction. De-dupe is two partial unique indexes keyed on projectId: directional (projectId, source, target, interaction) and ASSOCIATION-only unordered (projectId, LEAST, GREATEST).
  • One hand-edited migration: fresh Interaction type (enum-swap footgun), a guarded dedup pre-flight, correct FK/table/type drop order. pnpm db:check is clean.

Service

  • connectNodes accepts cross-scope and lineal (ingress) endpoints; rejects only the self-link; interaction is an input; the dedup findFirst branches on interaction (unordered for ASSOCIATION, exact-ordered for directional).
  • moveNode drops the orphan-reject (keeps the cycle-reject).
  • deleteNode/restoreNode drop the Flow arms, gain a Spec sweep; deleteEdge reverts to a lone soft-delete. Deleted flow.service / flow-route.service / flow-parser and the routeFlow cross-scope writer.
  • getCanvas reduced to { interiorNodes, interiorEdges, breadcrumbs } via a single both-endpoints relation filter (single round trip, no waterfall).

Beyond the issue text (caught in review, included here)

  • prisma-errors.ts matcher now accepts both new index names; dead Flow matchers removed (silent P2002 mis-handling otherwise).
  • Export seam: export.service / markdown.ts referenced the dropped canvasNodeId in raw CTEs (a runtime crash, not a compile error). Switched subtree + boundary derivation to endpoint-membership; golden re-baselined. (Markdown export + MCP: typed cross-scope connections, generated components #67 owns the full typed-export rewrite.)

MCP / API / Client

  • connect_components + apply_graph drop canvasNode, gain interaction, accept cross-scope endpoints. Removed the 9 flow/flowSpec/flowRoute tRPC procedures.
  • Removed all dead Flow UI (boundary-group, boundary-proxy, route-flow popover, flow palette, attach-spec, flow pill, flow: handle branch, flow-direction, flow-interaction-display, spec-kinds).

Docs (travel with the slice)

  • New: ADR-0027 (Connection carries its own interaction), ADR-0028 (cross-scope + lineal=ingress; same-Canvas retired), ADR-0030 (cascade/undo without FlowRoutes).
  • Amended: ADR-0005/0010/0023/0024/0026; ~20 CONTEXT.md entries amended/tombstoned.

Scope boundary

Stops at the seams: getCanvas ancestry rendering + far-end proxies → #63; spec→Component generation → #64; typed arrowhead rendering + interaction picker → #65; "Connect to…" gesture → #66; export rewrite + MCP spec-attach tool → #67.

Validation

  • pnpm check (eslint + tsc) — green
  • pnpm db:check (drift gate) — clean
  • ✅ Service tests — 181/183 in a clean run, covering cross-scope create, lineal create, self-link reject, dedup on both indexes (incl. REQUEST A→B / PUSH A→B / REQUEST B→A coexistence), move-no-orphan, deleteEdge lone, and the Spec cascade sweep. The only 2 misses were resetDb() TRUNCATE hook timeouts (transient Neon latency — one in token.service.test.ts, untouched by this PR).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor

    • Connections now carry intrinsic interaction types (e.g., ASSOCIATION/request) and determine arrowheads from those interactions.
    • Canvas now renders only interior components and their connections; boundary proxies/groups are deferred.
  • Removals

    • Flow-driven UI (flow palette, routing controls, flow-count badges, and Flow-based routing) removed.
    • Flow/FlowRoute models retired; Spec-based workflow introduced; component detail no longer shows "attach flow" UI.
  • Improvements

    • Cross-scope connections supported (links between components across nesting levels).
    • Markdown export and tooling docs updated to reflect typed, cross-scope connections.

Re-found the connection model: a Connection is now a directed, typed Edge that
may link any two Components at any scope. Retires the Flow capability model.

Model
- Drop Flow / FlowRoute; rename the 1:1 spec row FlowSpec -> Spec and point it at
  derived child Components via Node.sourceSpecId + Node.specKey.
- FlowInteraction -> Interaction enum, gaining ASSOCIATION (the default).
- Edge drops canvasNodeId (scope is derived from endpoint ancestry, #63) and
  gains interaction. De-dupe is two partial unique indexes keyed on projectId:
  directional (projectId, source, target, interaction) and ASSOCIATION-only
  unordered (projectId, LEAST, GREATEST). One hand-edited migration with the
  Postgres enum-swap ordering and a guarded dedup pre-flight.

Service
- connectNodes accepts cross-scope and lineal (ingress) endpoints; rejects only
  the self-link; interaction is an input; dedup findFirst branches on interaction.
- moveNode drops the orphan-reject (keeps the cycle-reject).
- deleteNode/restoreNode drop the Flow arms and gain a Spec sweep; deleteEdge
  reverts to a lone soft-delete. Delete flow.service / flow-route.service /
  flow-parser and the routeFlow cross-scope writer.
- getCanvas reduced to { interiorNodes, interiorEdges, breadcrumbs } via a single
  both-endpoints relation filter (no waterfall); cross-scope rendering is #63.
- prisma-errors: matcher accepts both new index names; drop dead Flow matchers.
- export.service / markdown: minimal seam for the dropped canvasNodeId (subtree
  + boundary CTEs become endpoint-membership; golden re-baselined). #67 owns the
  full typed-export rewrite.

MCP / API
- connect_components + apply_graph drop canvasNode, gain interaction, accept
  cross-scope endpoints. Remove the 9 flow/flowSpec/flowRoute tRPC procedures.

Client
- Remove dead Flow UI (boundary-group, boundary-proxy, route-flow popover, flow
  palette, attach-spec, flow pill, flow: handle branch, flow-direction,
  flow-interaction-display, spec-kinds). Connections render as plain lines;
  typed arrowheads are #65.

Docs (travel with the slice)
- Write ADR-0027 (Connection carries its own interaction), ADR-0028 (cross-scope
  + lineal=ingress; same-Canvas retired), ADR-0030 (cascade/undo without
  FlowRoutes). Amend ADR-0005/0010/0023/0024/0026. Amend/tombstone CONTEXT.md.

pnpm check green; db:check clean; service tests cover cross-scope, lineal,
self-link reject, dedup (both indexes), move-no-orphan, deleteEdge lone, and the
Spec cascade sweep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
infinite-docs Ready Ready Preview, Comment Jun 2, 2026 12:17am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Retires Flow/FlowRoute; adds Edge.interaction and Spec model; shifts edge scope to endpoint-derived cross-scope model; updates dedup to interaction-aware partial unique indexes; removes Flow parser/service/UI; simplifies Canvas/UI to interior nodes and edges; updates migrations, services, API, tests, and docs.

Changes

Migration & Schema

Layer / File(s) Summary
Prisma schema and migration
prisma/schema.prisma, prisma/migrations/20260601120000_retire_flow_model/migration.sql, prisma/migrations/20260601233356_spec_owner_live_partial_unique/migration.sql
Adds Interaction/SpecKind enums and Spec model; adds Edge.interaction defaulting to ASSOCIATION; removes Edge.canvasNodeId, drops Flow/FlowRoute/FlowSpec tables; creates two partial unique indexes (directional and unordered ASSOCIATION); makes Spec owner uniqueness partial on live rows.
Docs & ADRs
CONTEXT.md, docs/adr/*
CONTEXT.md and ADRs updated/added to document Connection.interaction, cross-scope/lineal connections, dedupe semantics, and cascade/undo changes (ADRs 0027/0028/0030 and amendments).

Server: services, API, schemas

Layer / File(s) Summary
Edge service & dedup
src/server/architecture/edge.service.ts, src/server/architecture/prisma-errors.ts
Adds activeDuplicateWhere (ASSOCIATION unordered vs directional ordered); connectNodes accepts/persists interaction; deleteEdge simplified to lone soft-delete; restoreEdge pre-checks interaction-aware dedupe; prisma error matcher accepts new index names.
Node service & cascade
src/server/architecture/node.service.ts
getCanvas returns only interiorNodes/interiorEdges/breadcrumbs; moveNode drops orphan-reject while keeping cycle check; deleteNode/restoreNode sweep edges by endpoint-membership and stamp/restore owned Specs; returns now include specIds.
Schemas & API wiring
src/lib/schemas.ts, src/server/api/routers/architecture.ts, src/server/architecture/apply-graph.service.ts
Adds interaction Zod enum and Interaction type; connectNodesInput/applyGraphConnectionInput remove canvasNode and add interaction defaulting to ASSOCIATION; architecture router removes Flow TRPC endpoints and updates deleteEdge mutation; apply_graph no longer resolves canvasNode.

Client UI

Layer / File(s) Summary
Canvas interior rendering
src/app/p/[slug]/_canvas/canvas.tsx
Canvas renders only interior nodes/edges; toRFEdge no longer computes arrowheads here; optimistic edges include interaction default; handleConnect persists connections directly via connectNodes and reconciles optimistic edges.
Detail panel & nodes
src/app/p/[slug]/_canvas/component-detail-panel.tsx, src/app/p/[slug]/_canvas/component-node.tsx
ComponentDetailPanel drops attach-spec/Flow palette UI, removes slug and onFlowCountChange props; ComponentNode removes flowCount and related UI.
Connection edge UI
src/app/p/[slug]/_canvas/connection-edge.tsx
ConnectionEdge simplified to label/edit only; removed Flow aggregation UI and routing types; marker props passed through.
Boundary node & routing UI removed
src/app/p/[slug]/_canvas/boundary-proxy-node.tsx, src/app/p/[slug]/_canvas/boundary-group-node.tsx, src/app/p/[slug]/_canvas/route-flow-popover.tsx
Files removed along with routing/handle helpers and related types.

Flow infra removal & tests

Layer / File(s) Summary
Flow service & parsers removed
src/server/architecture/flow.service.ts, src/server/architecture/flow-parser/*, src/lib/flow-direction.ts, src/lib/flow-interaction-display.ts, src/lib/spec-kinds.ts
Removes Flow service, parser registry, individual parsers, and Flow-related client helpers/exports.
Tests & fixtures
src/server/architecture/__tests__/*
Flow-related test suites removed; edge/node tests updated to assert default ASSOCIATION, cross-scope/lineal cases, new dedupe expectations, lone delete/restore semantics; markdown export fixtures updated to drop canvas annotations. Test DB reset updated to truncate Spec.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Canvas
  participant ArchitectureAPI
  participant EdgeService
  participant DB
  Client->>Canvas: user draws connection (source,target)
  Canvas->>ArchitectureAPI: connectNodes(projectId, sourceId, targetId, interaction="ASSOCIATION")
  ArchitectureAPI->>EdgeService: connectNodes(input)
  EdgeService->>DB: findFirst(activeDuplicateWhere(...))
  DB-->>EdgeService: duplicate? / none
  EdgeService->>DB: create Edge with interaction
  DB-->>EdgeService: created Edge
  EdgeService-->>ArchitectureAPI: created Edge
  ArchitectureAPI-->>Canvas: return real edge -> reconcile optimistic
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • #63 — Cross-scope rendering / boundary-proxy work: this PR implements cross-scope Connections and defers rendering to follow-up #63.
  • #65 — Interaction-derived arrowheads: this PR adds Edge.interaction and interaction semantics required by #65.
  • #66 — Cross-scope "Connect to…" gesture: this PR updates connect/create paths and input schema (interaction + cross-scope) needed by #66.

Possibly related PRs

🐰 Farewell Flows, hello Interaction!
Specs planted seeds, edges now speak—ASSOCIATION first, arrows peek.
Cross-scope hops, canvas grows light,
a rabbit cheers the cleaner sight. ✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/retire-flow-typed-connections

@CuriouslyCory

Copy link
Copy Markdown
Owner Author

✅ Final clean service-test run: 183/183 passed (10/10 files). The two earlier misses were confirmed transient resetDb() TRUNCATE hook timeouts (Neon latency) — they pass on a clean run. pnpm check and pnpm db:check remain green.

@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: 2

🧹 Nitpick comments (2)
src/server/architecture/__tests__/markdown-export.test.ts (1)

121-126: ⚡ Quick win

Add an inherited boundary regression case to this fixture.

This subtree setup only exercises origin: "direct". Since the new boundary query has a separate descendant-owned path (is_direct = false), one child→external connection here would give both the pure serializer and the service test a regression case for that branch.

🤖 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/server/architecture/__tests__/markdown-export.test.ts` around lines 121 -
126, The fixture only exercises origin: "direct" for internal connections (see
the root.edges filter for ["n-api","n-auth","n-users"]); add a regression edge
that models a descendant-owned/inherited boundary path by inserting an edge
whose source is one of those child nodes (e.g., "n-api") and whose target is
external (not in ["n-api","n-auth","n-users"]) with the boundary fields set to
indicate a non-direct/inherited path (set is_direct: false or origin:
"inherited" and any descendant-owned flag your model uses). This ensures both
the pure serializer and service tests hit the descendant-owned (inherited)
branch.
src/server/architecture/__tests__/edge.service.test.ts (1)

211-229: 💤 Low value

Consider clarifying the test name.

The test creates REQUEST A→B, PUSH A→B, REQUEST B→A, and ASSOCIATION A→B (4 total edges), but the name "lets the four directional interactions and an association coexist" might suggest 4 distinct directional types plus 1 association = 5 edges. The test logic is correct; the name could be more precise (e.g., "lets directional interactions on both ordered pairs plus association coexist").

🤖 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/server/architecture/__tests__/edge.service.test.ts` around lines 211 -
229, Rename the test description string in the it(...) block to clearly reflect
that there are directional interactions on both ordered pairs plus one
association (e.g., "lets directional interactions on both ordered pairs plus
association coexist") so it doesn't imply five edges; update the string literal
in the test that uses seedTwoRootNodes(), draw (the helper that calls
connectNodes), and the final expect on testDb.edge.count to match the clearer
wording while leaving the test logic (the four connectNodes calls and the expect
toBe(4)) unchanged.
🤖 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 `@prisma/schema.prisma`:
- Around line 307-323: Remove the unconditional uniqueness on Spec.ownerNodeId
in the Prisma model and enforce uniqueness only for non-deleted (live) rows:
delete the `@unique` attribute on ownerNodeId in model Spec (keeping deletedAt for
soft-delete) and add a migration that drops the existing unique constraint/index
created for ownerNodeId and replaces it with a partial/filtered unique index
that applies only when deletedAt IS NULL (i.e., CREATE UNIQUE INDEX ... ON
"Spec" ("ownerNodeId") WHERE "deletedAt" IS NULL). Ensure the migration updates
any DB-level constraint names created earlier so new Specs can be created for a
node while a tombstoned Spec row remains.

In `@src/server/architecture/export.service.ts`:
- Around line 187-199: The recursive CTE "subtree" in export.service.ts needs
the same recursion guard used elsewhere: introduce a depth column and enforce
SUBTREE_DEPTH_CAP when walking Node rows for canvasNodeId and projectId; e.g.,
seed the CTE with depth = 0 and in the recursive branch only select children
when depth < SUBTREE_DEPTH_CAP, incrementing depth on each recursion so the CTE
stops at SUBTREE_DEPTH_CAP and prevents unbounded recursion caused by cycles or
bad data.

---

Nitpick comments:
In `@src/server/architecture/__tests__/edge.service.test.ts`:
- Around line 211-229: Rename the test description string in the it(...) block
to clearly reflect that there are directional interactions on both ordered pairs
plus one association (e.g., "lets directional interactions on both ordered pairs
plus association coexist") so it doesn't imply five edges; update the string
literal in the test that uses seedTwoRootNodes(), draw (the helper that calls
connectNodes), and the final expect on testDb.edge.count to match the clearer
wording while leaving the test logic (the four connectNodes calls and the expect
toBe(4)) unchanged.

In `@src/server/architecture/__tests__/markdown-export.test.ts`:
- Around line 121-126: The fixture only exercises origin: "direct" for internal
connections (see the root.edges filter for ["n-api","n-auth","n-users"]); add a
regression edge that models a descendant-owned/inherited boundary path by
inserting an edge whose source is one of those child nodes (e.g., "n-api") and
whose target is external (not in ["n-api","n-auth","n-users"]) with the boundary
fields set to indicate a non-direct/inherited path (set is_direct: false or
origin: "inherited" and any descendant-owned flag your model uses). This ensures
both the pure serializer and service tests hit the descendant-owned (inherited)
branch.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d4b159d-1869-41f2-8be4-682ee86b7171

📥 Commits

Reviewing files that changed from the base of the PR and between 6f8e2f4 and 8264a7c.

📒 Files selected for processing (49)
  • CONTEXT.md
  • docs/adr/0005-edge-scope-and-service-enforced-invariants.md
  • docs/adr/0010-edge-dedup-partial-unique-index.md
  • docs/adr/0023-connection-direction-derived-from-flows.md
  • docs/adr/0027-connection-carries-its-own-interaction.md
  • docs/adr/0028-cross-scope-connections-lineal-ingress.md
  • docs/adr/0030-cascade-undo-without-flowroutes.md
  • prisma/migrations/20260601120000_retire_flow_model/migration.sql
  • prisma/schema.prisma
  • src/app/p/[slug]/_canvas/boundary-group-node.tsx
  • src/app/p/[slug]/_canvas/boundary-proxy-node.tsx
  • src/app/p/[slug]/_canvas/canvas.tsx
  • src/app/p/[slug]/_canvas/component-detail-panel.tsx
  • src/app/p/[slug]/_canvas/component-node.tsx
  • src/app/p/[slug]/_canvas/connection-edge.tsx
  • src/app/p/[slug]/_canvas/route-flow-popover.tsx
  • src/lib/flow-direction.ts
  • src/lib/flow-interaction-display.ts
  • src/lib/schemas.ts
  • src/lib/spec-kinds.ts
  • src/lib/types.ts
  • src/server/api/routers/architecture.ts
  • src/server/architecture/__tests__/apply-graph.service.test.ts
  • src/server/architecture/__tests__/edge.service.test.ts
  • src/server/architecture/__tests__/fixtures/export-project-full.md
  • src/server/architecture/__tests__/fixtures/export-subtree-full.md
  • src/server/architecture/__tests__/flow-parser.test.ts
  • src/server/architecture/__tests__/flow-route.service.test.ts
  • src/server/architecture/__tests__/flow.service.test.ts
  • src/server/architecture/__tests__/helpers/test-db.ts
  • src/server/architecture/__tests__/markdown-export.test.ts
  • src/server/architecture/__tests__/node.service.test.ts
  • src/server/architecture/apply-graph.service.ts
  • src/server/architecture/edge.service.ts
  • src/server/architecture/errors.ts
  • src/server/architecture/export.service.ts
  • src/server/architecture/flow-parser/index.ts
  • src/server/architecture/flow-parser/parsers/asyncapi.ts
  • src/server/architecture/flow-parser/parsers/graphql.ts
  • src/server/architecture/flow-parser/parsers/openapi.ts
  • src/server/architecture/flow-parser/parsers/sql-ddl.ts
  • src/server/architecture/flow-parser/parsers/ts-signature.ts
  • src/server/architecture/flow-parser/shared.ts
  • src/server/architecture/flow-route.service.ts
  • src/server/architecture/flow.service.ts
  • src/server/architecture/markdown.ts
  • src/server/architecture/node.service.ts
  • src/server/architecture/prisma-errors.ts
  • src/server/mcp/tool-catalog.ts
💤 Files with no reviewable changes (19)
  • src/server/architecture/flow-route.service.ts
  • src/lib/flow-direction.ts
  • src/lib/flow-interaction-display.ts
  • src/app/p/[slug]/_canvas/boundary-group-node.tsx
  • src/server/architecture/flow-parser/parsers/openapi.ts
  • src/server/architecture/tests/flow-parser.test.ts
  • src/lib/spec-kinds.ts
  • src/app/p/[slug]/_canvas/route-flow-popover.tsx
  • src/server/architecture/flow-parser/parsers/graphql.ts
  • src/server/architecture/flow-parser/index.ts
  • src/app/p/[slug]/_canvas/boundary-proxy-node.tsx
  • src/server/architecture/tests/flow-route.service.test.ts
  • src/app/p/[slug]/_canvas/component-node.tsx
  • src/server/architecture/flow-parser/parsers/ts-signature.ts
  • src/server/architecture/flow.service.ts
  • src/server/architecture/tests/flow.service.test.ts
  • src/server/architecture/flow-parser/parsers/asyncapi.ts
  • src/server/architecture/flow-parser/shared.ts
  • src/server/architecture/flow-parser/parsers/sql-ddl.ts

Comment thread prisma/schema.prisma
Comment thread src/server/architecture/export.service.ts
…y unique

Consolidated findings from a backend-architect review, a senior-engineer
review, and CodeRabbit on PR #68. Addresses correctness, prod-safety, and
stale agent/client-facing docs orphaned by the Flow retirement.

Correctness
- export.service: add the missing SUBTREE_DEPTH_CAP guard to the boundary
  CTE; its two sibling CTEs already had it, so a parent cycle (bad data or a
  reparent regression) could have hung the export while the others failed
  loud. Refresh the cap comment now that moveNode exists.
- Spec ownership: drop the all-rows `ownerNodeId @unique` and replace with a
  live-only partial unique index `idx_spec_owner_live` (WHERE deletedAt IS
  NULL), mirroring how Edge de-dupe lives in partial indexes. The old
  constraint reserved the owner across tombstones, blocking re-attach after
  undo and making restoreNode's conflictingSpecIds path unreachable. The
  Prisma model relation becomes a list because @unique is also the arity
  declaration; the singular accessor `ownedSpec` was never traversed.
  New migration 20260601233356_spec_owner_live_partial_unique authored via
  pnpm db:author + hand-edited partial index (ADR-0010 pattern).

Robustness / prod-safety
- retire_flow migration: IF EXISTS on the DROPs as best-effort re-run
  defense for hand-touched prod DBs. Document that this is NOT full
  idempotency (FK drops target tables this migration also drops) and that
  idx_edge_dedup is intentionally redefined under the same name.
- restoreNode: document the MUST-run-inside-$transaction contract — the
  Edge revival can throw after the Node revival commits its statement, so
  atomicity depends on the caller's transaction rolling everything back.

Agent / client doc-drift (orphaned by the model change, outside the diff)
- llms.txt: drop the retired same-Canvas invariant from the MCP tools
  blurb; rename Flow titles -> Spec source in the trust-boundary list.
- connection-rules: rewrite header + canConnect docstrings to ADR-0027/0028
  and the ASSOCIATION (unordered) rule this helper actually mirrors. Add a
  #65 forward-note that directional interactions de-dupe on the ordered
  triple, so this helper's existence-only check must grow an interaction
  arm then.

P3 cleanups
- Stale comments: updateNodeKind ("no Edge, Flow, or FlowRoute" -> "no Edge
  or Spec"); component-node DescendComponentContext doc disambiguates "the
  flow's double-click" -> "React Flow's double-click".
- Test renames: edge.service "four directional interactions" -> "directional
  interactions on both ordered pairs plus an association coexist";
  connection-rules.test "undirected; ADR-0023" -> "unordered pair; ADR-0027".
- Inherited-boundary regression case (CodeRabbit): added n-analytics +
  n-users->n-analytics edge to the export fixture so subtree exports surface
  Analytics API as an "inherited" boundary proxy (is_direct = false),
  covering the previously-untested descendant-owned branch in both the pure
  serializer and the real-DB service test. Goldens regenerated.

Validation
- pnpm check (eslint + tsc): green
- pnpm db:check (drift gate): green (partial index invisible to Prisma, by
  design — same posture as the Edge dedup indexes)
- pnpm test: 183/183, 10/10 files

Deferred (per the approved plan; tracked for follow-up)
- P2 perf: connectNodes/apply_graph read waterfalls, updatePositions
  N-UPDATEs. Pre-existing; ties to ADR-0026 *_unauthorized extraction.

Co-Authored-By: Claude Opus 4.7 <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.

Cross-scope connections: retire Flow model; typed connectNodes

1 participant