Tasks — add-console-connection-revoke-delete-controls
tasks17/17
Created Updated openspec/changes/add-console-connection-revoke-delete-controls/tasks.mdView on GitHub →
1. Owner-session /_ref revoke + delete adapters (shared cascade)
- 1.1 Extend
MountRefConnectorsContextinreference-implementation/server/routes/ref-connectors.tswith the shared revoke/delete dependencies:updateConnectorInstanceStatus,deleteConnection,getOwnerSubjectId(already present),emitSpineEvent,createTraceContext,ensureRequestId,setReferenceTraceId,canonicalConnectorKey(already present). - 1.2 Add
mountRefConnectionRevoke(app, ctx)→POST /_ref/connections/:connectorInstanceId/revoke: resolve+verify the connection viaresolveRefConnectionNamespace, callctx.updateConnectorInstanceStatus(id, { status: 'revoked', updatedAt, revokedAt }), emitowner_agent.connection.revokeaudit, return the revoked projection. - 1.3 Add
mountRefConnectionDelete(app, ctx)→DELETE /_ref/connections/:connectorInstanceId: callctx.deleteConnection(id, { ownerSubjectId, now }), emitowner_agent.connection.deleteaudit with the deletion summary, return the summary. Let the store's typed errors (connection_run_active,default_account_delete_unsupported,connector_instance_not_found) flow throughhandleError. - 1.4 Wire both mounts in
reference-implementation/server/index.jsalongside the existingmountRefConnection*calls, injecting the SAMEupdateConnectorInstanceStatus/deleteConnection(withpurgephases) / audit deps the bearer routes already receive (lines ~3981/4022). - 1.5 Reference route tests: revoke flips one instance + emits audit + rejects non-owner-session; delete delegates to shared
deleteConnection+ emits audit + rejects non-owner-session; typed refusals surface unchanged.
2. Console client wrappers
- 2.1 Add
revokeConnection(connectionId)anddeleteConnection(connectionId)toapps/console/src/app/dashboard/lib/operator-runs.ts, posting/deleting viaconnectionControlPath(connectionId, "/revoke")andconnectionControlPath(connectionId, "")withfetchAs(owner-session cookie), classifying typed errors. - 2.2 Client-wrapper result tests for the typed-outcome classification (mirrors
cancel-run-result.test.ts).
3. Console server actions
- 3.1 Add
revokeConnectionAction(formData)anddeleteConnectionAction(formData)toapps/console/src/app/dashboard/records/[connector]/actions.ts: re-verify owner session (requireDashboardAccess), enforceconfirm_revoke=yes/confirm_delete === connection_idserver-side, call the client wrapper, redirect back withmessage/errorquery,revalidatePath. - 3.2 Server-action tests: confirmation enforced server-side; typed errors mapped to the in-place banner param; success revalidates.
4. Console danger-zone UI
- 4.1 Add a
ConnectionDangerZoneclient component underrecords/[connector]/with a Revoke form (confirm checkbox, destructive submit, retain-records copy) and a Delete form (type-the-connection-id confirm, destructive submit, erase + active-run/default-account copy). - 4.2 Render it on
records/[connector]/page.tsx(always — the page only renders for a resolved connection), passingconnectionId/connectorInstanceIdand the typed-outcome banner fromsearchParams. - 4.3 Component/page tests: real connection shows both affordances; revoke copy retains records + stops future collection; delete copy erases this connection + may refuse active/default-account; delete submit gated on matching typed id; the danger zone is absent from catalog-only contexts (covered by the route guard — assert the list view / no-connection path renders no destructive control).
5. Validation
- 5.1
openspec validate add-console-connection-revoke-delete-controls --strict - 5.2
openspec validate --all --strict - 5.3 Focused console tests green; reference route tests green.
- 5.4
pnpm --filter pdpp-console types:check - 5.5
git diff --check
Acceptance checks
- A resolved connection's detail page renders Revoke + Delete; a catalog-only / no-connection context renders neither.
- Revoke confirmation copy says records/grants retained + future collection stops; delete confirmation copy says this connection's records are erased and active/default-account may be refused; delete requires reproducing the connection id (server-enforced).
- The server actions call
POST /_ref/connections/:id/revokeandDELETE /_ref/connections/:id; those routes delegate to the same store primitives + audit as the bearer routes (no duplicate cascade). - All validations above pass.