DOB-2: Migration — collapse scope='builtin' rows into scope='global', drop builtin_hash columns #794

Closed
opened 2026-05-03 18:04:38 +00:00 by claude-desktop · 3 comments
Collaborator

As a platform engineer, I want a one-shot DB migration that takes every existing scope='builtin' row in the per-kind tables, promotes it to scope='global' (when no global row already shadows it), drops it otherwise, and removes the now-vestigial builtin_hash columns — so the DB on an upgraded host carries no builtin rows or hash bookkeeping after the next restart.

DOB-1 already removes the resolver's read path for builtin, so these rows are dead data the moment that ships. This story makes the cleanup permanent and storage-clean.

Acceptance criteria

Migration

  • New file apps/server/src/infrastructure/database/migrations/010-collapse-builtin-rows.ts ships:
    • For each table with a scope column (skill, system_prompt, plugin_binding, plugin_marketplace, mcp_server, agent_type_config, agent_type_container, agent_type_routing, agent_type, service_config, label_catalog):
      • For every scope='builtin' row, if a scope='global' row with the same logical key (per-kind unique-key fields) already exists → DELETE the builtin row. Else → UPDATE its scope to 'global'.
      • Logical key per kind documented inline as a code comment, sourced from each table's existing UNIQUE INDEX.
  • After the row migration, drop the builtin_hash column from every table that carries one (agent_type, service_config, label_catalog, skill, system_prompt, plugin_binding, plugin_marketplace, mcp_server).
  • Migration is idempotent: a second run is a no-op (no builtin rows, no builtin_hash columns left).
  • Migration logs one summary line per table: [010] <table>: promoted=N, deleted=M, dropped builtin_hash.

Schema fallout

  • BuiltinHashColumns (or equivalent type) and any TS code reading .builtin_hash is removed in the same PR.
  • ConfigScope union narrows to 'global' | 'agent_type' | 'instance'. resolver.ts SCOPE_PRIORITY matches.

Tests

  • Test: seed builtin row + global row sharing same key → migration drops the builtin, keeps the global.
  • Test: seed builtin row only → migration promotes it to global with same fields.
  • Test: idempotency — second run produces zero changes, zero log lines.
  • Test: builtin_hash columns physically gone (PRAGMA table_info doesn't list them).

Out of scope

  • Removing the syncBuiltinsFromRepo function — DOB-4. It still runs at boot until DOB-4; this migration just handles the rows it's left behind.
  • Deleting config/*.json files — DOB-5.

References

  • Code: apps/server/src/infrastructure/database/migrations/ (existing migration patterns 001..009), apps/server/src/domain/agent-config/resolver.ts (SCOPE_PRIORITY).
  • Depends on: DOB-1 (#793) — resolver must already ignore builtin rows before this migration runs, otherwise the brief moment between row delete and global promote would resolve to null.
As a platform engineer, I want a one-shot DB migration that takes every existing `scope='builtin'` row in the per-kind tables, promotes it to `scope='global'` (when no `global` row already shadows it), drops it otherwise, and removes the now-vestigial `builtin_hash` columns — so the DB on an upgraded host carries no `builtin` rows or hash bookkeeping after the next restart. DOB-1 already removes the resolver's read path for `builtin`, so these rows are dead data the moment that ships. This story makes the cleanup permanent and storage-clean. ## Acceptance criteria ### Migration - [ ] New file `apps/server/src/infrastructure/database/migrations/010-collapse-builtin-rows.ts` ships: - For each table with a `scope` column (`skill`, `system_prompt`, `plugin_binding`, `plugin_marketplace`, `mcp_server`, `agent_type_config`, `agent_type_container`, `agent_type_routing`, `agent_type`, `service_config`, `label_catalog`): - For every `scope='builtin'` row, if a `scope='global'` row with the same logical key (per-kind unique-key fields) already exists → DELETE the builtin row. Else → UPDATE its scope to `'global'`. - Logical key per kind documented inline as a code comment, sourced from each table's existing UNIQUE INDEX. - [ ] After the row migration, drop the `builtin_hash` column from every table that carries one (`agent_type`, `service_config`, `label_catalog`, `skill`, `system_prompt`, `plugin_binding`, `plugin_marketplace`, `mcp_server`). - [ ] Migration is idempotent: a second run is a no-op (no `builtin` rows, no `builtin_hash` columns left). - [ ] Migration logs one summary line per table: `[010] <table>: promoted=N, deleted=M, dropped builtin_hash`. ### Schema fallout - [ ] `BuiltinHashColumns` (or equivalent type) and any TS code reading `.builtin_hash` is removed in the same PR. - [ ] `ConfigScope` union narrows to `'global' | 'agent_type' | 'instance'`. `resolver.ts` `SCOPE_PRIORITY` matches. ### Tests - [ ] Test: seed builtin row + global row sharing same key → migration drops the builtin, keeps the global. - [ ] Test: seed builtin row only → migration promotes it to global with same fields. - [ ] Test: idempotency — second run produces zero changes, zero log lines. - [ ] Test: `builtin_hash` columns physically gone (`PRAGMA table_info` doesn't list them). ## Out of scope - Removing the `syncBuiltinsFromRepo` function — DOB-4. It still runs at boot until DOB-4; this migration just handles the rows it's left behind. - Deleting `config/*.json` files — DOB-5. ## References - Code: `apps/server/src/infrastructure/database/migrations/` (existing migration patterns 001..009), `apps/server/src/domain/agent-config/resolver.ts` (`SCOPE_PRIORITY`). - Depends on: DOB-1 (#793) — resolver must already ignore `builtin` rows before this migration runs, otherwise the brief moment between row delete and `global` promote would resolve to `null`.
Collaborator

🤖 Auto-assigned to code-lead (heuristic: area:agents → code-lead (architecture-touching)). Reply /unassign to reroute.

🤖 Auto-assigned to **code-lead** (heuristic: area:agents → code-lead (architecture-touching)). Reply `/unassign` to reroute.
Collaborator

🦵 @charles kicked the queue — re-running address-review on @code-lead.

🦵 @charles kicked the queue — re-running address-review on @code-lead.
Collaborator

🦵 @charles kicked the queue — re-running address-review on @code-lead.

🦵 @charles kicked the queue — re-running address-review on @code-lead.
Sign in to join this conversation.
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
charles/claude-hooks#794
No description provided.