Skip to content

[db] scopeInClause does not COALESCE NULL scope to 'global' the way scopeVisible does — a NULL-scope row is visible to one reader and hidden from the other #107

Description

@devinmlowe

Evidence

src/_core/db/scope.ts:14-17 treats a NULL scope as global:

export function scopeVisible(rowScope, scopes) {
  if (!scopes || scopes.length === 0) return true;
  return scopes.includes(rowScope ?? GLOBAL_SCOPE);
}

scope.ts:20-23 does not:

export function scopeInClause(col, scopes) {
  if (!scopes || scopes.length === 0) return null;
  return { sql: `${col} IN (${scopes.map(() => "?").join(", ")})`, params: [...scopes] };
}

col IN (...) is never true for NULL. widenScope (scope.ts:37) also guards scope IS NOT NULL, so a NULL row is neither widened nor listed. Callers of scopeInClause: src/episodic/search.ts:64,107, src/semantic/inspect.ts:80, src/semantic/commitments.ts:604.

Mechanism

Every scoped table except memories declares scope TEXT DEFAULT 'global' without NOT NULL (schema.ts:620,655,790). A row that ends up with scope = NULL is visible to scopeVisible-based readers (the semantic layer, forgetMemory's assertScope) but invisible to scopeInClause-based readers (episodic recall, engram memories list, commitments) for any read_scopes, including ones that contain global.

Severity note

Verified there is no in-tree producer of NULL scope today: every INSERT coalesces to 'global' (store.ts, entity.ts, relationship.ts, commitments.ts), and the ALTER TABLE ... DEFAULT 'global' migrations back-fill existing rows. It becomes reachable through an external write, a hand edit, or a future insert that binds undefined (better-sqlite3 binds it as NULL). Filing as a latent consistency bug, P3.

Suggested fix

One line in scopeInClause: COALESCE(${col}, 'global') IN (...), or (${col} IN (...) OR ${col} IS NULL) when the list contains global, so both helpers agree. Alternatively add NOT NULL to the four columns in a migration.

Related: #25.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: P3Later: consistency, hardening, design work with no observed failures

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions