Files
kandev/docs/decisions/2026-08-31-generic-plugin-host-boundary.md
T
2026-09-01 07:19:09 +00:00

44 KiB
Raw Blame History

ADR-2026-08-31-generic-plugin-host-boundary: Generic plugin Host boundary and capability approvals

Status: proposed Date: 2026-08-31 Area: backend, frontend, protocol, security, workflow

Context

The Coordinator product is moving into a separately released plugin. Its policy, scheduling, durable state, reconciliation, prompts, reports, tools, and product UI belong to that plugin, but it still needs a supported way to observe and change Kandev state. The current Host contract is too small for that use case, and its manifest declarations are not an operator approval ledger.

The preserved migration baselines prove useful shapes but also show the boundary failure. Host PR #2793 at afd2b699bfe9b6af9353ea01728582f61a7be2be combines generic conversation and relation work with Coordinator-specific principals, grants, storage, and UI. Plugin PR #1 at 5bfdbcf7d9608d1210453f95ebfc8f66c3179225 keeps product state in the plugin but uses dispatch bookkeeping rather than a durable intent/outbox protocol. Both heads are read-only migration evidence, not branches to extend or close here.

This decision freezes generic public names and invariants so later implementation units do not invent Coordinator concepts in core. It defines contracts; it does not implement them.

Decision

Ownership and sanctioned call chain

Coordinator identity, policy, scheduling, durable SQLite state, reconciliation, prompts, reports, namespaced agent tools, and Overview/Chat/Reports/Policy/Audit UI remain plugin-owned. Kandev owns only generic Host contracts, shared domain commands and queries, capability approval, audit/event infrastructure, and generic UI/config primitives.

The only production call chain is:

plugin-managed agent
  -> namespaced plugin-owned agent tool
  -> plugin backend
  -> capability-gated generic Host RPC
  -> shared Kandev domain command or query
  -> repository and event infrastructure

An ordinary task agent may reach the same named domain command or query through a global MCP adapter. Host RPC and MCP request DTOs may differ, but neither adapter owns workflow, WIP, authorization, ownership, idempotency, lifecycle, relation, task-run, pending-transition, or audit rules.

A plugin must never integrate through ambient/global Kandev MCP, private or undocumented REST, direct database access, shelling into Kandev, or prompt/comment text treated as authority. Core must not gain a Coordinator table, field, profile, role, principal, grant, setting, tool, or audit vocabulary.

Exact DTOs and result vocabulary

Every new exact Host request introduced by this ADR carries request_id and workspace_id. Operations that need an explicit approval-generation precondition also carry capability_revision; ordinary calls use current Host-side authorization. Every new exact writer also carries idempotency_key, exact target resource versions, and a PendingTransitionGuard that is either expect_none or { pending_transition_id, resource_version }. An agent-originated writer additionally carries ManagedExecutionProvenance with conversation_id, session_id, and execution_generation_token. The Host derives installation identity; callers cannot supply or override it.

Every list request carries Page { limit, cursor, snapshot_version }; responses carry PageInfo { next_cursor, has_more, snapshot_version }. Cursors are opaque and bound to workspace, filters, snapshot version, capability revision, and installation. Every mutable/readback resource carries resource_version.

This requirement does not retroactively add fields to any shipped v1 request. The legacy compatibility table below names the only compatibility path. A new exact RPC has a distinct *Exact wire method, DTO, and host.v2.* capability; it never accepts a missing workspace or revision as a synthetic legacy approval, and it never reuses api_read:* or api_write:* as authority. Implementations reject absent exact fields as INVALID, except that an existing v1 method continues to use its listed v1 behavior. The Host derives installation_id in both cases; it is never caller input.

Writers return a typed HostCommandResult with one of these stable states:

State Meaning
APPLIED The command committed and the response contains authoritative readback.
ALREADY_APPLIED The same idempotency identity already committed with the same canonical digest.
NO_CHANGE Preconditions held, but authoritative state already matched the requested outcome.
CONFLICT A target version, pending-transition predicate, directive state, or generation changed.
DENIED Current approval, provenance, or immutable Human policy rejects the operation.
NOT_FOUND The exact workspace-scoped target does not exist or is not visible to the principal.
INVALID The request is structurally or semantically invalid.
UNSUPPORTED The Host version or provider cannot satisfy the named contract.
RATE_LIMITED Provider rate budget prevents the operation; reset metadata is present when known.
UNAVAILABLE A required service is temporarily unavailable and no side effect is claimed.
PARTIAL Only a contract that explicitly defines durable progress may use this; completed receipts are included.

HostCommandResult also carries audit_id, result_resource_version, and an authoritative readback or readback reference. Each stable reason has exactly one parent state and retry model:

Parent state Stable reasons Client behavior
DENIED STALE_CAPABILITY_REVISION, CAPABILITY_REVOKED, HUMAN_RESERVED, MISSING_CAPABILITY_APPROVAL, PROVENANCE_REJECTED Fail closed. Park the action and require Human approval, reconfiguration, or an eligible fresh installation; never retry automatically.
CONFLICT STALE_RESOURCE_VERSION, PENDING_TRANSITION_CONFLICT, EXECUTION_GENERATION_FENCED, DIRECTIVE_REVOKED, DIRECTIVE_EXPIRED, DIRECTIVE_ALREADY_RESOLVED, IDEMPOTENCY_MISMATCH Do not repeat the side effect. Read back authoritative state; retry only with a newly constructed operation when that state permits it.
NOT_FOUND WORKSPACE_TARGET_NOT_FOUND Stop against this identity and refresh the caller’s projection.
INVALID MISSING_EXACT_FIELD, MALFORMED_PRECONDITION, UNSUPPORTED_LEGACY_SHAPE Correct the request or use the documented v1 method; no automatic retry.
UNSUPPORTED HOST_VERSION_UNSUPPORTED, PROVIDER_CONTRACT_UNSUPPORTED, UNKNOWN_RESULT_VALUE Park pending an upgrade or a separately supported contract.
RATE_LIMITED PROVIDER_RATE_LIMITED Retry only after the supplied reset/Retry-After boundary.
UNAVAILABLE DEPENDENCY_UNAVAILABLE Retry the same idempotency identity using bounded backoff.

Transport status codes may classify the same outcome, but clients branch on the typed result and tolerate unknown enum values as UNSUPPORTED.

H6 capability context bootstrap

Before the first exact call, a plugin invokes GetCapabilityContext on its connection-bound Host. The request carries only request_id; the Host derives the installation identity from the plugin connection. The response reports the Host contract version, context revision, approved workspace capability revisions, capability IDs, manifest digest, and approval status. It grants no authority. Each exact operation still uses current Host-side authorization and supplies a capability revision when that operation requires an explicit approval-generation precondition.

The Host emits capability-context-changed after an approval, reduction, revocation, upgrade, or policy change. A stale exact request returns DENIED/STALE_CAPABILITY_REVISION; the plugin constructs a new request rather than retrying the side effect. An empty workspace list is a valid degraded state.

Legacy v1 compatibility fence

The current kandev.plugin.v1.Host surface is a shipped, manifest-declaration API, not the H6 approval protocol. It remains callable only with the following named method/capability behavior; its omitted fields mean v1 compatibility, not an approval revision. A v1 call cannot invoke a new exact writer, issue a directive, consume a continuation, or gain a host.v2.* capability.

Existing v1 methods Existing authority Absent-field behavior H0 boundary
GetState, SetState, DeleteState, ListState; GetSecret, SetSecret, DeleteSecret; RevealSecret; EmitEvent; InvokeUtilityAgent Their existing manifest declarations (state, secrets, events, agent_invoke) No workspace or capability revision is inferred. Existing plugin-scoped semantics continue. Not an H1-H5 exact operation; no H6 authority is created.
GetConfig(GetConfigRequest {}) Ungated, plugin-global operator configuration The empty request remains empty and has no workspace, approval, or capability revision. It returns only the calling plugin’s own config under the existing secret policy. It is explicitly outside H6. A future scoped configuration operation, if needed, must be a new versioned successor and cannot change GetConfig.
ListTasks, GetTask, ListWorkspaces, ListWorkflows, ListWorkflowSteps, ListAgentProfiles, ListExecutorProfiles, ListRepositories, ListSessions, ListSessionCodeStats, ListMessages, ListPendingInteractions, GetInteraction The exact existing api_read:<resource> declaration documented in plugin.proto Existing request fields remain optional/as shipped; no workspace or revision is synthesized. H2 exact projections use separately named *Exact methods and host.v2.read:* capabilities. v1 reads cannot satisfy an H0 exact-read receipt.
CreateTask, UpdateTask, MoveTask, SendMessage, PreviewPluginOwnedTaskTree, DeletePluginOwnedTaskTree, RespondToPermission, AnswerClarification, CancelClarification The exact existing api_write:<resource> declaration documented in plugin.proto Existing request fields remain as shipped; no workspace, H6 revision, guard, or provenance is invented. They are not H3/H4/H5 exact writers. A plugin needing an H0 writer must call the new exact method with a host.v2.write:* approval.

No synthetic legacy revision exists. Existing installed plugins keep only the v1 behavior they already had, subject to their existing manifest declaration and ordinary lifecycle. A new installation using a legacy v1 writer additionally requires a server-side compatibility grant scoped to that installation; the grant cannot authorize any new exact capability or bypass H6/C1/C2 safeguards.

Public Host surface inventory

The wire service remains kandev.plugin.v1.Host. The namespace column is the permanent SDK shape; the RPC names in the method column are permanent wire names. HostReadReceipt identifies the installation, workspace, capability revision, request, and snapshot read. HostAuditReceipt additionally records before/after versions, idempotency identity, provenance when present, and the command result.

Unit Permanent service / namespace Permanent method names Request DTO Response and result states Page, version, idempotency, and preconditions Declared capability Audit / event identity Shared domain owner
H1 managed conversations Host.ManagedAgentConversations EnsureManagedAgentConversationExact; DispatchManagedAgentConversationExact; GetManagedAgentConversationStatusExact; ListManagedAgentConversationsExact; DeleteManagedAgentConversationExact Corresponding *ExactRequest DTOs ManagedAgentConversationResult: common states plus CONFIGURATION_REQUIRED, BUSY, FENCED, and descriptor/status readback List uses Page; every descriptor has resource_version; writers carry idempotency, capability revision, exact descriptor version, transition guard, and generation token where continuing a run host.v2.read:managed_agent_conversations; host.v2.write:managed_agent_conversations HostReadReceipt; HostAuditReceipt; managed_agent_conversation.* with audit_id ManagedAgentConversationQuery and ManagedAgentConversationCommand; H1
H1 generic chat UI host.ui.WorkspaceAgentChat WorkspaceAgentChat(props) WorkspaceAgentChatProps { workspaceId, conversationId, resourceVersion, readOnly?, onStatus? } Host-owned loading, ready, configuration-required, fenced, error, and read-only states The component consumes H1 versions and never receives authority through props No new capability; backing H1 calls remain gated UI lifecycle generation and H1 receipts Host UI primitive; H1
H2a topology and runtime projections Host.Workspaces, Host.Workflows, Host.Tasks, Host.Sessions, Host.Interactions ListWorkspacesExact; ListWorkflowsExact; ListWorkflowStepsExact; ListTasksExact; GetTaskExact; ListSessionsExact; ListPendingInteractionsExact; GetInteractionExact New exact workspace-explicit filters plus Page; exact getters use workspace plus id Bounded projection DTOs with per-item resource_version; pending interactions preserve their typed terminal states Every list uses page/snapshot binding; no read filter grants authority host.v2.read:workspaces, host.v2.read:workflows, host.v2.read:tasks, host.v2.read:sessions, host.v2.read:interactions HostReadReceipt; no mutation event WorkspaceTopologyQuery, TaskProjectionQuery, SessionProjectionQuery, InteractionQuery; H2a
H2b communications Host.Messages, Host.TaskInbox, Host.TaskDirectives ListSanitizedMessagesExact; ListTaskInboxExact; GetTaskDirectiveExact; ListTaskDirectivesExact New exact workspace/task/session filters, sanitization level, directive id, Page Sanitized message, durable inbox item, and directive status/readback; terminal directive states remain queryable Snapshot-bound pages; items carry resource versions and canonical digests host.v2.read:messages, host.v2.read:task_inbox, host.v2.read:task_directives HostReadReceipt; no mutation event TaskCommunicationQuery, TaskInboxQuery, TaskDirectiveQuery; H2b
H2c relations and pending transitions Host.TaskRelations, Host.TaskTransitions GetTaskRelationsExact; ListTaskRelationsExact; ListPendingTaskTransitionsExact New exact task/workspace filters plus Page Relation graph with edge versions and child/dependency closure; pending transition with predicate and resource version Snapshot-bound pages; versions are consumable by H3b/H3c/H5 host.v2.read:task_relations, host.v2.read:task_transitions HostReadReceipt; no mutation event TaskRelationQuery, TaskTransitionQuery; H2c
H2d exact change evidence Host.ChangeRequests GetChangeRequestEvidenceExact; ListChangeRequestEvidenceExact Canonical provider, workspace, repository, base, head, immutable head/merge-ref identity, and Page for the result or a named nested collection Exact checks/jobs, review, thread, mergeability, divergence, rate budget, and receipt projections described below Snapshot/resource version binds provider, repository, base, immutable head, merge ref, provider connection generation, and nested cursors host.v2.read:change_requests HostReadReceipt plus provider read receipt ChangeEvidenceQuery; H2d
H2e terminal provenance Host.TerminalResources GetTerminalResourceProvenanceExact Exact workspace/task, optional closure depth, and Page plus section key for every nested collection Canonical change disposition, resources, consumers, closure, interactions, cleanup ownership, and prior receipts described below One exact provenance resource_version; every nested page binds to it; no cleanup authority is implied host.v2.read:terminal_resources HostReadReceipt; no mutation event TerminalResourceQuery; H2e
H3a messaging and directives Host.Messages, Host.TaskDirectives SendMessageExact; IssueTaskDirectiveExact; ResolveTaskDirectiveExact SendMessageExactRequest, IssueTaskDirectiveExactRequest, ResolveTaskDirectiveExactRequest Common results; directive readback includes pending, resolved, revoked, expired, and generation-fenced states Capability revision, idempotency, target/session versions, transition guard; agent origin adds managed execution provenance; directive semantics are frozen below host.v2.write:messages, host.v2.write:task_directives HostAuditReceipt; message.dispatched; task_directive.issued|resolved|revoked MessageCommand, TaskDirectiveCommand; H3a
H3b task writers Host.Tasks CreateTaskExact; MoveTaskExact; UpdateTaskExact; SetTaskLabelsExact Exact create/placement, move, field-mask, and label requests Common results with authoritative task readback Capability revision when required, idempotency, exact task/workflow/step versions as applicable, transition guard, optional managed execution provenance host.v2.write:tasks HostAuditReceipt; existing task.* events gain audit_id TaskCommand; H3b
H3c relation writers Host.TaskRelations AddTaskRelationExact; RemoveTaskRelationExact Exact workspace, source, target, relation kind, edge/endpoint versions Common results with authoritative relation-graph readback Capability revision, idempotency, endpoint and edge versions, transition guard, optional managed execution provenance host.v2.write:task_relations HostAuditReceipt; task_relation.added|removed TaskRelationCommand; H3c
H3d provider actions No Host writer is approved in H0 No RPC or SDK method is reserved None UNSUPPORTED if a plugin assumes one None None None Deferred independently as recorded below; H3d
H4 exact execution lifecycle Host.TaskRuns, Host.Sessions EnsureTaskRunExact; RecoverSessionExact Exact task/workstep/session, desired execution identity, prompt/action digest, dispatch idempotency, expected versions Common results plus one admissible execution_generation_token and authoritative run/session readback Capability revision, idempotency, exact task/workstep/session/run versions, transition guard; replacement must fence the prior generation first host.v2.write:task_runs, host.v2.write:sessions HostAuditReceipt; task_run.ensured; session.recovered; generation-fenced lifecycle events TaskRunCommand, SessionRecoveryCommand; H4
H5 pending transitions Host.TaskTransitions CancelPendingTaskTransitionExact Exact transition id/version and cancellation reason Common results plus optional single-use continuation token and authoritative readback Cancel requires capability revision, idempotency, exact transition version, and current target version host.v2.write:task_transitions HostAuditReceipt; task_transition.cancelled TaskTransitionCommand; H5
H7 generic config and UI manifest config schema, legacy Host.GetConfig, host.ui, plugin registry legacy GetConfig(GetConfigRequest {}); formats agent-profile and textarea; numeric minimum/maximum; WorkspaceAgentChat; registerIntegrationSettings; Integrations navigation Existing JSON Schema config and registry DTOs Typed validation/configuration-required states; revocable registration handles GetConfig remains plugin-global v1; a later exact scoped config API must use a new DTO/version No H6 capability for legacy config rendering; each new exact backing API uses its own host.v2.* capability Config revision and owner lifecycle generation; no domain event Plugin config/registry owners; H7

No public Host name in this inventory depends on any product plugin being installed.

H2d exact-head evidence

ChangeRequestEvidence contains canonical provider ID and connection generation; workspace; immutable repository ID plus canonical owner/name; base branch and base commit; head branch and immutable head commit; immutable merge-ref identity when the provider exposes one; draft, mergeability, conflict, and base-divergence states; every exact check-run or job identity, status, conclusion, URL, and timestamps; causal relationships from a check/job to its aggregate; review decision; requested and completed reviewers; unresolved actionable thread count; provider rate budget and reset; notification receipts; provider-action receipts; snapshot/resource version; and provider read receipt. Aggregate green is never evidence that each causal job passed, and an updated head invalidates evidence and action receipts from the old head.

H1 result mapping

H1-specific statuses are readback values, not additional HostCommandResult states. When an H1 operation cannot proceed, the common result uses the parent state and stable reason below.

H1 status Parent state Stable reason Client behavior
CONFIGURATION_REQUIRED DENIED HOST_CONFIGURATION_REQUIRED Park until the required Host configuration is present.
BUSY CONFLICT CONVERSATION_BUSY Read current status and construct a new operation after it is available.
FENCED CONFLICT EXECUTION_GENERATION_FENCED Stop the old generation and read the current generation before retry.

H2e terminal resource provenance

TerminalResourceProvenance contains canonical change and commit disposition; pushed, contained, unpushed, and orphan history; owned worktree; local and remote branches; active sessions, turns, processes, runtimes, data, and artifact consumers; child and dependency closure; pending interactions and transitions; cleanup owner; prior preview/action receipts; and its resource version. Done placement or merged status never grants cleanup authority.

Any later cleanup writer is a separate capability and command. It must be exact, provenance-bound, policy-gated, dry-run capable, idempotent, auditable, and followed by authoritative readback. Until that writer is separately approved, a plugin may only report a Human cleanup action.

C1: installation authority and execution provenance

The Host principal is PluginInstallationPrincipal { installation_id, workspace_id, capability_revision }. It authorizes deterministic scheduler, startup repair, reconciliation, event recovery, and outbox retry without requiring a live managed conversation.

An agent-originated exact write additionally binds the exact managed conversation, session, and execution-generation fencing token. This provenance proves which execution requested an operation; it never creates, delegates, or widens installation authority. A missing, revoked, or stale approval revision is DENIED/STALE_CAPABILITY_REVISION or DENIED/CAPABILITY_REVOKED; a stale execution token is CONFLICT/EXECUTION_GENERATION_FENCED.

C2: atomic pending-transition safety

Every writer that can wake, redirect, message, create work for, relate, or transition a task accepts an exact PendingTransitionGuard. Its shared domain command atomically revalidates the guard in the same transaction or serialized critical section as the side effect. A separate list followed by send, move, ensure, or update is not a safety proof.

CancelPendingTaskTransitionExact may return a single-use continuation token bound to the cancelled transition, target versions, installation, capability revision, idempotency identity, and expiry. A following writer either consumes that token atomically or revalidates the now-current pending-transition predicate itself.

C3: non-amplifying TaskDirectives

A TaskDirective is bound to installation, capability revision, workspace, exact task, exact capability class, canonical instruction/action digest, issue time, expiry, revocation state, single-resolution state, optional task/workflow/session- generation preconditions, stable idempotency identity, and Host audit ID.

Worker admission intersects all of those fields with the current installation approval and immutable Human-reserved policy. A directive cannot delegate itself, widen through prose, authorize another operation, survive capability revocation, resolve twice, or replay into a replacement execution generation. Failed admission returns a typed denial or conflict and never partially dispatches.

C4: execution-generation fencing and durable dispatch

EnsureTaskRunExact and RecoverSessionExact establish exactly one admissible task/workstep execution generation and return its fencing token. Messages, directives, prompt admission, completion, and transition signals for that run require the token. Replacement terminally fences the previous generation before dispatch.

The plugin persists an action intent/outbox record before any Host dispatch. It then records the Host acknowledgment, audit_id, result, and authoritative readback. Recovery retries the same idempotency identity. Failed-session unread queues are diagnostic evidence, not durable work state.

C5: terminal provenance before cleanup

H2e is the sole generic summary of terminal resources and consumers. Cleanup is never inferred from a workflow column or change-request state. A missing provenance field, active consumer, stale resource version, or unknown ownership causes a Human cleanup action, not optimistic deletion.

C6: exact-head provider evidence

H2d binds every provider observation and future action receipt to canonical provider/repository/base/head and immutable head/merge-ref identity. Any future H3d command is a separate capability with exact-head, current-state, rate-budget, and idempotency preconditions. Merge is not a Host writer in this decision.

C7: permanent implementation decomposition

H2 and H3 are documentation umbrellas only. Permanent implementation/task/PR units are H2a through H2e and H3a through H3d. Each has one owner/worktree/PR unless source evidence proves two shapes inseparable. No implementation unit may recreate a Coordinator-specific core concept to avoid one of these generic boundaries.

Capability model and H6 decision

Decision: H6_REQUIRED.

Current source can enforce only a manifest declaration copied into a global installed record. It cannot represent the principal or approval revision required above.

Source evidence What exists Why it is insufficient
apps/backend/internal/plugins/manifest/manifest.go (Capabilities) Manifest-owned events, api_read, api_write, and boolean declarations A package declares its own ceiling; there is no Human-approved workspace set, revision, or revocation record.
apps/backend/internal/plugins/store/store.go (Record) One filesystem installation record embeds the manifest and runtime status The record is instance-global and has no workspace approval, approval history, actor, or immutable audit identity.
apps/backend/internal/plugins/service_install.go (Service.Install) Admin-only install validates and saves the package manifest Install does not persist a reviewed capability set distinct from the package declaration; an upgrade replaces the embedded manifest.
apps/backend/internal/plugins/service.go (hostForPlugin) A Host instance copies rec.Capabilities at spawn/restart Authorization knows plugin ID and declared capabilities only, not workspace, current approval revision, revocation, or execution provenance.
apps/backend/internal/plugins/host.go and host_data.go Per-method declaration checks return PermissionDenied The checks do not intersect an operator approval or produce a generic Host audit receipt.
apps/backend/internal/task/models/models.go (PermissionResolutionAudit) Narrow audit for agent permission resolution It is not a generic plugin command/audit ledger and cannot identify installation capability revisions.

The smallest extension is owned by the plugin system:

Contract Required shape
PluginCapabilityApproval One current row per (installation_id, workspace_id) with monotonic revision, manifest capability digest, approved capability IDs/classes, status active|revoked, approving Human actor, issued/updated timestamps, and immutable Human-policy version.
PluginCapabilityApprovalEvent Append-only grant, narrow, revoke, and upgrade-review events with audit_id, before/after revisions and digests, actor, reason, and timestamp.
Authorization The effective set is manifest declaration ∩ current workspace approval ∩ immutable Human policy. The request revision must exactly equal the active revision.
Revocation Revocation increments the revision, denies new exact calls immediately, fences outstanding directives/tokens, and returns DENIED/CAPABILITY_REVOKED to an old approval.
Upgrade Equal or narrower declarations retain the approved set at a new recorded manifest digest and a new approval revision. Any widening remains unavailable until a Human approves a new revision; activation fails closed for required unavailable capabilities.
Compatibility The legacy-v1 compatibility fence governs shipped RPCs. Every H1-H5 exact capability named here requires an explicit host.v2.* workspace approval revision; no v1 declaration implicitly gains it.
Audit Every H1-H5 read receipt and write receipt records installation, workspace, exact approval revision, method/capability, request/idempotency digest, result, resource versions, and managed execution provenance when present.

No Coordinator principal, role, profile, grant, or audit type is created.

installation_id is a Host-minted opaque UUID created at install and never derived from plugin ID, package digest, workspace, or path. Its lifecycle is part of the approval authority:

Lifecycle event installation_id and approval behavior
Upgrade Retains the same installation_id; records the new manifest digest and a new approval revision. Equal/narrower capability declarations preserve only the intersected active approvals. A widened declaration is inactive until a Human grants the widened host.v2.* set.
Rollback Retains the same installation_id; records the rollback manifest digest and a new approval revision. It restores no prior approval implicitly: the active set is the current manifest intersection and current Human approval.
Uninstall Immediately revokes every active approval, increments each revision, fences directives/tokens, and tombstones the installation_id plus approval-event history for audit. It cannot be reused for authorization.
Reinstall Mints a fresh installation_id even for the same plugin ID/package/workspace. It starts with no H6 approval; a Human must create new workspace approvals. Tombstoned authority and old receipts remain historical only.

Human-reserved and non-delegable matrix

“Agent provenance” below means it is required when the request originated in a managed agent execution. Installation approval is always required even when that column says no.

Capability class Installation approval may authorize Agent provenance additionally required Exact preconditions Human-reserved
Read-only projections Yes No Workspace, capability revision, snapshot/resource version No, except secret values and cross-workspace data
Managed conversation ensure/status/list/dispatch Yes Only for an agent-originated continuation Conversation/config versions; idempotency; transition guard; generation token for continuation Delete is reserved when unique/still-needed content is not proven disposable
Routine messages and task writes Yes Yes for agent-originated writes; no for deterministic backend reconciliation Target/workflow/step versions, transition guard, idempotency, optional continuation token No, unless the requested field/action enters another reserved row
Trusted directive issue/resolve Yes, as a separately approved class Yes for agent-originated issue/resolve Directive digest/state, target versions, generation, expiry, transition guard Cannot authorize any Human-reserved class
Relations and pending-transition cancellation Yes Yes for agent-originated writes Endpoint/edge or transition versions, transition guard, idempotency Destructive relation effects remain subject to dependency policy; no ToDeploy override
Task-run ensure and session recovery Yes Yes for agent-originated recovery; no for deterministic repair Exact task/workstep/session/run versions, generation fencing, transition guard, outbox idempotency No, but replacement must fence rather than rewrite history
Provider retry/rerun, draft-to-ready, reviewer notification Not in H0 N/A Would require exact head, current state, idempotency, rate budget, audit, and readback Deferred; no authority exists
Removal of unique or still-needed state No N/A Exact provenance, dry-run receipt, Human confirmation, current versions Yes
Security/trust-boundary changes No N/A Human-authenticated exact change and policy version Yes
Secret/credential disclosure or scope expansion No N/A Human-authenticated secret scope and audit Yes
Cross-workspace access No N/A Separate Human approval per workspace; no wildcard delegation Yes
Force-push, rebase, squash, amend, or history rewrite No N/A No Host writer defined Yes
Merge No N/A No Host writer defined Yes
Release No N/A No Host writer defined Yes
Deploy No N/A No Host writer defined Yes

H3d provider-action decision

H3d is explicitly deferred. Existing provider-neutral UI callbacks do not satisfy a durable backend command boundary, and the built-in GitHub writer does not provide the exact-head/idempotency/audit contract required here.

Candidate action Decision Provider-neutral target and exact-head/current-state preconditions Idempotency and notification identity Capability and result/rate-budget behavior Audit receipt and authoritative readback Source evidence and missing invariant
Retry or rerun Deferred Must bind provider, repository, immutable change identity, exact head/merge ref, check/job identity, and current rerunnable conclusion. Must use one key per exact head/check attempt; no notification identity applies. A future host.v2.write:change_actions result must expose typed outcome and provider reset metadata; no capability is reserved now. Must return HostAuditReceipt, provider action receipt, and exact post-action check readback. apps/backend/internal/github/client.go exposes check reads but no provider-neutral check/job rerun command. Providers differ on workflow, pipeline, job, and check-suite identity.
Draft to ready Deferred Must bind provider, repository, immutable change identity, exact head/merge ref, and current draft state. Must be idempotent per exact head and current draft state. A future host.v2.write:change_actions result must expose typed outcome and provider reset metadata; no capability is reserved now. Must return HostAuditReceipt, provider action receipt, and exact post-action draft readback. apps/packages/plugin-sdk/src/index.ts exposes supportsDraft and draft creation only. There is no shared backend transition command or provider receipt.
Reviewer notification or re-request Deferred Must bind provider, repository, immutable change identity, exact head/merge ref, reviewer identity, and current review state. Must use a notification-once-per-head key for each reviewer and request kind. A future host.v2.write:change_actions result must expose typed outcome and provider reset metadata; no capability is reserved now. Must return HostAuditReceipt, provider action receipt, and exact post-action reviewer readback. apps/backend/internal/github/service_pr.go accepts repository/number/reviewer inputs without immutable-head binding, generic semantics, audit, or authoritative readback.

No capability string, RPC, or provider action is reserved for these candidates. A later H3d ADR may include an action only after defining provider-neutral target identity, immutable head/current state, idempotency, notification-once-per-head when applicable, rate-budget behavior, audit receipt, and authoritative readback. Merge is excluded.

Versioning and compatibility

  • kandev.plugin.v1 and public SDK DTOs evolve additively. Removing/renaming fields, changing field meaning, or reusing enum numbers requires a new protocol version.
  • Unknown fields are ignored and preserved where the runtime permits. Unknown enum or result values map to typed UNSUPPORTED; they never fall through to success.
  • Capability IDs and result strings are permanent once shipped. A retired capability identity is never reused for another meaning.
  • The plugin manifest's existing min_kandev_version is the declaration consumed by the Host. A plugin that needs this surface sets it to the first stable Kandev release that implements every required capability. H0 does not guess that future version.
  • Plugin upgrade follows the H6 revision rules above. A missing required capability, stale approval revision, or older Host yields UNSUPPORTED/DENIED; the plugin parks the action and reports a configuration/upgrade requirement. It must not fall back to MCP, private REST, SQL, or shell.
  • Host RPC and global MCP authorization is tested separately before parity: Host authenticates PluginInstallationPrincipal and H6 approval/revision, while MCP authenticates its ordinary user/session principal and policy. Only after each adapter authorizes its own principal do parity tests invoke the same named domain query/command and compare shared-domain preconditions, idempotency, conflicts, mutation, audit/readback, and error vocabulary. A Host approval denial is never expected to equal an MCP authorization verdict.
  • Events are at-least-once hints with event_schema_version, resource_version, audit_id, installation/workspace/capability revision, and immutable resource identity. Unknown events may be ignored. Dropped, duplicated, delayed, or reordered events are repaired by authoritative Host readback; event text is never authority.
  • Rollout order is H6 approval/audit foundation, then required read contracts, then exact writers, then the plugin adapters/UI. The plugin stays degraded and performs no unsafe write when any required Host capability is absent.
  • Exact-version integration I1 records accepted Host and plugin commit IDs, their declared minimum/actual versions, capability revision, and compatibility-test receipts. A moving branch or aggregate “latest” version is not evidence.

Migration and disposition ledger

H0 does not close, message, move, rewrite, repurpose, or mutate any listed task or PR.

Evidence identity Disposition and reusable evidence Replacement owner / sequencing
Task 9e67c426-1300-46ef-a00f-e5603791212d; host PR #2793 at afd2b699bfe9b6af9353ea01728582f61a7be2be Keep as monolithic read-only migration baseline. Extract conversation proto/SDK/service/test patterns from commits e7ea57a1, e13ca6cd, 76e44a74, 9d2e0733, ad843976, and 4baa37d8; supersede Coordinator authority/storage/UI (internal/coordinator, workspace-agent principal/grant shapes). H1 owns conversations; H2c owns relations; H6 owns generic approval/audit. Extract only after H0.
Plugin PR #1 at 5bfdbcf7d9608d1210453f95ebfc8f66c3179225; tasks 52892e8e-dc44-4d38-80ab-14bb75f7b6bf, 3ec598a8-b49d-4d5f-9bf3-52b0d611cd32 Keep plugin/recovery baseline. Preserve server/coordinator/{config,prompt,reports,state,tools}.go and ui/src/** as plugin-owned evidence. Supersede scheduler.go dispatch bookkeeping with C4 intent/outbox and Host readback. P1/P2 consume stable H1/H2 contracts; plugin recovery follows exact accepted Host head.
PR #2756 Keep scheduler history (internal/scheduler/cron, session-wake repository/service/tests). Do not close until a linked plugin scheduler replacement exists. Plugin scheduler/P1 after H0 and stable Host reads.
Task c642d57a-5a24-48ca-8f85-57d31115eeb5; PR #2909 Retain generic automatic exact-turn repair and durable delivery evidence in lifecycle, orchestrator message queue, completion intent, and stale-session tests. H4 extracts explicit TaskRuns.EnsureExact / Sessions.RecoverExact; it does not replace generic repair.
Task 9349b6e5-a167-4d88-af14-cb355015e3dd; PR #2841 Extract relation projection/service/wire tests (host_task_relations.go, handoff_access.go, related-task authorization tests). Supersede Coordinator-specific MCP authorization. H2c, then H3c.
Task ec384aac-cd4f-469c-8893-3aa8383da9d6; PR #2974 Retain task-inbox queue identity, bounded polling, transition, and trusted-principal evidence (list_task_inbox, messagequeue repository/service/tests). Do not reuse task-specific mutation authority. H2b read projection only; H3a later owns exact writers.
Task fa3fba49-2018-460b-a600-adae23b24cc8; PR #3048 Security-decision input only. Reuse adversarial provenance/audit test ideas; do not continue internal/coordinator, coordinator grants, UI, routes, tables, or MCP authority. H6 generic plugin/workspace approval and Host audit extension.
Task b2da5061-07a3-46e6-ab48-3881929ac9a5; merged PR #3147 Keep platform TTL/orphan invariant and row-local/race-safe pending-move sweep tests. Never recreate it in the plugin. H2c observes; H5 composes with the retained platform invariant.
Task 7056a702-a3c3-4fe8-8535-c6b8d340ef6a; PR #3155 Retain exact compare-and-delete transaction history in pending_move_exact_cancel.go and its repository/service tests. No H0 mutation or disposition action. H5 after H2c and this capability identity; C2 applies to every following writer.
Task 531a41cd-57ef-495a-8dfa-614d2a4d0d52 Preserve the supplied Human-owned ToDeploy boundary as generic H3c design input only. Do not inspect or mutate the protected task. H3c after H2c; no task-specific authority.
Task afdb2ef3-06ca-4cd5-a074-c4e691679da9 Preserve isolated QA baseline. H0 does not reuse moving version inputs. Exact-version I1 replaces version inputs on accepted Host/plugin heads.

Dependency and delivery graph

  1. H0 completes first.
  2. H1, H2a through H2e, and H7 may then proceed independently where file ownership permits.
  3. H3a follows H2a/H2b; H3b follows H2a/H2c; H3c follows H2c; H3d follows H2d only after a later action decision.
  4. H4 follows H1, H2a, H2b, and H3a directive semantics.
  5. H5 follows H2c and H0 capability identity. C2 must reach every affected writer before plugin messaging/recovery is safe.
  6. P1 follows H0 and stable H1/H2 state shapes. Each P2 adapter follows its Host dependency; P3 follows H7 and stable P1 view models.
  7. I1 runs only on exact accepted Host/plugin heads and the declared minimum version.
  8. Every node independently traverses Work, Review, QA, PR/CI, and semantic Human-QA. None of those phases authorizes merge, deploy, release, or ToDeploy.

Consequences

  • The Coordinator remains independently releasable and core gains only reusable plugin contracts.
  • H6 is mandatory before any H1-H5 capability named here can authorize a write.
  • The public contract is larger and intentionally exact: resource versions, transition predicates, generation tokens, audit receipts, and readback add work to every adapter but prevent split-brain automation.
  • H3d has no implementation unit until provider-neutral semantics exist.
  • Existing Host v1 behavior remains compatible for existing plugins, but it does not implicitly grant the new surface.

Alternatives Considered

Continue the monolithic host PR

Rejected because it makes product identity, grants, scheduling, and UI core concepts and couples Coordinator delivery to a Kandev release.

Let the plugin call global MCP or private REST

Rejected because ambient authority, consumer-specific authorization, and network topology bypass installation capability revisions and cannot guarantee command parity.

Treat the manifest as Human approval

Rejected because a package authors its own manifest, upgrades can widen it, and the current record has no workspace revision, revocation history, or generic audit.

Use list-then-write concurrency checks

Rejected because another writer can change the pending transition, run generation, or target resource between calls.

Approve provider actions now

Rejected because current source does not prove stable provider-neutral identities or exact-head/idempotency/audit semantics for any of the three candidates.