{"record":{"id":"663a0097b266c2db","repo":"gastownhall/beads","slug":"blocking-dependency-s-s-conflicts-with-parent","errorCode":null,"errorMessage":"blocking dependency %s -> %s conflicts with parent-child hierarchy","messagePattern":"blocking dependency (.+?) -> (.+?) conflicts with parent-child hierarchy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":1013,"sourceCode":"\t\t\t}\n\t\t\tswitch dep.Type {\n\t\t\tcase types.DepBlocks, types.DepConditionalBlocks:\n\t\t\t\tblocking = append(blocking, dep)\n\t\t\t\tscheduling[dep.IssueID] = append(scheduling[dep.IssueID], dep.DependsOnID)\n\t\t\tcase types.DepParentChild:\n\t\t\t\thierarchy[dep.IssueID] = append(hierarchy[dep.IssueID], dep.DependsOnID)\n\t\t\t\tscheduling[dep.IssueID] = append(scheduling[dep.IssueID], dep.DependsOnID)\n\t\t\t}\n\t\t}\n\t}\n\tif hasDirectedCycle(scheduling) {\n\t\treturn fmt.Errorf(\"legacy SQLite dependency graph has a scheduling cycle\")\n\t}\n\tfor _, dep := range blocking {\n\t\tif types.ExtractPrefix(dep.IssueID) == types.ExtractPrefix(dep.DependsOnID) &&\n\t\t\t(reachable(hierarchy, dep.IssueID, dep.DependsOnID) ||\n\t\t\t\treachable(hierarchy, dep.DependsOnID, dep.IssueID)) {\n\t\t\treturn fmt.Errorf(\"blocking dependency %s -> %s conflicts with parent-child hierarchy\", dep.IssueID, dep.DependsOnID)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc hasDirectedCycle(graph map[string][]string) bool {\n\tindegree := make(map[string]int, len(graph))\n\tfor from, targets := range graph {\n\t\tif _, ok := indegree[from]; !ok {\n\t\t\tindegree[from] = 0\n\t\t}\n\t\tfor _, target := range targets {\n\t\t\tindegree[target]++\n\t\t}\n\t}\n\tqueue := make([]string, 0, len(indegree))\n\tfor id, degree := range indegree {\n\t\tif degree == 0 {","sourceCodeStart":995,"sourceCodeEnd":1031,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L995-L1031","documentation":"During legacy SQLite migration, dependencies are validated against the parent-child hierarchy. A blocking dependency between two issues that are also related as parent/child (in either direction) is rejected because it creates contradictory scheduling semantics. The migration aborts rather than importing an inconsistent graph.","triggerScenarios":"Migrating a legacy SQLite database that contains a blocking dependency (dep.IssueID -> dep.DependsOnID) where both endpoints share the same prefix and one is reachable from the other via parent-child edges (reachable(hierarchy, ...) in either direction).","commonSituations":"Users manually created blocking deps between a parent and its child (or child and parent) in an old beads database; older versions allowed this but current code forbids it; import data from third-party tooling that doesn't distinguish hierarchy from blocking edges.","solutions":["Open the legacy SQLite DB and delete or re-point the offending blocking dependency so it links non-hierarchical issues (find it with a query on the dependencies table matching the two IDs in the message).","Convert the parent-child relationship to the dependency instead: if the intent was ordering, keep only the blocking dep between siblings and restructure the hierarchy.","Re-run the migration after fixing the dependency rows."],"exampleFix":"// before (legacy DB)\n// issue mol-1 parent of mol-2; dependency mol-1 blocks mol-2\nDELETE FROM dependencies WHERE issue_id='mol-1' AND depends_on_id='mol-2';\n// after: dependency exists only between sibling issues, hierarchy kept intact","handlingStrategy":"validation","validationCode":"for _, dep := range blocking {\n    if types.ExtractPrefix(dep.IssueID) == types.ExtractPrefix(dep.DependsOnID) &&\n        (reachable(hierarchy, dep.IssueID, dep.DependsOnID) ||\n            reachable(hierarchy, dep.DependsOnID, dep.IssueID)) {\n        return fmt.Errorf(\"fix dependency %s -> %s before migration\", dep.IssueID, dep.DependsOnID)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never create blocking deps between parent and child issues","Run the pre-migration validation on a copy of the DB first","Audit legacy dependency tables for prefix-matching endpoints"],"tags":["migration","sqlite","data-validation","dependency-graph"],"backgroundTag":"dependency-conflicts-with-hierarchy","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}