{"record":{"id":"d2f0e38d1c94080a","repo":"gastownhall/beads","slug":"applygraph-edge-d-s-s-creates-a-blocking-reve","errorCode":null,"errorMessage":"applyGraph: edge %d %s->%s creates a blocking reverse of a parent-child relationship","messagePattern":"applyGraph: edge (.+?) (.+?)->(.+?) creates a blocking reverse of a parent-child relationship","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue.go","lineNumber":1193,"sourceCode":"\t// for transitive hierarchy visibility, not for deferring structural errors.\n\tfor i, edge := range plan.Edges {\n\t\tfromID := resolveEdgeRef(edge.FromKey, edge.FromID, keyToID)\n\t\tif fromID == \"\" {\n\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: edge %d references undefined from_key %q\", i, edge.FromKey)\n\t\t}\n\t\ttoID := resolveEdgeRef(edge.ToKey, edge.ToID, keyToID)\n\t\tif toID == \"\" {\n\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: edge %d references undefined to_key %q\", i, edge.ToKey)\n\t\t}\n\t\tdepType := edge.Type\n\t\tif depType == \"\" {\n\t\t\tdepType = types.DepBlocks\n\t\t}\n\t\tif parentDepPairs[depPairKey(fromID, toID)] && depType != types.DepParentChild {\n\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: edge %d %s->%s duplicates a parent-child relationship with dependency type %q\", i, fromID, toID, depType)\n\t\t}\n\t\tif parentDepPairs[depPairKey(toID, fromID)] && cycleRelevantDepType(depType) {\n\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: edge %d %s->%s creates a blocking reverse of a parent-child relationship\", i, fromID, toID)\n\t\t}\n\t}\n\n\t// Pass 3 — insert node parent-child deps now that all IDs are known. These\n\t// must be visible before any blocking edge in the same plan so the storage\n\t// hierarchy guard evaluates existing + planned ancestry.\n\tfor _, node := range plan.Nodes {\n\t\tparentID := node.ParentID\n\t\tif node.ParentKey != \"\" {\n\t\t\tparentID = keyToID[node.ParentKey]\n\t\t}\n\t\tif parentID == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tchildID := keyToID[node.Key]\n\t\tdep := &types.Dependency{\n\t\t\tIssueID:     childID,\n\t\t\tDependsOnID: parentID,","sourceCodeStart":1175,"sourceCodeEnd":1211,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue.go#L1175-L1211","documentation":"applyGraph rejects any explicit edge that runs in the blocking direction opposite to an existing/planned parent-child relationship (child blocks parent) when the edge type is cycle-relevant. Such an edge would make a child gate its own ancestor, so the apply fails before any write.","triggerScenarios":"Plan includes edge child->parent (the reverse of a parent-child pair in parentDepPairs) with a scheduling type like \"blocks\" (or empty, which defaults to blocks).","commonSituations":"Swapped From/To fields when constructing the edge; re-parenting an issue without cleaning up old blocker edges; two agents concurrently creating parent and blocker edges.","solutions":["Swap FromKey/ToKey so the edge points parent->child, or remove it","Delete the stale blocker edge between the pair (bd dep remove) before re-parenting","Change the type to a non-scheduling edge (e.g. related) if blocking semantics are not intended","Re-run bd doctor / recompute the plan to detect the inverted relationship"],"exampleFix":"// before\nGraphEdge{FromKey: \"bd-2\", ToKey: \"bd-1\", Type: \"blocks\"} // bd-1 is parent of bd-2\n// after\nGraphEdge{FromKey: \"bd-1\", ToKey: \"bd-2\", Type: \"blocks\"} // or remove the edge","handlingStrategy":"validation","validationCode":"for _, e := range plan.Edges {\n    if parentPairs[depPairKey(e.ToKey, e.FromKey)] && cycleRelevant(e.Type) {\n        return fmt.Errorf(\"edge %s->%s blocks its own ancestor\", e.FromKey, e.ToKey)\n    }\n}","typeGuard":"func isBlockingReverse(e GraphEdge, parentPairs map[string]bool) bool {\n    return parentPairs[depPairKey(e.ToKey, e.FromKey)] && cycleRelevantDepType(e.Type)\n}","tryCatchPattern":null,"preventionTips":["Double-check From/To ordering when an endpoint is a parent","Strip stale blocker edges before re-parenting issues","Track ancestor sets client-side and reject child->parent scheduling edges","Run bd doctor to detect inverted relationships early"],"tags":["graph-apply","cycle","parent-child"],"backgroundTag":"dependency-cycle","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}