Skip to content
Documentation GitHub
Agent

Skill Storage

Status: Design landing Reference epics: INK-835, INK-829, INK-833, INK-844 ADRs: ADR-016

Skills are part of the workspace, but they are not world content (see Skill System; decision D31). They have their own storage shape, their own permission surface, their own sync scope, and an explicit non-relationship with the submit boundary. This page specifies those pieces.

The sibling pages:

A skill is a package — not a single content blob. The package has:

  • Metadata: id, name, description, version, source (system | user | imported | marketplace reserved), author, declared required_capabilities, tags, declared parameter_schema, optional model_preference.
  • Framing: the authored prose (or PromptTemplate artifact) the Composer uses as the prompt opening.
  • Artifacts: zero or more typed supporting resources — Description, Approach, PromptTemplate, Example.
  • Invocation contract: input_schema, output_schema, declared progress-reporting shape, named interrupt points.

Artifact kinds are a closed set. New kinds require an explicit design pass. Retired kinds — DspyModule, and CodeTemplate in its RLM-sandbox sense — are gone. A Resource kind (for attached reference files) is an open question; it is not part of the current design.

Skills live in inklings.db (decision D26 — the unified workspace database). They do not live:

  • In agents.db — that database is retired; skill tables live in inklings.db.
  • In the world-content schema (pages, blocks, entities, etc.). There are no foreign keys from skill tables into world-content tables.
  • In the Agent Memory System tiers. Skills are not memory.

The host (Tauri side) owns the tables. The sidecar reads and writes through MCP tools (see MCP System); Rust handlers execute the SQL. This follows the same separation every workspace-adjacent table uses.

Three tables: skills, skill_artifacts, and skill_permissions.

The package’s identity and metadata.

CREATE TABLE skills (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
version TEXT NOT NULL DEFAULT '1.0.0',
source TEXT NOT NULL DEFAULT 'system',
required_capabilities TEXT NOT NULL DEFAULT '[]', -- JSON array
tags TEXT NOT NULL DEFAULT '[]', -- JSON array
artifact_manifest TEXT, -- reserved; optional hash for cache invalidation
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX idx_skills_source ON skills(source);
CREATE INDEX idx_skills_version ON skills(version);

A StoredMetadata JSON column approach is used for fields outside this base shape (parameter schema, invocation contract, model preference). JSON-blob metadata is the right shape for schema resilience across skill-format revisions.

The source values:

  • system — bundled with the app.
  • user — authored in this workspace.
  • imported — brought in from a file or another workspace.
  • marketplace — reserved; marketplace browsing and publishing are not implemented.

Typed supporting resources belonging to a skill.

CREATE TABLE skill_artifacts (
id TEXT PRIMARY KEY,
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
kind TEXT NOT NULL, -- Description | Approach | PromptTemplate | Example
name TEXT NOT NULL,
content TEXT NOT NULL,
summary TEXT,
ordering INTEGER NOT NULL DEFAULT 0,
estimated_tokens INTEGER,
state_blob BLOB, -- reserved; unused post-skill-compiler removal
UNIQUE(skill_id, name)
);
CREATE INDEX idx_skill_artifacts_skill ON skill_artifacts(skill_id);

The state_blob column is a tombstone — retained to avoid a migration, but it has no writer. The DSPy skill-compilation pipeline that historically wrote this column is retired (decision D5); nothing in the current design reads or writes it.

The per-workspace permission record for a skill. One row per (skill_id, workspace_id).

CREATE TABLE skill_permissions (
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
workspace_id TEXT NOT NULL,
enabled INTEGER NOT NULL DEFAULT 1, -- bool
channels_allowed TEXT NOT NULL DEFAULT '[]', -- JSON: ["all"] | ["channel_id", ...]
autonomous_task_contexts TEXT NOT NULL DEFAULT '[]', -- JSON: category names where the skill is invokable autonomously
updated_at TEXT NOT NULL,
PRIMARY KEY (skill_id, workspace_id)
);
CREATE INDEX idx_skill_permissions_workspace ON skill_permissions(workspace_id);

Semantics:

  • enabled gates all invocation. A disabled skill is not dispatched even if the caller asks for it by name.
  • channels_allowed restricts conversation-driven invocations to named channels. The special value ["all"] means every channel. An empty array means no channel — conversation-driven invocation is disabled but scheduled-task invocation may still be allowed.
  • autonomous_task_contexts restricts scheduled-task and agent-selected autonomous invocations. The category model for autonomous tasks is specified in the scheduling subsystem (see Scheduling System and the autonomous-task resolution in the parking lot); this table carries the named references.
  • updated_at drives sync conflict resolution (LWW).

The permission record is created with sensible defaults on install. Defaults for an imported skill are “enabled: false, channels_allowed: [], autonomous_task_contexts: []” — an import is acknowledged, but permissions are opt-in per workspace. Defaults for a system skill can be “enabled: true, channels_allowed: [‘all’]” if the bundled skill’s manifest declares low-risk use.

The retired execution_traces table (from the DSPy skill-compilation era) is not recreated. Observability lives on the invocation record associated with the turn, not a separate trace table.

  • cached_skills — the v1 single-content table from before multi-artifact. Exits when its last consumer exits.
  • execution_traces / artifact_traces — retired with the DSPy skill-compilation pipeline (D5).
  • agents.db — the whole database retires; tables that belong here move into inklings.db.

Explicitly, for avoidance of doubt:

  • No origin column on skills or skill_artifacts. Skills are not world-model claims.
  • No lifecycle column. There is no candidatecanonical-allowed transition for skill CRUD.
  • No foreign keys from skill tables into world-content tables.
  • No derivation_links rows between skill entities.
  • No deviation_records rows produced by skill CRUD.
  • Skill writes do not pass through the submit boundary’s invariant check (see Submit Boundary). They pass through the ordinary repository-layer consistency checks any SQLite write goes through.

The separation is architectural, not just a convention. The Tauri command layer routes skill CRUD to the SkillPackageRepository, which writes directly to these tables. It does not invoke the submit-boundary adapter chain. An implementation that accidentally added a boundary-crossing path would violate decision D31 and the subsystem’s layering.

Markdown with YAML frontmatter is the canonical transport format for skills (see Skill System — canonical authoring format). The SQLite record is a cache and index over the markdown source; the .md file is the portable artifact.

A skill exports as a single .md file. For bulk sharing or marketplace distribution, .md files are collected into a .tar bundle — one file per skill, no subdirectories required.

The file structure is:

<skill-name>.md # frontmatter (metadata + contract) + markdown body (framing + artifacts)

The frontmatter fields map directly to the skills table columns and the StoredMetadata JSON blob. The markdown body holds the prompt framing and any structured artifact sections. The round-trip is lossless: importing and immediately re-exporting produces an identical file.

There is no skill.json manifest, no framing.md side-file, and no artifacts/ subdirectory. The single .md file is the complete, self-contained representation.

import_skill is the application-layer use case. It accepts:

  • A skill .md file (or a .tar of .md files for bulk import).
  • An acknowledged source: the worldbuilder has confirmed the origin and intent. Import is not gated by a draft-like limbo (decision D31).
  • An optional capability preview: before the import commits, the UI renders which tools the skill declares it will use and what the caller’s capability set would expose to it. The worldbuilder acknowledges; the import proceeds.

On import:

  1. The frontmatter is parsed and validated against the current schema (required fields, closed artifact-kind set, known required_scopes values).
  2. The markdown body is parsed into framing prose and any structured artifact sections.
  3. If a skill with the same id exists, the import is idempotent-by-id — it overwrites, preserving the local skill_permissions row unless the import explicitly carries permissions.
  4. Rows are written to skills, skill_artifacts, and skill_permissions (with conservative defaults for imported source).
  5. The skill appears in the workspace’s skill surface.

The application-layer use case is named import_skill. The marketplace source value is reserved; marketplace-sourced installs route through this same path. Marketplace browsing and publishing are not implemented.

The Supabase-backed sync module (skill_sync) replicates skills across devices for a given workspace.

  • All workspace skills sync — not only user-authored. Bundled, user-authored, and imported skills are all in scope.
  • skill_artifacts syncs alongside skills. An artifact change is part of the skill’s state; it participates in the same LWW conflict-resolution by updated_at on the parent skills row.
  • skill_permissions syncs across devices. A skill enabled on the worldbuilder’s laptop is enabled on the worldbuilder’s desktop for the same workspace. LWW on the permissions row’s own updated_at.
  • LWW-by-updated_at is the conflict resolution strategy.
  • marketplace_skill_refs schema capacity is reserved. Marketplace browsing and publishing are not implemented.
  • Covers: per-workspace-cross-device. A skill present in workspace W on device A syncs to device B when B opens W.
  • Does not cover: cross-workspace following. Whether installing a skill in workspace A automatically exposes it in workspace B is a separate question. Currently, installing in A affects only A.

Sync is orthogonal to permissions. A skill arriving on a new device does not silently enable itself in channels that were not enabled before. The permissions row syncs too; its enabled state governs invocability on every device.

Skills are not agent memory. The four-tier Agent Memory System does not store skills, skill artifacts, or skill permissions. A skill’s invocation reads memory through MCP memory tools the same way any turn does; the skill definition itself lives in these storage tables, separate from the memory tiers.

If language elsewhere in the Codex implies skills persist inside agent_memory, that language is superseded by this page.

Relationship to provenance and the submit boundary

Section titled “Relationship to provenance and the submit boundary”

Repeated for emphasis, because the prior D18 framing said the opposite:

  • Skill CRUD does not cross the Submit Boundary.
  • Skills do not carry Provenance.
  • No Derivation Links between skill entities.
  • No Deviation Records produced by skill CRUD.
  • Content produced by a skill during execution crosses the boundary with full provenance, same as any agent write (decision D20).
  • Skill System — the surrounding framing.
  • Skill Composer — the runtime subagent that reads these entities.
  • MCP System — the transport for skill-management tools (create_skill, update_skill, list_skills, add_artifact) the sidecar uses to call back into the Rust domain.
  • Agent Memory System — distinct; skills are not a memory tier.
  • Scheduling System — consumes skill_permissions.autonomous_task_contexts when evaluating whether a scheduled task may invoke a skill.
  • Permission System — capability resolution at invocation time; independent of skill_permissions (the latter gates whether the Composer is dispatched at all; the former narrows the tool surface once dispatched).
  • Artifact kind set. Whether to add Resource (generic attached reference file — image, data table, small binary) is a design call for a future pass. Worldbuilding skills that bundle reference material are the motivating use.
  • Cross-workspace skill following. Whether a skill installed in workspace A should follow the author to workspace B. Deferred; per-workspace-cross-device is what the current subsystem commits to.
  • Permission granularity. Whether channels_allowed should permit per-channel-type (e.g., “all research channels”) as well as per-channel-id. Current schema supports per-id + "all"; richer forms are a schema addition, not a breaking change.
  • Marketplace storage shape. Whether marketplace-sourced skills get a separate table (marketplace_skill_refs is reserved) or reuse skills with source='marketplace' and additional metadata. Not resolved; marketplace is not implemented.
  • It does not describe the composition algorithm or its I/O contract. See Skill Composer.
  • It does not describe the authoring UI. That is a frontend concern.
  • It does not specify the Tauri command or HTTP bridge signatures. Those mirror the application-layer CRUD and are documented alongside other Tauri commands.
  • It does not describe autonomous-task categorization in detail. See Scheduling System and the autonomous-task-category work tracked in the parking lot.

Was this page helpful?