SR-1 agent_skill table + Drizzle store + apply_caveman/apply_artifact_style columns #869

Closed
opened 2026-05-05 10:28:14 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As a platform engineer, I want a single agent_skill table that stores per-agent_type and per-agent instance skill bodies, plus two boolean columns on agent_type to gate the caveman / artifact-style appendices, so that the rest of the rework has a clear schema to build on.

Foundational ticket. No call-site changes here — that follows in SR-4.

Acceptance criteria

Schema

  • New agent_skill table with columns (scope TEXT NOT NULL CHECK (scope IN ('type','instance')), agent_type TEXT NOT NULL, instance_id INTEGER NULL, name TEXT NOT NULL, body TEXT NOT NULL, updated_at INTEGER NOT NULL).
  • Foreign key: instance_id REFERENCES agents(id) ON DELETE CASCADE.
  • CHECK: (scope='type' AND instance_id IS NULL) OR (scope='instance' AND instance_id IS NOT NULL).
  • Partial unique index ux_agent_skill_type ON agent_skill(agent_type, name) WHERE scope='type'.
  • Partial unique index ux_agent_skill_instance ON agent_skill(instance_id, name) WHERE scope='instance'.
  • BEFORE INSERT and BEFORE UPDATE triggers on agent_skill raise when scope='instance' and agent_type != (SELECT type FROM agents WHERE id = NEW.instance_id). Test fixture proves the trigger fires on mismatch.
  • Two new columns on agent_type: apply_caveman INTEGER DEFAULT 0, apply_artifact_style INTEGER DEFAULT 1.

Drizzle module

  • New apps/server/src/infrastructure/database/agent-skill-store.ts module exposes getAgentTypeSkill(agentType, name), getAgentInstanceSkill(instanceId, name), listAgentTypeSkills(agentType), listAgentInstanceSkills(instanceId), upsertAgentTypeSkill(agentType, name, body), upsertAgentInstanceSkill(instanceId, agentType, name, body), deleteAgentTypeSkill(agentType, name), deleteAgentInstanceSkill(instanceId, name).
  • No raw bun:sqlite imports outside this module (just sql-layer-check passes).
  • Drizzle schema lives in apps/server/src/infrastructure/database/schema/agent-skill.ts; migration generated via drizzle-kit and committed under apps/server/src/infrastructure/database/drizzle/.

Tests

  • agent-skill-store.test.ts covers: round-trip CRUD per scope, partial-unique-index conflict on duplicate insert, cascade-delete clears instance rows when agents row is deleted, consistency trigger fires on mismatch and rolls back the transaction.

Out of scope

  • Reading from / writing to this table from any dispatch path (covered in SR-4).
  • Migrating data out of the legacy skill table (covered in SR-3).
  • API or UI surface (covered in SR-7 / SR-8).
  • Touching the legacy skill table or skill_overrides_json column — both stay live in parallel until SR-11.

References

  • Spec: specs/skills-rework.md §Schema, §Tests.
  • Existing legacy table: apps/server/src/infrastructure/database/schema/agent-config-scope.ts (skill).
  • Drizzle conventions: docs/database.md.
## User story As a platform engineer, I want a single `agent_skill` table that stores per-`agent_type` and per-`agent instance` skill bodies, plus two boolean columns on `agent_type` to gate the caveman / artifact-style appendices, so that the rest of the rework has a clear schema to build on. Foundational ticket. No call-site changes here — that follows in SR-4. ## Acceptance criteria ### Schema - [ ] New `agent_skill` table with columns `(scope TEXT NOT NULL CHECK (scope IN ('type','instance')), agent_type TEXT NOT NULL, instance_id INTEGER NULL, name TEXT NOT NULL, body TEXT NOT NULL, updated_at INTEGER NOT NULL)`. - [ ] Foreign key: `instance_id REFERENCES agents(id) ON DELETE CASCADE`. - [ ] CHECK: `(scope='type' AND instance_id IS NULL) OR (scope='instance' AND instance_id IS NOT NULL)`. - [ ] Partial unique index `ux_agent_skill_type ON agent_skill(agent_type, name) WHERE scope='type'`. - [ ] Partial unique index `ux_agent_skill_instance ON agent_skill(instance_id, name) WHERE scope='instance'`. - [ ] `BEFORE INSERT` and `BEFORE UPDATE` triggers on `agent_skill` raise when `scope='instance'` and `agent_type != (SELECT type FROM agents WHERE id = NEW.instance_id)`. Test fixture proves the trigger fires on mismatch. - [ ] Two new columns on `agent_type`: `apply_caveman INTEGER DEFAULT 0`, `apply_artifact_style INTEGER DEFAULT 1`. ### Drizzle module - [ ] New `apps/server/src/infrastructure/database/agent-skill-store.ts` module exposes `getAgentTypeSkill(agentType, name)`, `getAgentInstanceSkill(instanceId, name)`, `listAgentTypeSkills(agentType)`, `listAgentInstanceSkills(instanceId)`, `upsertAgentTypeSkill(agentType, name, body)`, `upsertAgentInstanceSkill(instanceId, agentType, name, body)`, `deleteAgentTypeSkill(agentType, name)`, `deleteAgentInstanceSkill(instanceId, name)`. - [ ] No raw `bun:sqlite` imports outside this module (`just sql-layer-check` passes). - [ ] Drizzle schema lives in `apps/server/src/infrastructure/database/schema/agent-skill.ts`; migration generated via `drizzle-kit` and committed under `apps/server/src/infrastructure/database/drizzle/`. ### Tests - [ ] `agent-skill-store.test.ts` covers: round-trip CRUD per scope, partial-unique-index conflict on duplicate insert, cascade-delete clears instance rows when `agents` row is deleted, consistency trigger fires on mismatch and rolls back the transaction. ## Out of scope - Reading from / writing to this table from any dispatch path (covered in SR-4). - Migrating data out of the legacy `skill` table (covered in SR-3). - API or UI surface (covered in SR-7 / SR-8). - Touching the legacy `skill` table or `skill_overrides_json` column — both stay live in parallel until SR-11. ## References - Spec: `specs/skills-rework.md` §Schema, §Tests. - Existing legacy table: `apps/server/src/infrastructure/database/schema/agent-config-scope.ts` (`skill`). - Drizzle conventions: `docs/database.md`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

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