6.9 KiB
spec, adr, created, status
| spec | adr | created | status |
|---|---|---|---|
| docs/specs/plugins/requirements/plugins.md | docs/decisions/0043-plugin-host-data-api.md | 2026-07-17 | implemented |
Implementation Plan: Plugin Host data API
Overview
Give plugins a typed, capability-gated path to read and write kandev domain data
over the existing kandev.plugin.v1 Host gRPC channel, replacing direct SQLite
access. Land the contract first (proto + regen) and the one net-new aggregation
(per-session LOC), then the SDK's Go-native accessors, then kandev's server-side
implementation, and finally the proof (rewrite the agent-stats plugin) plus tests
and docs. The implemented write surface is CreateTask, UpdateTask, and
SendMessage; workflow transitions use MoveTask.
The draft contract at docs/plans/plugins/HOST-DATA-API.proto is largely settled
— refine field-level details against the real DTOs (apps/backend/pkg/api/v1/),
do not redesign.
Backend
Proto contract (apps/backend/proto/kandev/plugin/v1/plugin.proto)
Fold the read and write RPCs and DTOs from
docs/plans/plugins/HOST-DATA-API.proto into the frozen plugin proto. Decide
placement: add the RPCs to the existing service Host (simplest — reuses the
single Host broker connection and the existing capability interceptor) rather
than a separate service HostData, unless a separate service is needed to keep
the interceptor's per-RPC capability lookup clean. Regenerate stubs with
make -C apps/backend proto. Read RPCs: ListTasks, GetTask, ListWorkspaces,
ListWorkflows, ListWorkflowSteps, ListAgentProfiles, ListRepositories,
ListSessions, ListSessionCodeStats. DTOs mirror
apps/backend/pkg/api/v1/{task,workflow,agent,workspace}.go field-for-field with
RFC3339 string timestamps and optional nullables.
Per-session LOC aggregation (internal/analytics/repository/sqlite/)
No existing method computes per-session committed + peak-pending LOC.
internal/analytics/repository/sqlite/stats.go aggregates git stats at workspace
and per-repository granularity only. Add a read-only aggregation returning, per
session id: SUM(insertions) / SUM(deletions) from task_session_commits, and
the peak pending-diff (MAX over each snapshot's SUM(json_each(files).additions / .deletions)) from task_session_git_snapshots. This mirrors the SQL the
agent-stats plugin runs today (stats.go:sessionsQuery). Use the dialect
helpers for SQLite/Postgres portability; filter by session-id set / workspace and
support pagination bounds. Expose it through the analytics (or task) service so
the Host handler calls a service method, not the repository.
Host data RPC implementation (internal/plugins/)
Extend pluginHost (or add a sibling under internal/plugins/) to implement the
new Host data interface methods. Each read handler:
- checks
manifest.CanRead("<resource>")— reuse the existing per-plugin binding andpermissionDenied("api_read:<resource>")helper shape; - calls the relevant service method (
taskSvcfor tasks/workspaces/repositories/ sessions, workflow service for workflows/steps, agent settings for profiles, the new aggregation for code stats) — never a repository directly; - maps internal models → the Go-native DTOs with explicit code.
Wire the new service dependencies into
plugins.Service/plugins.NewService/Provide, and thread them intohostForPlugin. The implemented writes route through the task service and orchestrator delivery path.
Frontend
None. This feature has no SPA surface; it is a plugin-facing gRPC contract.
SDK (apps/backend/pkg/pluginsdk/)
Extend the Go-native Host surface authors and kandev share:
- Add Go-native DTO structs (
Task,Session,SessionCodeStats,Workspace,Workflow,WorkflowStep,AgentProfile,Repository, page types) mirroring the proto, withtoProto/fromProtohelpers alongside the existing ones intypes.go. - Add data methods to the
Hostinterface (or aHostDatasub-interface exposed viahost.Tasks(),host.Sessions(), ...), withgrpcHostClient(plugin-side) andgrpcHostServer(kandev-side) conversions inhost.go. - Author-facing ergonomics: typed accessors returning Go-native structs, e.g.
host.Tasks().List(ctx, filter, page),host.Sessions().List(...),host.Sessions().CodeStats(...).
Tests
- DTO mapping (
internal/plugins/*_test.go): table-driven — internal model → DTO for each resource, including nullable/optional and timestamp formatting. - Capability gating (
internal/plugins/host_test.go): each read RPC returnsPermissionDeniedwithcapability 'api_read:<resource>' not declaredwhen the manifest omits the resource, and succeeds when it declares it. Write RPCs enforce theirapi_writecapabilities and return their task/message results. - Per-session LOC aggregation (
internal/analytics/repository/sqlite/*_test.go): real SQLite fixture with commits + snapshots; assert committed sums and the peak-pendingMAXsemantics (peak, not latest; not double-counted). - SDK conversions (
pkg/pluginsdk/*_test.go): proto↔native round-trip for the new DTOs. - Integration: a plugin (via the SDK Host client over the broker) calls
ListSessions+ListSessionCodeStatsand gets rows without DB access — extend the existinginternal/plugins/host_test.go/ integration harness.
E2E Tests
None. No user-visible UI surface. The integration test above covers the plugin↔host path.
Implementation Waves
Wave 1 (parallel):
- [x] [task-01-proto-contract](task-01-proto-contract.md)
- [x] [task-02-session-loc-aggregation](task-02-session-loc-aggregation.md)
Wave 2:
- [x] [task-03-sdk-data-accessors](task-03-sdk-data-accessors.md)
Wave 3:
- [x] [task-04-host-data-impl](task-04-host-data-impl.md)
Wave 4 (parallel):
- [x] [task-05-rewrite-agent-stats-plugin](task-05-rewrite-agent-stats-plugin.md)
- [x] [task-06-tests](task-06-tests.md)
- [x] [task-07-docs](task-07-docs.md)
Rationale: task-01 (contract) and task-02 (aggregation) touch disjoint packages and can run in parallel. The SDK (task-03) needs generated stubs from task-01. The kandev impl (task-04) needs the stubs (task-01), the aggregation (task-02), and the Go-native interface/DTOs (task-03). Wave 4 tasks touch disjoint trees (a separate plugin repo, backend tests, public docs) and parallelize.
Open Questions
- Proto placement: RPCs on the existing
service Hostvs a newservice HostDataserved on the same broker connection. Recommendation: extendservice Hostto reuse the existing capability interceptor and single broker wiring; revisit only if the interceptor's per-RPC capability mapping gets unwieldy. Settle in task-01. - Aggregation home:
internal/analytics(where git stats already live) vs a new method on the task repository'sgit_snapshots.go. Recommendation: analytics repository, consistent withGetGitStats/GetRepositoryStats. Settle in task-02.