Skip to content

Cross-prefix bd dep add silently downgrades a bead ref to an external ref, and external deps are invisible to dep list/tree/ready (blocked beads offered as ready) #5784

Description

@Madhansamy-code

Summary

bd dep add accepts a dependency between beads in two different rigs, prints a
success line, and persists nothing. The edge is absent on read-back from both
the authoring path and the consumer path. No error, no non-zero exit.

The silent part is the real defect: a caller building a dependency graph gets a
tick on every call and ends up with a graph that does not contain the edges. In
our case a normative requirement said "these cross-rig dependencies must exist",
every command reported success, and the graph had none of them.

Environment

  • bd version 1.1.2 (20e493e56: main@20e493e569c9)
  • macOS (arm64)
  • One town, two rigs added with gt rig add, distinct prefixes, separate bead
    stores. Both prefixes resolve from the town root.

Steps to reproduce

Two rigs in one town — call the prefixes aaa and bbb. From the town root,
where both prefixes resolve:

$ bd show aaa-100          # resolves
$ bd show bbb-200          # resolves  -> not a resolution problem

$ bd dep add aaa-100 bbb-200
✓ Added dependency: aaa-100 depends on bbb-200 (blocks)

$ bd show aaa-100 --json | jq '.[0].dependencies'
[]                          # from the town root

$ cd <rig-aaa> && bd show aaa-100 --json | jq '.[0].dependencies'
[]                          # from inside the owning rig, i.e. the consumer path

Exit code is 0 throughout.

Control (rules out a bad read-back method)

The identical command form within one rig persists correctly:

$ bd dep add aaa-101 aaa-100
$ bd show aaa-101 --json | jq '.[0].dependencies'
[{"id": "aaa-100", "dependency_type": "blocks"}]

So the read-back is sound and the failure is specific to crossing bead stores.
Both prefixes resolving from the town root also rules out prefix resolution.

Expected

Either:

  1. the cross-store edge persists and bd ready on the dependent side respects
    it; or
  2. the command fails loudly, non-zero, so the caller knows the constraint
    was not recorded.

Option 2 alone would have saved us the incident. Silent success on a write that
cannot be performed is the worst of the three possible behaviours, because the
graph then looks complete to every later reader.

Root-cause guess

Dependencies look to be stored per bead store, and the cross-store write is
dropped while the command reports on intent rather than on the result of the
write. Not verified — I have not read the source.

Related, found while working around it

bd refuses an epic → task dependency:

epics can only block other epics, not tasks

That is defensible on its own, but combined with the above it forces an
awkward shape: a stand-in bead that an epic must block on has to be typed
epic, even when it holds no children and represents no work. The result is an
epic with no children, which then looks like a modelling error to any structure
check. Worth considering whether the epic/task restriction should apply to
blocks edges specifically, or whether there should be a bead type intended for
markers of this kind.

Workaround in use

A proxy bead inside the dependent store, standing in for the bead in the other
store, with the correspondence recorded on both sides and the acceptance
criterion being "the named bead in the other store is closed, verified by
reading that store". It restores enforcement on one side only. The graph still
contains no cross-store edge, and we are recording that as knowingly unmet
rather than reporting it as satisfied.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions