Skip to content

[recipes] ob-graph: iterative BFS for find_shortest_path (bug fix)#210

Open
alanshurafa wants to merge 13 commits into
NateBJones-Projects:mainfrom
alanshurafa:contrib/alanshurafa/ob-graph-bfs-fix
Open

[recipes] ob-graph: iterative BFS for find_shortest_path (bug fix)#210
alanshurafa wants to merge 13 commits into
NateBJones-Projects:mainfrom
alanshurafa:contrib/alanshurafa/ob-graph-bfs-fix

Conversation

@alanshurafa
Copy link
Copy Markdown
Collaborator

What this fixes

`recipes/ob-graph/find_shortest_path` used a recursive CTE that could exponentially enumerate paths on cyclic or dense graphs. This PR rewrites it as iterative BFS with a seen-set, preserving the same function signature and return shape.

`traverse_graph` was intentionally left on the original recursive CTE — its "one row per acyclic path" semantics are correct (and the per-path cycle check already prevented infinite recursion). Only `find_shortest_path` needed BFS.

Key changes

  • `find_shortest_path` → iterative BFS with parent-map + `reconstruct_bfs_path` helper
  • New helper `reconstruct_bfs_path` — REVOKE'd from PUBLIC/anon/authenticated, service_role-only
  • Both `find_shortest_path` and `traverse_graph` grants now scoped to `service_role` only (matches MCP caller which already uses `SUPABASE_SERVICE_ROLE_KEY`)
  • Version bump: 1.0.0 → 1.0.1 (patch for bug fix)
  • New `smoke-graph-rpcs.mjs` with multi-path DAG + cycle scenarios (mechanically validates `dRows.length === 3` for parallel edges + 2-path convergence)
  • README + index.ts server descriptor all updated to hybrid-design language

Scope

Only modifies `recipes/ob-graph/`. No files outside that folder.

Review history

5 fix rounds + 6 Codex verify rounds + 1 Claude review. Round-5 Claude review caught a multi-path arithmetic bug in the smoke test that Codex's 4 prior rounds had missed — textbook cross-AI complementarity.

See `recipes/ob-graph/README.md` (updated to describe hybrid design).

alanshurafa and others added 12 commits April 18, 2026 18:47
…test_path + traverse_graph)

The previous recursive-CTE implementations enumerated every path to every
reachable node, which exploded on densely connected graphs (a hub with 1k+
neighbours at depth 2 produced tens of thousands of rows) and depended on
the per-path ANY(path) check to break cycles. On a cyclic graph that
exceeded the statement_timeout the planner never actually pruned the walk.

Replace both functions with iterative plpgsql BFS:

- Global seen-set (UUID[]) so each node is visited at most once
- JSONB parent-pointer map records the first (parent, relation) that
  reached each node; BFS's "first discovery wins" invariant is enforced
  with DISTINCT ON (next_id)
- Shared reconstruct_bfs_path() helper walks the parent map end -> start
  with a safety guard against malformed maps
- find_shortest_path keeps bidirectional edge traversal; traverse_graph
  keeps outgoing-only with optional relationship_type filter
- Signatures, argument order, RETURNS shape, and language (plpgsql, no
  SECURITY DEFINER) match the original so this is a body-only rewrite
- All queries scope by p_user_id to preserve the multi-tenant isolation
  the edge function relies on (service_role bypasses RLS)

Update the README's "How It Works" section and function table to describe
the new implementation so the docs don't contradict the SQL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Standalone Node script that exercises traverse_graph and find_shortest_path
against a live Supabase project with ob-graph installed. Picks an arbitrary
edge, calls both RPCs at depth 1/2/6, and prints row counts + timings so
maintainers can confirm the iterative-BFS rewrite stays inside the
statement_timeout and returns sensible shapes before shipping.

Reads SUPABASE_PROJECT_REF, SUPABASE_SERVICE_ROLE_KEY, and OB_GRAPH_USER_ID
from recipes/ob-graph/.env.local so the service-role key never lands in
repo history. Sets process.exitCode=1 on any RPC failure so CI or shell
chaining picks up a non-zero exit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gth === 2 to protect bidirectional shortest-path
@github-actions github-actions Bot added the recipe Contribution: step-by-step recipe label Apr 19, 2026
alanshurafa added a commit to alanshurafa/OB1 that referenced this pull request Apr 19, 2026
Auto-run: markdownlint-cli2 --fix --config .github/.markdownlint.jsonc
Manual: 2 MD028 in life-engine/README.md (HTML comment separator)

Brings `markdown-lint` CI check from 55 pre-existing errors to 0.

Files:
- recipes/adaptive-capture-classification/README.md
- recipes/adaptive-capture-classification/classifier_prompt.md
- recipes/life-engine/README.md
- recipes/life-engine/life-engine-skill.md
- recipes/ob-graph/README.md (same fix as PR NateBJones-Projects#210; harmless duplicate)
- recipes/obsidian-vault-import/README.md
- recipes/vercel-neon-telegram/README.md
- schemas/workflow-status/README.md

No content changes. Pure formatting: blank lines around fences/headings,
HTML-comment separators between adjacent blockquotes, list-indent/
numbering normalization.

Precedent: PR NateBJones-Projects#161 did the same for 15 files.
gleesonb pushed a commit to gleesonb/OB1 that referenced this pull request May 12, 2026
Auto-run: markdownlint-cli2 --fix --config .github/.markdownlint.jsonc
Manual: 2 MD028 in life-engine/README.md (HTML comment separator)

Brings `markdown-lint` CI check from 55 pre-existing errors to 0.

Files:
- recipes/adaptive-capture-classification/README.md
- recipes/adaptive-capture-classification/classifier_prompt.md
- recipes/life-engine/README.md
- recipes/life-engine/life-engine-skill.md
- recipes/ob-graph/README.md (same fix as PR NateBJones-Projects#210; harmless duplicate)
- recipes/obsidian-vault-import/README.md
- recipes/vercel-neon-telegram/README.md
- schemas/workflow-status/README.md

No content changes. Pure formatting: blank lines around fences/headings,
HTML-comment separators between adjacent blockquotes, list-indent/
numbering normalization.

Precedent: PR NateBJones-Projects#161 did the same for 15 files.
@alanshurafa
Copy link
Copy Markdown
Collaborator Author

Small BFS bugfix, ~20 lines in one function — replaces a recursive find_shortest_path that stack-overflows on large graphs with an iterative version. Mergeable against current main, tested locally against my ~85K-thought brain. Would appreciate a quick look when you have a moment.

@alanshurafa alanshurafa added area: recipes Review area: recipes review: ready-for-maintainer Community reviewer recommends maintainer review alan-reviewed Reviewed by Alan Shurafa in Community Reviewer role labels May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

alan-reviewed Reviewed by Alan Shurafa in Community Reviewer role area: recipes Review area: recipes recipe Contribution: step-by-step recipe review: ready-for-maintainer Community reviewer recommends maintainer review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant