{"record":{"id":"765575b8d18742dd","repo":"gastownhall/beads","slug":"node-q-storage-class-ephemeral-conflicts-with-ep","errorCode":null,"errorMessage":"node %q: storage_class ephemeral conflicts with ephemeral: false","messagePattern":"node %q: storage_class ephemeral conflicts with ephemeral: false","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":911,"sourceCode":"\t\tnoHistory = *node.NoHistory\n\t}\n\tif ephemeral && noHistory {\n\t\treturn false, false, \"\", fmt.Errorf(\"node %q: ephemeral and no_history are mutually exclusive\", node.Key)\n\t}\n\tissueType := types.IssueType(node.Type)\n\tif issueType == \"\" {\n\t\tissueType = types.TypeTask\n\t}\n\tclass, err = resolveStorageClass(node.StorageClass, issueType.Normalize())\n\tif err != nil {\n\t\treturn false, false, \"\", fmt.Errorf(\"node %q: %w\", node.Key, err)\n\t}\n\tif class == types.StorageClassEphemeral {\n\t\tif noHistory {\n\t\t\treturn false, false, \"\", fmt.Errorf(\"node %q: storage_class ephemeral and no_history are mutually exclusive\", node.Key)\n\t\t}\n\t\tif node.Ephemeral != nil && !*node.Ephemeral {\n\t\t\treturn false, false, \"\", fmt.Errorf(\"node %q: storage_class ephemeral conflicts with ephemeral: false\", node.Key)\n\t\t}\n\t\tephemeral = true\n\t\tclass = \"\"\n\t}\n\t// Reconcile the requested durable class with the effective wisp plane\n\t// (flag > config, Protocol v0.1 §C1.3): an explicit node storage_class\n\t// contradicts an ephemeral/no_history node and is rejected, so the durable\n\t// intent is preserved rather than silently collapsed into an\n\t// effective-ephemeral record; a per-type config default yields to the\n\t// effective plane. versioned normalizes to the unset marker only after the\n\t// check (C2.4).\n\tvar conflict bool\n\tclass, conflict = reconcileStorageClassPlane(class, node.StorageClass != \"\", ephemeral || noHistory)\n\tif conflict {\n\t\treturn false, false, \"\", fmt.Errorf(\"node %q: storage_class %s conflicts with ephemeral/no_history: wisp-plane records are storage class ephemeral\", node.Key, class)\n\t}\n\treturn ephemeral, noHistory, class, nil\n}","sourceCodeStart":893,"sourceCodeEnd":929,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L893-L929","documentation":"A node explicitly set `storage_class: \"ephemeral\"` but also `ephemeral: false`. The explicit false contradicts the storage class, so apply aborts. The node cannot simultaneously claim to be ephemeral (via class) and not ephemeral (via flag).","triggerScenarios":"Applying a plan node with both `storage_class: \"ephemeral\"` and `\"ephemeral\": false`.","commonSituations":"Generated plans where one code path sets ephemeral:false by default and another sets storage_class; manual edits flipping the boolean without removing the storage class.","solutions":["Remove `\"ephemeral\": false` from the node, keeping storage_class ephemeral.","Or change storage_class to a durable value if ephemeral:false is the intended state.","Fix the plan generator so it emits only one of the two settings."],"exampleFix":"// before\n{\"key\": \"bd-123\", \"storage_class\": \"ephemeral\", \"ephemeral\": false}\n// after\n{\"key\": \"bd-123\", \"storage_class\": \"ephemeral\"}","handlingStrategy":"validation","validationCode":"for _, n := range plan.Nodes {\n  if n.StorageClass == \"ephemeral\" && n.Ephemeral != nil && !*n.Ephemeral {\n    return fmt.Errorf(\"node %q: ephemeral:false contradicts storage_class ephemeral\", n.Key)\n  }\n}","typeGuard":"func contradictsEphemeral(n Node) bool {\n  return n.StorageClass == \"ephemeral\" && n.Ephemeral != nil && !*n.Ephemeral\n}","tryCatchPattern":"if err := bd.GraphApply(ctx, plan); err != nil {\n  if strings.Contains(err.Error(), \"conflicts with ephemeral: false\") {\n    // fix plan generator output and retry\n  }\n}","preventionTips":["Set at most one of ephemeral flag / storage_class per node.","Have generators emit storage_class OR the boolean, not both.","Review hand-edited plan JSON for half-removed fields."],"tags":["cli","graph-apply","storage-class","config-conflict"],"backgroundTag":"conflicting-storage-settings","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}