{"record":{"id":"33884697c1a0f562","repo":"gastownhall/beads","slug":"cannot-modify-pinned-issue-s-use-force-to-over","errorCode":null,"errorMessage":"cannot modify pinned issue %s (use --force to override)","messagePattern":"cannot modify pinned issue (.+?) \\(use --force to override\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/validation/issue.go","lineNumber":68,"sourceCode":"}\n\n// NotPinned validates that an issue is not pinned, by either of the two ways\n// bd expresses a pin: the Pinned boolean column or the pinned status. Returns\n// an error if either trigger fires, unless force is true.\n//\n// Both triggers are load-bearing because consumers disagree on which one they\n// write and read (ga-z3vht). Gas Town pins by status — it enumerates pins with\n// List(ListOptions{Status: StatusPinned}) across 21 sites (hook_check.go,\n// prime_output.go, molecule_step.go, up.go) — while Gas City reads the boolean\n// (compute_awake_set.go). Checking only one strips the other consumer's\n// protection outright, so do not \"simplify\" either clause away.\nfunc NotPinned(force bool) IssueValidator {\n\treturn func(id string, issue *types.Issue) error {\n\t\tif issue == nil {\n\t\t\treturn nil // Let Exists() handle nil check if needed\n\t\t}\n\t\tif !force && (issue.Pinned || issue.Status == types.StatusPinned) {\n\t\t\treturn fmt.Errorf(\"cannot modify pinned issue %s (use --force to override)\", id)\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// CanonicalActor normalizes an identity string so two spellings of the same\n// Gas Town identity compare equal. The same identity arrives at bd in more\n// than one spelling depending on which layer produced the string it was\n// handed: a dotted alias like \"gastown.mayor\" gets its dot replaced wherever\n// a dot is unsafe for that context — \"__\" in a session name, \"_\" in a Dolt\n// table/database name, \"-\" elsewhere — and bd only ever sees the resulting\n// string, never the substitution itself (ga-wzl83). None of \".\", \"_\", \"-\"\n// carries meaning in an identity string: each is always a positional\n// separator between a rig and a role/agent name, never part of either name.\n// Collapsing a run of them to one canonical separator lets two spellings of\n// the same identity compare equal without weakening comparisons between\n// genuinely different identities, whose non-separator characters still\n// differ (e.g. \"gastown.mayor\" vs \"gastown.dog-3\" stay distinct).","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/validation/issue.go#L50-L86","documentation":"NotPinned(force) blocks modification of pinned issues. An issue is pinned if its Pinned flag is true or its Status is StatusPinned; pins signal 'do not touch' (e.g. seed issues, anchors).","triggerScenarios":"Update/close/assign on an issue with Pinned=true or Status='pinned' while force=false in the validator.","commonSituations":"Bulk scripts that iterate all open issues and hit pinned anchors; new contributors unaware an issue is intentionally pinned; automation closing stale issues that are pinned.","solutions":["Pass --force to the bd command if you genuinely need to modify the pinned issue","Reconsider whether the operation should skip pinned issues (filter them out)","Use 'bd show <id>' to confirm pin state and who/why it was pinned","Ask the maintainer who pinned it before forcing"],"exampleFix":"// before\nbd close bd-anchor  // blocked: pinned\n// after\nbd close bd-anchor --force  // intentional override","handlingStrategy":"validation","validationCode":"if issue.Pinned || issue.Status == types.StatusPinned {\n    return fmt.Errorf(\"%s is pinned; skipping (use --force deliberately)\", issue.ID)\n}","typeGuard":"func isPinned(issue *types.Issue) bool { return issue != nil && (issue.Pinned || issue.Status == types.StatusPinned) }","tryCatchPattern":"if err := closeIssue(id); err != nil {\n    if strings.Contains(err.Error(), \"pinned\") && forceAllowed {\n        return closeIssueForce(id)\n    }\n    return err\n}","preventionTips":["Skip pinned issues in automation/bulk scripts by default","Treat --force on a pinned issue as a deliberate, logged decision","Check 'bd show <id>' pin state before closing stale issues"],"tags":["validation","pinned","guardrail"],"backgroundTag":"protected-resource-modification","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}