fix(apply): bound the blast-radius subtree down-walk CTE
`group_blast_radius`'s `app_chain` up-walk is depth-capped (`ac.depth < 64`, added by76926deto stop a should-be-impossible `groups.parent_id` cycle from spinning the recursive CTE forever), but the sibling `subtree` down-walk in the same query was left unbounded. A `parent_id` cycle would loop `subtree` indefinitely and hang the plan — the exact exposure76926deset out to close, left half-done on the descendant side. Add a `depth` column + `WHERE s.depth < 64` to `subtree`, mirroring `app_chain`. Defense-in-depth: the reparent cycle-guard already prevents `parent_id` cycles, so this hardens against an unreachable state — but that is precisely the bar76926deapplied. Surfaced by a post-adoption audit of the §7/§6 ownership code. Verified: 416 manager-core lib tests; 12 ownership/create/divergence/approval journeys (incl. plan_previews_ownership_and_blast_radius, which drives this query); clippy -D warnings + fmt clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2739,9 +2739,10 @@ impl ApplyService {
|
||||
// `exclude` (the planning project) or by nothing are dropped.
|
||||
let rows: Vec<(String, i64)> = sqlx::query_as(
|
||||
"WITH RECURSIVE subtree AS (
|
||||
SELECT id FROM groups WHERE id = $1
|
||||
SELECT id, 0 AS depth FROM groups WHERE id = $1
|
||||
UNION ALL
|
||||
SELECT g.id FROM groups g JOIN subtree s ON g.parent_id = s.id
|
||||
SELECT g.id, s.depth + 1 FROM groups g JOIN subtree s ON g.parent_id = s.id
|
||||
WHERE s.depth < 64
|
||||
),
|
||||
app_chain AS (
|
||||
SELECT a.id AS app_id, a.group_id AS gid, 0 AS depth
|
||||
|
||||
Reference in New Issue
Block a user