Skip to content

Draw BLOCKS links between every ticket on a dependency graph - #349

Merged
shafty023 merged 2 commits into
mainfrom
fix/prd-dependency-graph-links-between-shown-tickets
Oct 26, 2026
Merged

shafty023 merged 2 commits into
mainfrom
fix/prd-dependency-graph-links-between-shown-tickets

Conversation

@shafty023

Copy link
Copy Markdown
Collaborator

Purpose

A PRD-800 dependency graph draws ISS-12711, ISS-12712 and ISS-12713 because the in-scope ticket ISS-12552 blocks all three. ISS-12711 and ISS-12712 also BLOCK ISS-12713 in ClosedLoop, but the graph left those two links out. So hovering ISS-12713 highlighted only ISS-12552 as its blocker. Turning on "Show the full chain of outside blockers" did not help, because that option only walks blockers upward from in-scope tickets.

Root cause: fetch_graph kept a BLOCKS link only when it touched an in-scope ticket, or when it led into an outside blocker the walk had queried. It never read the links of downstream tickets. At the default depth of 1 it did not read the outside blockers' links either. So a link between two drawn tickets outside the scope could never appear.

Change

Affected plugin: closedloop-core (1.5.2 to 1.5.3).

  • skills/prd-dependency-graph/scripts/refresh_server.py: once the walk has decided which tickets to draw, fetch_graph reads the BLOCKS links of every drawn ticket it has not already read. It then draws every link whose two ends are both on the graph. The lists the walk already read are reused, so each drawn ticket's links are read once, on the same 8-worker pool and the same paged /artifact-links/resolved call. The pass adds links only. Which tickets are drawn is unchanged. fetch_graph is the only place link data is built: --new, --once, the refresh button and the outside-blockers checkbox all go through it, and the page's JavaScript only renders what the helper returns. So a refresh cannot drop these links again.
  • SKILL.md: the "What counts as a dependency" section now covers links between tickets on the graph.
  • Version bump in both plugin manifests, plus a CHANGELOG entry.

Tests

  • New tools/python/test_prd_dependency_graph_links.py covers the reported shape. Scope ticket A blocks X, Y and Z, which are all outside the scope, and Y blocks Z. Two outside blockers block A; one of them also blocks the other and Y. Two unrelated tickets touch Z only. The tests run at depths 1, all and 0, on a new page, after a refresh from the page, and after switching the outside-blockers option on. They assert that Y -> Z is in the data and in the rendered arrows, which the page builds its hover chains from, and that Z's upstream chain includes Y. They also check that the legend's "N BLOCKS links, all drawn" count equals the edge count, that no new tickets appear, and that each drawn ticket's links are read exactly once. All 5 cases fail on main and pass on this branch.
  • test_launcher_follows_plugin_update_after_old_cache_is_removed hardcoded "1.5.3" as the newer cached version. With the source now at 1.5.3 that tied, and the launcher picks the source on a tie, so it ran the real helper's --ensure instead. The test now uses a version newer than the checkout's own. What it checks has not changed.
  • conftest.py: the fake API records which tickets' BLOCKS links were read.

Commands run:

uv run ruff check .        # all checks passed
uv run pyright             # 0 errors
uv run pytest plugins/     # 2514 passed, 1 timeout (see below)
uv run pytest plugins/closedloop-core/tools/python/   # 94 passed
node --test --test-reporter=spec $(git ls-files 'plugins/**/*.test.{mjs,cjs,js}')   # 803 of 806 passed
node scripts/check_graph.cjs <regenerated PRD-800 page> ISS-12713   # hover: 3 upstream, 0 errors

The full pytest and Node runs overlapped on one machine. The 4 failures were all timeouts in suites this branch does not touch: test_install_script.py::test_repairs_project_scoped_plugin_from_project_path (20s subprocess timeout) and 3 vibe cl-sweep app-server tests. All 4 pass when run on their own. This branch changes no JS files.

I regenerated PRD-800 against the live API (depth 1 and all). It draws the same 14 nodes as before, with 21 BLOCKS links instead of 13. ISS-12713 is now blocked by ISS-12552, ISS-12711 and ISS-12712, and the headless check confirms that hovering it lights all three.

- fetch_graph now reads the BLOCKS links of every drawn ticket the
  walk did not already read, and draws each link whose two ends are
  both on the graph. A ticket the scope blocks that also blocks
  another such ticket (ISS-12711 and ISS-12712 blocking ISS-12713 on
  PRD-800) now gets its arrow and appears in hover and pinned chains.
- Links already read during the walk are reused, so each drawn ticket
  is read once. No ticket is added to the graph.
- New pages, --once, the refresh button and the outside blockers
  option all build links through fetch_graph, so a refresh keeps them.
- Tests cover the A blocks X, Y, Z and Y blocks Z shape at depths 1,
  all and 0, a page refresh, and the option toggle. The launcher test
  no longer hardcodes the next plugin version.
- Bump closedloop-core to 1.5.3 and add a changelog entry.

Testing: ruff, pyright, pytest plugins/, node --test over plugins,
regenerated PRD-800 against the live API and ran check_graph.cjs on it

Risks: one extra paged links call per downstream ticket and, at depth
1, per outside blocker
@shafty023
shafty023 merged commit d95a8ab into main Oct 26, 2026
10 checks passed
@shafty023
shafty023 deleted the fix/prd-dependency-graph-links-between-shown-tickets branch October 26, 2026 20:28
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