{"record":{"id":"afe50877ddab8e63","repo":"gastownhall/beads","slug":"dependency-s-s-crosses-ephemeral-storage","errorCode":null,"errorMessage":"dependency %s -> %s crosses ephemeral storage","messagePattern":"dependency (.+?) -> (.+?) crosses ephemeral storage","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":908,"sourceCode":"\t\treturn err\n\t}\n\tif err := checkCurrentVarchars(\n\t\tcurrentVarchar{\"dependency issue_id\", id, types.MaxFieldLen},\n\t\tcurrentVarchar{\"dependency depends_on_id\", to, types.MaxFieldLen},\n\t\tcurrentVarchar{\"dependency type\", typ, currentShortVarcharRunes},\n\t\tcurrentVarchar{\"dependency created_by\", by, types.MaxFieldLen},\n\t); err != nil {\n\t\treturn err\n\t}\n\tif by == \"\" {\n\t\treturn fmt.Errorf(\"dependency created_by is empty for %s -> %s\", id, to)\n\t}\n\tissue := byID[id]\n\tif issue == nil || byID[to] == nil {\n\t\treturn fmt.Errorf(\"orphan dependency %s -> %s\", id, to)\n\t}\n\tif issue.Ephemeral != byID[to].Ephemeral {\n\t\treturn fmt.Errorf(\"dependency %s -> %s crosses ephemeral storage\", id, to)\n\t}\n\tkey := id + \"\\x00\" + to\n\tif seenDeps[key] {\n\t\treturn fmt.Errorf(\"multiple legacy dependencies for %s -> %s\", id, to)\n\t}\n\tseenDeps[key] = true\n\tcreated, e := parseTime(at)\n\tif e != nil {\n\t\treturn e\n\t}\n\tif created.IsZero() {\n\t\treturn fmt.Errorf(\"dependency created_at is zero for %s -> %s\", id, to)\n\t}\n\tif (metadata.Valid && metadata.String != \"\") || (thread.Valid && thread.String != \"\") {\n\t\treturn fmt.Errorf(\"dependency %s -> %s uses unsupported metadata or thread ID\", id, to)\n\t}\n\tif !types.DependencyType(typ).IsValid() {\n\t\treturn fmt.Errorf(\"dependency %s -> %s has invalid type\", id, to)","sourceCodeStart":890,"sourceCodeEnd":926,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L890-L926","documentation":"A dependency edge connects an ephemeral issue to a non-ephemeral issue (their Ephemeral flags differ). The current storage model separates ephemeral wisps from durable issues, and cross-boundary dependencies are unsupported, so the reader rejects such edges during legacy migration instead of producing an invalid graph.","triggerScenarios":"appendLegacyDependencyRow loads both endpoint issues and finds issue.Ephemeral != byID[to].Ephemeral for the id -> to pair — i.e. one endpoint lives in ephemeral storage and the other in durable storage.","commonSituations":"Legacy databases written before ephemeral/durable separation was enforced at write time, allowing mixed edges; manual merging of an ephemeral DB with a durable one; tools that created dependencies across the boundary before the rule existed.","solutions":["Delete the cross-boundary edge in the legacy DB before migrating: DELETE FROM dependencies WHERE issue_id = '<id>' AND depends_on_id = '<to>'.","If the edge is needed, change the ephemeral classification of one endpoint (move the issue into the matching storage class) so both endpoints agree.","Audit for all such edges pre-migration by comparing each dependency's endpoints' ephemeral flags.","Check how the mixed edge was created (old bd version or manual merge) and prevent recurrence with the current write-time enforcement."],"exampleFix":"// before: ephemeral -> durable edge\nDELETE FROM dependencies WHERE issue_id = 'bd-abc' AND depends_on_id = 'bd-xyz';\n// after: no cross-storage edges remain","handlingStrategy":"validation","validationCode":"// verify both endpoints of every edge share the same ephemeral flag\nrows, err := legacyDB.Query(`SELECT d.issue_id, d.depends_on_id FROM dependencies d\n  JOIN issues a ON a.id = d.issue_id\n  JOIN issues b ON b.id = d.depends_on_id\n  WHERE a.ephemeral != b.ephemeral`)\n// delete or reclassify these edges before migrating","typeGuard":"func sameStorageClass(a, b *types.Issue) bool {\n    return a.Ephemeral == b.Ephemeral\n}","tryCatchPattern":"if err := migrateLegacy(db); err != nil {\n    if strings.Contains(err.Error(), \"crosses ephemeral storage\") {\n        return fmt.Errorf(\"remove cross-boundary dependency edges and retry: %w\", err)\n    }\n    return err\n}","preventionTips":["Never create dependencies between ephemeral and durable issues; rely on current write-time enforcement.","When merging databases, keep ephemeral and durable issues in separate dependency graphs.","Audit edges by comparing endpoint ephemeral flags before any migration."],"tags":["migration","sqlite","dependency","ephemeral-storage"],"backgroundTag":"cross-storage-dependency","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}