fix: stabilize canvas runtime and permission review (#3600)
This commit is contained in:
@@ -6,7 +6,7 @@ system: canvases
|
||||
owners:
|
||||
- canvases
|
||||
created: 2026-08-26
|
||||
last_updated: 2026-09-08
|
||||
last_updated: 2026-09-10
|
||||
requirements:
|
||||
- REQ-CANVASES-AGENT-WEB-APPS-001
|
||||
- REQ-CANVASES-AGENT-WEB-APPS-002
|
||||
@@ -78,10 +78,7 @@ standard web protocol that the Plugins system owns.
|
||||
|
||||
## Implementation baseline and feature gate
|
||||
|
||||
Implementation starts from current `main`. PR #3061 contains the superseded
|
||||
declarative canvas implementation and must not merge as a prerequisite. The
|
||||
new implementation creates the canvas model and host surfaces directly from
|
||||
this design.
|
||||
PR #3061's declarative implementation is superseded, not a prerequisite.
|
||||
|
||||
The runtime flag identity is `features.canvases`. Its environment variable is
|
||||
`KANDEV_FEATURES_CANVASES`. The first implementation sets `prod`, `dev`, and
|
||||
@@ -188,7 +185,8 @@ The normal flow starts in a task conversation:
|
||||
8. The agent calls `publish_canvas_kandev` with the canvas ID and source path.
|
||||
9. Kandev reads that directory through the trusted task execution file API.
|
||||
10. The Plugins service validates and stores an immutable release.
|
||||
11. The canvas activates immediately when its permissions fit existing grants.
|
||||
11. The canvas activates when its permissions fit existing grants or its
|
||||
recorded [initial creation authority](local-creation-authority.md).
|
||||
12. The task client receives a lifecycle event and opens the new canvas.
|
||||
|
||||
The source directory is workspace-relative and scoped to the current execution.
|
||||
@@ -253,23 +251,12 @@ The optional section is absent when the resolved profile lacks canvas tools.
|
||||
This covers canvas requests inside ordinary task conversations, independently
|
||||
of the guided task preset.
|
||||
|
||||
`CanvasTaskCreateLauncher` retains the localized
|
||||
`CanvasTaskCreateLauncher` retains the visible, editable
|
||||
`canvases:createCanvasTaskPrompt` preset in the normal `TaskCreateDialog`.
|
||||
The preset is visible and editable. Proposed English wording:
|
||||
|
||||
> Create an interactive canvas inside Kandev for the application I describe.
|
||||
> If the application goal is missing, ask what the canvas must show or do.
|
||||
> Find the Kandev canvas MCP tools before writing application files.
|
||||
> If they are not callable, use native tool search for `kandev canvas` or inspect the available MCP catalog.
|
||||
> Call `create_canvas_kandev` to create the draft and obtain its source directory.
|
||||
> Read `read_canvas_authoring_skill_kandev` once without a path.
|
||||
> Build inside the returned directory and use authorized live Kandev data for domain views.
|
||||
> Call `publish_canvas_kandev` and address any validation errors.
|
||||
> Report the canvas identity and whether its release is active, awaits permission review, or was unsuccessful.
|
||||
> If publication is unsuccessful, report the failure and do not claim that the canvas is published.
|
||||
> If workspace access requires promotion, explain the user action that is still required.
|
||||
> A local build alone does not publish a canvas inside Kandev.
|
||||
> If the tools remain unavailable, report the limitation instead of claiming that workspace files are a Kandev canvas.
|
||||
The locale catalog owns the exact wording. It names discovery, creation,
|
||||
assigned-root authoring, live data, the single core skill read, publication,
|
||||
and honest outcome reporting. It explains permission review and promotion
|
||||
when required, and does not equate local files or a build with publication.
|
||||
|
||||
The existing selected question capability governs any clarification. The
|
||||
preset cannot grant a question tool to an autopilot session that lacks one.
|
||||
@@ -376,6 +363,10 @@ If the declaration needs more permission, the release gets
|
||||
`pending_permission` status. The service publishes
|
||||
`canvas.release.permission_required`. The active release remains unchanged.
|
||||
|
||||
Recorded owner-authorized first publication uses
|
||||
[the creation authority transaction](local-creation-authority.md) instead.
|
||||
The generic publish and manual review paths do not infer that authority.
|
||||
|
||||
Rollback selects the retained prior valid release. It does not restore old
|
||||
grants that the user revoked. If the current grants do not cover the old
|
||||
release, rollback needs a permission review.
|
||||
@@ -409,6 +400,41 @@ A release that adds permissions uses the same review component. Removing a
|
||||
declaration does not need approval. The effective grant contracts to the new
|
||||
declaration after activation.
|
||||
|
||||
### Readable review surfaces
|
||||
|
||||
`CanvasReleaseDialog` and `CanvasPromotionDialog` share a permission view model
|
||||
and human-readable provenance. Show release creation time, author kind, source
|
||||
task title, and source session name when currently authorized. Use an
|
||||
unavailable-source label for deleted or inaccessible records. Raw UUIDs stay in
|
||||
API identities and action payloads, never headings, accessible labels, or copy.
|
||||
Add optional source labels to the HTTP release/promotion projections through
|
||||
authorized task/session lookups; avoid client-side lookup waterfalls.
|
||||
|
||||
Select the pending release by default, otherwise the active release. A compact
|
||||
release selector preserves access to retained history. Distinguish Active from
|
||||
Previous for valid releases by comparing with `active_release_id`. Render one
|
||||
grouped declaration with newly requested rows marked. Use localized semantic
|
||||
labels for reads, writes, state, and supported event/resource names; preserve
|
||||
exact normalized network origins. Unknown values have a localized unsupported
|
||||
permission label and cannot be approved silently. Keep rejection reasons and
|
||||
real validation errors separate from normal permission review.
|
||||
|
||||
On desktop use a viewport-bounded wider dialog (target 48rem maximum width)
|
||||
with a fixed title and action footer. The single middle region can scroll;
|
||||
remove the 288px cap and repeated declared/missing-permission blocks. Pending
|
||||
release actions are Approve and Reject, retained release action is Roll back,
|
||||
and current release has Close. Reuse unchanged stale-review safeguards.
|
||||
|
||||
On phones use the full-height focused surface pattern from `task-layout.tsx`,
|
||||
with header/footer and internal scrolling mechanics from
|
||||
`mobile-menu-sheet.tsx`. This deep release review warrants a full-height
|
||||
surface; the short host action chooser remains an inset bottom drawer.
|
||||
Use `useResponsiveBreakpoint`, dynamic viewport height, safe areas, and
|
||||
touch-only targets of at least 44px. Desktop controls remain 28px. Share
|
||||
selection, actions, loading, and errors between presentations. Dismissal returns
|
||||
focus to the opener; a release change during review refreshes the selection and
|
||||
disables stale actions. No horizontal document overflow is permitted.
|
||||
|
||||
## Quick Chat editing
|
||||
|
||||
The workspace canvas host has an Edit canvas action. It calls a canvas edit
|
||||
@@ -609,6 +635,14 @@ The iframe can remain visible during an event-stream reconnect. The host shows
|
||||
the offline state outside the application. If the runtime token expires, the
|
||||
host requests a new URL and reloads the same active release.
|
||||
|
||||
Receiving a descriptor keeps `loading_runtime` and mounts the frame behind its
|
||||
loading cover. `CanvasHostSurface` must mount during this state, not only Ready.
|
||||
Only the current [plugin startup acknowledgement](../../plugins/system-design/isolated-web-app-contributions.md#runtime-startup-protocol)
|
||||
sets Ready. A failed or absent acknowledgement within 15 seconds tears down the
|
||||
frame and exposes Retry and Releases. Do not claim a specific CSP or network
|
||||
cause from the generic timeout. Retry, renewal, navigation, and authority
|
||||
changes fence old callbacks. Appearance remains applied before reveal.
|
||||
|
||||
If the release is unavailable, the host offers Edit, Releases, Roll back, or
|
||||
Remove according to current permissions. It never loads a rejected artifact.
|
||||
|
||||
@@ -628,23 +662,14 @@ repositories, sessions, or external services by itself.
|
||||
The plugin runtime repeats resource authorization for each request. Canvas
|
||||
authorization does not replace task or workspace authorization.
|
||||
|
||||
## Baseline from the superseded declarative work
|
||||
|
||||
The declarative implementation is not on `main` and is not a released
|
||||
contract. PR #3061 must close or remain unmerged. Implementation does not merge
|
||||
that block model before it creates the plugin-backed model.
|
||||
|
||||
Development databases that ran PR #3061 can keep unused declarative tables.
|
||||
The new implementation does not read them. It does not add a destructive
|
||||
startup migration for those development-only tables.
|
||||
|
||||
## Failure handling
|
||||
|
||||
- A missing task agent disables the create prompt and shows how to start one.
|
||||
- A source read error preserves the active release.
|
||||
- A package validation error returns safe file and rule diagnostics to the
|
||||
authoring agent.
|
||||
- A permission increase creates a pending release.
|
||||
- A permission increase after initial owner-authorized publication creates a
|
||||
pending release.
|
||||
- A promotion transaction either changes scope and grants together or changes
|
||||
neither.
|
||||
- A Quick Chat launch error leaves releases unchanged.
|
||||
|
||||
Reference in New Issue
Block a user