2.0 KiB
2.0 KiB
id, title, status, wave, depends_on, plan
| id | title | status | wave | depends_on | plan |
|---|---|---|---|---|---|
| task-03 | plugin_state SQLite store | done | 1 | docs/plans/plugins/plan.md |
plugin_state SQLite store
Title
SQLite-backed plugin-scoped KV state store following the per-package
initSchema() convention.
Inputs
- Spec
docs/specs/plugins/requirements/plugins.md→ "plugin_state(SQLite)" and "Plugin state API". Table:CREATE TABLE plugin_state ( id TEXT PRIMARY KEY, plugin_id TEXT NOT NULL, scope TEXT NOT NULL DEFAULT 'instance', scope_id TEXT, state_key TEXT NOT NULL, value_json TEXT NOT NULL, updated_at TEXT NOT NULL, UNIQUE (plugin_id, scope, scope_id, state_key)); - Scopes: instance | workspace | task | agent. Plugins cannot read others' state (enforced by always filtering on plugin_id — the API layer passes it, task-09).
- Pattern reference:
internal/task/repository/sqlite/base_schema.go(initSchema withCREATE TABLE IF NOT EXISTS) and how repos take a*sql.DB/db handle. Use the same db handle type the other stores use (db.Poolwriter/reader or*sql.DB— match sibling stores; inspectinternal/sentry/store.go).
Acceptance
StateStorewithinitSchema()creatingplugin_state(idempotent).- Methods:
Get(ctx, pluginID, scope, scopeID, key) (json.RawMessage, bool, error),Set(ctx, pluginID, scope, scopeID, key, value)(upsert on the UNIQUE key),Delete(ctx, pluginID, scope, scopeID, key),List(ctx, pluginID, scope, scopeID) ([]StateEntry, error). updated_atset to RFC3339 UTC on write. NULLscope_idhandled for instance scope.
Files
apps/backend/internal/plugins/state/store.goapps/backend/internal/plugins/state/store_test.go(in-memory or temp-file sqlite)
Verification
go test ./internal/plugins/state/...fromapps/backendmake -C apps/backend lint
Output contract
Report: db handle type chosen (match siblings), upsert approach, scope_id NULL
handling. Stay within internal/plugins/state/.
Dependencies
None.