fix: run the dependency cycle check before blocks_add writes an edge #848

Merged
hexajon merged 2 commits from issue-822-blocks-add-cycle-check into main 2026-10-20 17:18:37 +00:00
Owner

Summary

blocks_add ("issue A blocks issue B") wrote its edge with no cycle check. deps_add already checks the whole graph before it writes. The forge refuses only a cycle of two issues, so blocks_add could close a longer ring, such as A blocks B, B blocks C, and C blocks A, and the forge stored it.

The change:

  • Adding a block now lives in the deps service (internal/service/deps/block.go). "A blocks B" is the edge "B depends on A". AddBlockWith reads the existing dependencies of both issues, runs the same cycle check that deps_add runs, and returns a cycle error that names the path. It writes nothing on a cycle.
  • The CLI (issue sub blocks) and the MCP blocks_add action both call the deps service, so both surfaces refuse a cycle the same way.
  • The reference docs and the generated help say that blocks_add refuses a cycle.

Tests:

  • Unit tests for a ring of three, a cycle of two, and a clean edge. They use the existing deps test client.
  • A forge test against throwaway Forgejo and Gitea servers drives a three-issue ring through the blocks endpoint. It passed in the forge VM: ok madtea.lol/internal/service/deps 34.889s.

./scripts/gate.sh passes on this commit.

Closes #822

## Summary `blocks_add` ("issue A blocks issue B") wrote its edge with no cycle check. `deps_add` already checks the whole graph before it writes. The forge refuses only a cycle of two issues, so `blocks_add` could close a longer ring, such as A blocks B, B blocks C, and C blocks A, and the forge stored it. The change: - Adding a block now lives in the deps service (`internal/service/deps/block.go`). "A blocks B" is the edge "B depends on A". `AddBlockWith` reads the existing dependencies of both issues, runs the same cycle check that `deps_add` runs, and returns a cycle error that names the path. It writes nothing on a cycle. - The CLI (`issue sub` blocks) and the MCP `blocks_add` action both call the deps service, so both surfaces refuse a cycle the same way. - The reference docs and the generated help say that `blocks_add` refuses a cycle. Tests: - Unit tests for a ring of three, a cycle of two, and a clean edge. They use the existing deps test client. - A forge test against throwaway Forgejo and Gitea servers drives a three-issue ring through the blocks endpoint. It passed in the forge VM: `ok madtea.lol/internal/service/deps 34.889s`. `./scripts/gate.sh` passes on this commit. Closes #822
test: install the allow-all write guard in the blocks_add forge test
Some checks failed
drift / govulncheck (pull_request) Has been skipped
drift / surface-diff (pull_request) Successful in 32s
drift / forgejo-smoke (pull_request) Has been skipped
ci / mcp-conformance (pull_request) Successful in 1m26s
ci / mcp-conformance (push) Successful in -31s
drift / govulncheck (push) Has been skipped
drift / surface-diff (push) Successful in -39s
drift / forgejo-smoke (push) Has been skipped
ci / gate (push) Failing after 4m10s
ci / gate (pull_request) Failing after 15m38s
7a831a63a4
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea!848
No description provided.