Skip to content

Add reachable issue graph API#147

Draft
mariusvniekerk wants to merge 5 commits into
mainfrom
reachable-issue-graph-api
Draft

Add reachable issue graph API#147
mariusvniekerk wants to merge 5 commits into
mainfrom
reachable-issue-graph-api

Conversation

@mariusvniekerk

@mariusvniekerk mariusvniekerk commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Middleman currently has to reconstruct canonical kata relationship semantics in frontend TypeScript before it can render the reachable graph. That puts backend concerns like relationship direction, reciprocal dedupe, missing endpoint preservation, stable ordering, and layout-only transitive block pruning in the browser layer.

This PR moves the canonical reachable graph read model into the daemon. Clients can fetch a source issue graph with bounded or full depth and optional done filtering, then focus on rendering, local emphasis, viewport state, and browser layout concerns without owning kata graph semantics.

The branch is based on current mainline storage abstractions, so graph traversal reads links through db.Storage rather than a SQLite-only project-link query. The OpenAPI and generated client artifacts are included so downstream consumers can call the new route directly.

Related: kenn-io/middleman#621.

Validation: env -u KATA_SERVER go test -shuffle=on ./internal/api ./internal/daemon; make lint; push hooks also ran make api-check, make lint, and nilaway.

mariusvniekerk and others added 4 commits June 30, 2026 11:55
Middleman needs canonical task graph semantics from kata instead of rebuilding relationship traversal and layout pruning entirely in frontend code. This adds a daemon-owned graph read surface that resolves from stable issue identity, normalizes relationship direction, preserves unresolved endpoints, and exposes layout-only block pruning without returning browser-specific rendering state.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
The reachable graph response has a stable ordering contract, and keeping that ordering embedded in anonymous sort closures made it harder to see which fields define that contract. Put the comparison logic on the graph DTOs and the local traversal neighbor type so callers sort through named semantics instead of duplicating field-by-field ordering at each site.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
Current mainline routes daemon handlers through the backend-neutral db.Storage contract, so the reachable graph implementation needs to avoid concrete SQLite queries. Reading links through LinksByIssue keeps the graph endpoint portable across storage backends and lets the generated OpenAPI/client artifacts reflect the new route on the branch reviewers will inspect.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
Replaying the graph endpoint onto current main removed the project_id link column and made the projectID helper arguments fixture-only. Naming them as intentionally unused keeps lint green without changing the graph behavior under test.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
@roborev-ci

roborev-ci Bot commented Jun 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (65c5f20)

Summary verdict: one medium correctness issue should be addressed before merge.

Medium

  • internal/daemon/handlers_graph.go:53: The graph preloads only issues from the source project, so valid cross-project links are treated as unresolved and are not traversed or returned as nodes. If cross-project nodes are later included, internal/daemon/handlers_graph.go:88 would also render their qualified_id with the source project name.
    • Fix: Hydrate linked issues across active projects as traversal discovers them, and compute each node’s qualified_id from that issue’s own project.

Panel: ci_default_security | Synthesis: codex, 11s | Members: codex_default (codex/default, done, 3m23s), codex_security (codex/security, done, 1m21s) | Total: 4m55s

Reachable graph traversal can encounter links whose other endpoint belongs to another active project. Treating those endpoints as unresolved made valid graph nodes disappear and would have forced callers back into frontend-side graph reconstruction.

Hydrating linked issues by ID keeps traversal aligned with the existing cross-project link model, while deriving qualified IDs from each node's owning project avoids labeling foreign issues with the source project name.

Validation: env -u KATA_SERVER go test -shuffle=on ./internal/daemon -run 'TestReachableGraph_TraversesCrossProjectLinksWithQualifiedIDs'; env -u KATA_SERVER go test -shuffle=on ./internal/api ./internal/daemon; git diff --check; make lint; make api-check

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
@roborev-ci

roborev-ci Bot commented Jun 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (c76dd07)

Medium finding: hidden graph endpoints are surfaced as unresolved refs.

  • Medium: internal/daemon/handlers_graph.go:311
    Hidden endpoints are treated the same as genuinely missing endpoints. hydrateGraphIssue returns ok=false for soft-deleted issues and issues in archived projects, and graphEdgesAndUnresolved then emits an edge plus unresolved_refs for that UID. Normal deletes/archives can make the active graph show phantom unresolved references instead of simply omitting hidden nodes.
    Fix: Distinguish “not found” from “found but hidden” during hydration, and only append unresolved_refs for genuinely missing/corrupt endpoints; omit deleted or archived endpoints from edges and traversal.

Panel: ci_default_security | Synthesis: codex, 7s | Members: codex_default (codex/default, done, 4m46s), codex_security (codex/security, done, 2m3s) | Total: 6m56s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant