{"record":{"id":"39c280b89222e254","repo":"gastownhall/beads","slug":"edge-d-gate-spawner-fields-require-type-q","errorCode":null,"errorMessage":"edge %d: gate/spawner fields require type %q","messagePattern":"edge (.+?): gate/spawner fields require type %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":613,"sourceCode":"\t\t}\n\t\tif edge.ToKey != \"\" && !seenKeys[edge.ToKey] {\n\t\t\treturn fmt.Errorf(\"edge %d: to key %q not found in plan\", i, edge.ToKey)\n\t\t}\n\t\tif edge.FromKey == \"\" && edge.FromID == \"\" {\n\t\t\treturn fmt.Errorf(\"edge %d: must specify from_key or from_id\", i)\n\t\t}\n\t\tif edge.ToKey == \"\" && edge.ToID == \"\" {\n\t\t\treturn fmt.Errorf(\"edge %d: must specify to_key or to_id\", i)\n\t\t}\n\t\tif edge.Type != \"\" {\n\t\t\tdt := types.DependencyType(edge.Type)\n\t\t\tif !dt.IsValid() {\n\t\t\t\treturn fmt.Errorf(\"edge %d: invalid dependency type %q\", i, edge.Type)\n\t\t\t}\n\t\t}\n\t\tif edge.Gate != \"\" || edge.SpawnerKey != \"\" || edge.SpawnerID != \"\" {\n\t\t\tif graphApplyDependencyType(edge.Type) != types.DepWaitsFor {\n\t\t\t\treturn fmt.Errorf(\"edge %d: gate/spawner fields require type %q\", i, types.DepWaitsFor)\n\t\t\t}\n\t\t\tif edge.Gate != \"\" && !types.IsValidWaitsForGate(edge.Gate) {\n\t\t\t\treturn fmt.Errorf(\"edge %d: invalid gate %q (valid: %s, %s)\", i, edge.Gate, types.WaitsForAllChildren, types.WaitsForAnyChildren)\n\t\t\t}\n\t\t\tif edge.SpawnerKey != \"\" && edge.SpawnerID != \"\" {\n\t\t\t\treturn fmt.Errorf(\"edge %d: cannot specify both spawner_key and spawner_id\", i)\n\t\t\t}\n\t\t\tif edge.SpawnerKey != \"\" && !seenKeys[edge.SpawnerKey] {\n\t\t\t\treturn fmt.Errorf(\"edge %d: spawner key %q not found in plan\", i, edge.SpawnerKey)\n\t\t\t}\n\t\t\t// Gate evaluation reads the spawner from the dependency target\n\t\t\t// (depends_on_id), not metadata, so the spawner must equal the to\n\t\t\t// endpoint. Since to_id overrides to_key at apply time\n\t\t\t// (resolveEdgeRef), a key-named spawner can't be combined with to_id.\n\t\t\tif edge.SpawnerKey != \"\" && edge.ToID != \"\" {\n\t\t\t\treturn fmt.Errorf(\"edge %d: spawner_key %q cannot be combined with to_id %q (to_id overrides to_key as the waits-for target; use spawner_id)\", i, edge.SpawnerKey, edge.ToID)\n\t\t\t}\n\t\t\tif edge.SpawnerKey != \"\" && edge.SpawnerKey != edge.ToKey {","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L595-L631","documentation":"Gate and spawner fields (gate, spawner_key, spawner_id) only have meaning for waits-for dependencies. If any of these fields is set on an edge whose effective dependency type is not waits-for, graph apply rejects the plan, since the fields would be silently ignored otherwise.","triggerScenarios":"An edge sets gate, spawner_key, or spawner_id while edge.type is empty (defaulting to a non-waits-for type) or set to another type like blocks/related.","commonSituations":"Template-generated plans adding gate metadata to plain dependency edges; author hand-adding a gate to an existing non-waits-for edge; a converter mapping fields without adjusting type.","solutions":["Set edge.type to \"waits-for\" on edges that carry gate/spawner fields","Remove the gate/spawner fields if the edge should be a plain dependency","If converting an edge to waits-for, also satisfy the spawner/gate constraints (see related errors)"],"exampleFix":"// before\n{\"from_key\": \"a\", \"to_key\": \"b\", \"type\": \"related\", \"gate\": \"all-children\"}\n// after\n{\"from_key\": \"a\", \"to_key\": \"b\", \"type\": \"waits-for\", \"gate\": \"all-children\"}","handlingStrategy":"validation","validationCode":"if (e.Gate != \"\" || e.SpawnerKey != \"\" || e.SpawnerID != \"\") && e.Type != \"waits-for\" {\n  return fmt.Errorf(\"edge with gate/spawner fields must have type waits-for\")\n}","typeGuard":"func gateFieldsSet(e Edge) bool { return e.Gate != \"\" || e.SpawnerKey != \"\" || e.SpawnerID != \"\" }","tryCatchPattern":null,"preventionTips":["Only attach gate/spawner fields to waits-for edges","Have the generator assert type == waits-for whenever it emits gate fields","Document waits-for requirements in plan templates"],"tags":["graph-plan","waits-for","validation"],"backgroundTag":"gate-requires-waits-for-type","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}