docs: Document COALESCE/NULLIF clone-local field protection pattern
Expands the inline comment to serve as a reference for when to use the COALESCE(NULLIF(?, zero), column) defensive idiom in JSONL imports. Pattern protects clone-local state (pinned, gate fields) from being overwritten when importing JSONL where omitempty causes zero values to represent "field absent" rather than "field is zero". 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -330,12 +330,23 @@ func (s *SQLiteStorage) upsertIssueInTx(ctx context.Context, tx *sql.Tx, issue *
|
||||
}
|
||||
|
||||
if existingHash != issue.ContentHash {
|
||||
// Pinned field fix (bd-phtv): Use COALESCE(NULLIF(?, 0), pinned) to preserve
|
||||
// existing pinned=1 when incoming pinned=0 (which means field was absent in
|
||||
// JSONL due to omitempty). This prevents auto-import from resetting pinned issues.
|
||||
// Gate field fix (bd-gr4q): Same pattern for await_type, await_id, timeout_ns, waiters.
|
||||
// Gates are wisps and aren't exported to JSONL, so importing an issue with the same
|
||||
// ID would clear these fields without this protection.
|
||||
// Clone-local field protection pattern (bd-phtv, bd-gr4q):
|
||||
//
|
||||
// Some fields are clone-local state that shouldn't be overwritten by JSONL import:
|
||||
// - pinned: Local hook attachment (not synced between clones)
|
||||
// - await_type, await_id, timeout_ns, waiters: Gate state (wisps, never exported)
|
||||
//
|
||||
// Problem: Go's omitempty causes zero values to be absent from JSONL.
|
||||
// When importing, absent fields unmarshal as zero, which would overwrite local state.
|
||||
//
|
||||
// Solution: COALESCE(NULLIF(incoming, zero_value), existing_column)
|
||||
// - For strings: COALESCE(NULLIF(?, ''), column) -- preserve if incoming is ""
|
||||
// - For integers: COALESCE(NULLIF(?, 0), column) -- preserve if incoming is 0
|
||||
//
|
||||
// When to use this pattern:
|
||||
// 1. Field is clone-local (not part of shared issue ledger)
|
||||
// 2. Field uses omitempty (so zero value means "absent", not "clear")
|
||||
// 3. Accidental clearing would cause data loss or incorrect behavior
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
UPDATE issues SET
|
||||
content_hash = ?, title = ?, description = ?, design = ?,
|
||||
|
||||
Reference in New Issue
Block a user