← Files PixVerseARCHIVED FILE
skills-internal/pixverse-agent-canvas/SKILL.md
59.2 KB · Oct 4, 2026 · 12:28 UTC
---
name: pixverse-agent-canvas
description: PixVerse Canvas shared-graph operating skill for cloud preview, on-demand downloads and credits, early Codex in-app browser visibility, native media composition, guarded graph mutations, and paid node generation. Use for a selected Canvas project or node; do not use for ordinary non-Canvas queues.
---
# PixVerse Canvas
Use this as the canonical playbook for Canvas work. The cloud Canvas graph is the source of truth;
the CLI reads and mutates it, while the browser is only the user's visible workbench. Never infer graph
state from the browser DOM or use browser clicks as a replacement for CLI readback.
Run commands through the installed wrapper as `"${PVX}"`. Read
`../../doc/canvas-agent-web-sync.md` only when implementation details, state files, command review, or
failure debugging require more depth.
Read `../../skills-shared/web-handoff.md` before the first browser action. Its exact Codex in-app
Browser selection is shared by OAuth, subscription, workspace management, and Canvas.
## CLI 1.4.5 Project Arguments And Arrangement
Project-scoped wrapper commands accept a positional project ID or `--project-id`; both
resolve through the same local binding, region, checkpoint and paid-plan checks. Conflicting
IDs are rejected before remote work.
For a requested automatic layout, review the current checkpoint and run
`"${PVX}" pixverse canvas arrange <project-id> --allow-non-atomic-canvas-mutation --json`.
The flag selects the existing guarded non-atomic path; the user's arrangement request
already authorizes this layout operation, so do not add a separate approval question.
The wrapper reads before and after one mutation. If the receipt has no verifiable edit
version, follow with `canvas sync` to review the resulting graph; do not rerun arrangement.
See `../../skills-shared/pixverse-cli-1.4.5.md` for the reviewed CLI delta.
## Activation Boundary
This playbook, including the Canvas-native-first media policy, applies only after the current work
target has been resolved as a Canvas project. That is true when the user explicitly asks for Canvas or
provides a Canvas project target, or when the user is continuing a previously selected Canvas project
whose valid binding is the intended active target.
Do not activate this route merely because the plugin is installed, a repository contains an old
`.canvas-project.json`, or the media was originally generated by PixVerse. Presence of a binding file
alone must not override an explicit or otherwise clear non-Canvas request. For an ordinary queue,
local project, standalone asset, or local delivery workflow:
- do not run `capabilities canvas`, node-schema discovery, sync, or browser handoff
- do not create or bind a Canvas project just to see whether Canvas can perform the edit
- keep the existing non-Canvas queue, download, FFmpeg, subtitle, QA, and packaging behavior unchanged
## Cloud Preview And On-Demand Delivery
For the selected Canvas target, default to cloud preview in the Codex in-app Canvas viewer. Do not
automatically download final or intermediate media, repeat a local inline preview, run local technical
QA, query post-generation balances, or assemble an invoice. Generation success and ready downstream
nodes depend on cloud state, not on local copies or credit observation.
Downloading, local technical QA, and credit reporting are independent user intents. Download only for
an explicit file/export request or an authorized local-processing requirement; query credits only when
requested. A request for an MP4 does not authorize billing work. An unavailable IAB or preview is not
permission to download: return the Canvas link and disclose the unchecked preview.
Cloud preview still requires honest review. Inspect the visible result and already available output
metadata. Do not treat requested model parameters as measured output dimensions, duration, or audio.
Mark unavailable checks `not_checked`; never call a cloud visual review complete technical QA. Media
not downloaded and credits not queried are `not_requested`, not pending, failed, or zero consumption.
Use `project resume <slug> --surface canvas` and `project handoff <slug> --surface canvas` for this
selected route. The default local route remains unchanged even if a binding is present. Never call
queue-only `qa project` or `project ledger` to recover Canvas runs. For explicitly requested local QA,
download only the required nodes and inspect those files; do not expand into all project assets.
### Preserve The User's Viewing State
Automatic generation checks, QA, and delivery follow-up use the already visible inline preview at its
current size. Do not enlarge it: no expanded image/video dialog or lightbox, theater mode, fullscreen,
player/panel/window maximization, or Canvas/page zoom for inspection. Do not double-click media or
activate enlarge/maximize controls as a preview shortcut, including after a failed control or screenshot.
Only an explicit user request to enlarge, maximize, or enter fullscreen authorizes that viewing change.
"Check the result", "preview it", and approval to generate do not imply permission to enlarge. If the
user has already enlarged a preview, preserve it; do not press Escape, close it, shrink it, or restore
an earlier view merely to prepare the next automatic check.
Initial Canvas handoff may show the ordinary browser panel as required below, but does not authorize
media enlargement. Later automatic checks must not reclaim focus, repeatedly reopen a hidden panel,
or reset the user's viewing state. If the current preview cannot be inspected without those changes,
stop preview interaction and report the Canvas link, available metadata, and `not_checked` items.
Do not escalate to enlarged preview, repeated control attempts, or automatic downloads to complete QA.
## First Visible Milestone
Opening or reusing the Canvas in the Codex in-app Browser is the first visible milestone after entering
this route, not a final handoff step. Tell the user that the Canvas is being opened, then make it visible
before graph synchronization, capability discovery, prompt planning, paid preflight, or any other long
operation whenever a valid project binding already exists.
`canvas handoff` only returns a declarative browser contract and editor URL. It does not itself open,
select, display, or retain a Codex Browser tab. Creating or reopening a Codex task also does not execute
that contract automatically; once this Canvas route is selected, the Agent must perform the shared
Web handoff in the current turn.
- With a valid binding, run `"${PVX}" canvas handoff` immediately and acquire the exact `iab` browser.
- Without a binding, perform only the minimum action needed to establish the intended project, then
hand off immediately. An explicit new-project request uses `canvas project create`; an ordinary new
Canvas request may let its first intended project-scoped command create the binding automatically.
- If the user asked to continue a previous project and its binding is missing, recover the intended
project instead of silently creating a replacement.
- Do not delay browser visibility behind a separate `setup status`, full graph inspection, node-schema
scan, billing refresh, or generation preflight. Browser login and CLI authentication remain separate.
- If `iab` is unavailable, return the direct `editor_url` immediately, state that browser automation
stopped, and continue only with safe CLI work that does not depend on browser interaction.
Opening the Canvas does not authorize a graph mutation or paid generation. Keep the checkpoint,
edit-version, and explicit paid-confirmation gates below.
## Operating Loop
1. Resolve the intended project with the minimum binding work. Omit `--project-id` on either managed
CLI channel when the current project or repository binding is the intended target. Create
explicitly only when the user asks for a new Canvas project.
2. Open or reuse the exact project in the Codex in-app Browser and give the user that visible feedback.
3. Inspect only the capabilities and node types the requested operation actually needs. Reuse
capability results already read in this task; do not add `--refresh` unless validation says the
cached result is stale or the user explicitly asks for live refresh. A matching current `nodes[]`
record returned by `capabilities canvas` is the target node contract; `node_types[]` is accepted only
as a legacy envelope by the Wrapper. This is live/authenticated Canvas
discovery, unlike offline `capabilities create`. Do not call `node schema` for that
same type. Query one schema only when the target type is absent from capabilities, the returned
record is structurally unreadable, or a specific required field/constraint remains undefined after
consulting its reviewed reference (including a concrete validation error identifying that gap).
For executable generation nodes, use the runtime record's
`capability_adapter.contract_revision=canvas_cli_capability_adapter.v2`, selector routes, field
mappings, conditions, and resolved Create capability as the canonical Canvas-to-Create contract.
The preview envelope may omit top-level `capability_schema_revision`; the Wrapper accepts only that
known envelope gap when the requested node contracts themselves are compatible. Any other reported
incompatibility remains fail-closed. Never infer another mode or field from ordinary `create`
capabilities alone.
4. Before a graph patch uses a user-supplied local file or external URL, materialize that input through
the User-Supplied Media Uploads contract below and retain the returned provider-backed `file_path`.
Never place the original input path or URL in a Canvas node or generation reference field.
5. Immediately before composing a patch or edit-version mutation, accept the latest shared graph
checkpoint with `"${PVX}" canvas sync --format markdown`. Review its semantic changes and use the
accepted `.canvas-sync-state.json` as the local graph source; do not add another full `graph get` when
the wrapper will already pre-read and post-read the mutation.
6. For editing, composition, compression, audio, or other media transformations, apply the
Canvas-native-first policy below before downloading any graph asset or choosing local post tools.
7. Before creating or updating text nodes, apply the materialization policy below. Keep goals,
operational planning, and process notes out of the graph, while preserving script-like creative
planning as editable source nodes.
8. Use an atomic mutation where available. Submit it once, then trust the wrapper's post-read guard.
9. For a paid mutation, run the separate read-only `canvas paid preflight` and show its sheet.
Follow its effective `require` / `skip` policy below, then execute the exact returned bound-plan command.
10. After generation succeeds, inspect the cloud result without enlarging or changing the user's view
under Preserve The User's Viewing State, and continue ready dependencies immediately.
Sync before the next mutation; download or query credits only when separately requested.
## Minimum First Confirmation
For a new Canvas production whose first paid batch is exploratory or establishes continuity anchors,
optimize for the earliest useful confirmation. Before that confirmation, create only what is needed to
make the first paid decision safe:
- one compact production plan containing the usable script or beat list, shot count, continuity risks,
and stage map
- the ready-to-run prompts for the first paid nodes
- one Canvas patch built from the just-accepted checkpoint
- one paid preflight
If the compact production plan contains a usable story outline, beat script, scene breakdown,
shot-by-shot script, dialogue, or narration that governs the overall creation, materialize that
script-class portion as a Canvas text node in the same initial patch. Do not delay it until individual
generation nodes are submitted.
Keep document disclosure equally small. The critical path before this confirmation consists of the
selected public entrypoint (`pixverse-canvas`, or Studio if already active), this Canvas playbook, and
`web-handoff.md`. Direct Canvas entry does not require Studio; do not load both public entrypoints or
repeat browser handoff merely to switch entrypoints. Do not load the generic production
router, gateway, project-memory playbook, creative-orchestration guide, model references, or detailed
Canvas implementation document merely because the eventual production is complex. Load one only when
the first batch has a concrete unresolved need that this playbook cannot answer; otherwise defer it to
the later stage that consumes it.
Do not create separate brief, character bible, scene bible, sound sheet, route board, and detailed shot
table before an anchor-image confirmation when those files only repeat the same facts. Add the smallest
continuity or sound artifact before the later paid stage that actually needs it. A multi-scene film may
still require full bibles before control frames or motion; that requirement does not force those files
onto the critical path before the first anchor batch.
Batch deterministic local preparation into one host-tool call. The Canvas paid preflight already reads
authentication, account, membership, workspace, and balance, so do not run separate `auth status` or
`account info` calls on the healthy path. Keep the wrapper's pre-mutation and post-mutation graph reads:
they are concurrency checks, not duplicate discovery calls.
When a target binding already exists, prefer one `canvas prepare --node-type <type> ...` call for the
first capability contracts and accepted graph checkpoint. It returns the browser handoff, selected
contracts, sync report, and component timings together. Do not follow it with a full graph read or
schema calls for node types already returned. This aggregation does not cross a paid-confirmation gate
and does not replace the mutation wrapper's concurrency reads.
## Stage And Handoff Status
Every user-facing Canvas update must distinguish a usable intermediate from the requested final
deliverable. State the current stage position, what just completed, what remains, and the next paid
batch. For example: `Stage 2/5 — continuity anchors ready; the final video is not finished. Remaining:
shot boards, motion clips, Canvas finish, and QA.`
After each meaningful stage, use `project handoff` and report separately:
- the local project directory and local-only planning files
- the Canvas editor URL, accepted edit version, and cloud node status
- requested downloads, if any, which are local copies rather than new Canvas sources
- the next paid task count and approval gate
Record those stage facts in the handoff itself so later turns do not have to reconstruct them from
prose. For an incomplete stage, pass each remaining stage separately and describe the next paid batch:
```bash
"${PVX}" project handoff <slug> --surface canvas --stage <name> --stage-position <N/M> \
--final-deliverable-status incomplete \
--remaining-stage <next-stage> \
--next-paid-task-count <count> --next-paid-task <description> \
--approval-gate not_required --format markdown
```
Use `not_required` for the default automatic policy, or `required` when confirmation
is enabled. This handoff is progress metadata; paid preflight still owns execution policy.
For the final handoff, use `--final-deliverable-status complete`, no remaining stages,
`--next-paid-task-count 0`, and `--approval-gate complete`. The command persists this structured stage
record in the project manifest and reports only `task.localized` files from Canvas follow as localized
preview copies; unrelated local masters and user assets remain ordinary media.
Cloud delivery needs no local file. If the user requested a local deliverable, record
`--delivery-mode local --deliverable-path <absolute-file>` (repeat for multiple files); complete
handoff is rejected while those files are absent. Check the requested format separately before
claiming it was delivered. This does not trigger credit reporting.
Do not describe an anchor batch, shot board, or partial clip set as completion of a video request.
### Combine Intermediate Acceptance With The Next Paid Approval
When a completed anchor, control frame, or other intermediate is the known source for the next paid
batch, do not first end with a generic `reply continue if this looks good` and only prepare the paid
confirmation sheet after that reply. Complete all deterministic non-paid graph preparation, sync the
result, and run the read-only paid preflight before asking the user to continue. Present together:
- the named intermediate and its visible/not-checked review status
- the selected Canvas source node IDs and accepted edit version
- the complete confirmation sheet for the exact next paid batch
One clear affirmative response to that displayed sheet both accepts use of the named intermediate and
authorizes the exact bound next batch. Do not request another confirmation keyword after it. If the
user rejects the intermediate, leave the unused plan unsubmitted and revise it. Split intermediate
review from paid approval only when the user explicitly requests review-only/no next-stage preparation,
or when a material next-stage parameter still requires a user choice. Separate genuinely different
paid batches still require their own plans; this rule removes two prompts for one next batch, not the
safety boundary between batches. An earlier request made before any sheet exists remains incapable of
approving a not-yet-bound plan.
Read `../../skills-shared/quality-policy.md` for all image/video generation and account choices.
Apply `../../skills-internal/pixverse-seedance-prompt-enhance/SKILL.md` before writing Seedance prompts.
New image nodes use Sunburst 2K/high; new video nodes use Seedance 2.5 1080p through the
live adapter mappings. Existing explicit user choices and paid nodes remain intact.
## Video Model And Mode Preference
For Canvas `video_generate`, resolve model and generation mode independently in this order:
1. an explicit choice in the current user request
2. an applicable user or project preference already recorded for this Canvas work
3. the Canvas fallback for the still-unspecified field
The fallback model is `seedance-2.5` with 1080p quality. The fallback mode is the reference mode with canonical
`gen_type=reference_to_video`. If the user specified only one field, preserve it and apply the fallback
only to the other field. Never replace an explicit or recorded preference merely because the fallback
is available.
Reference mode requires at least one concrete provider-backed image or video path. Reuse suitable
references already in the graph or supplied by the user. If none exists, expose the missing reference
prerequisite and create or obtain the required reference through the normal staged and paid-confirmation
flow; do not submit an empty reference mode and do not silently switch to `text_to_video`. A clear
prompt-only or other mode choice from the user is a preference and takes precedence.
Current capabilities remain authoritative. Free/Basic and any model-entitlement rejection
pause for upgrade or explicit fallback consent; show the clickable subscription link.
Only after the choice prepare v6 540p / Nano Banana 2 Lite 1080p nodes, then run
`canvas paid preflight ... --accept-basic-fallback`. The returned plan binds that choice;
use its exact command and do not add flags to an earlier plan. Never change global preferences.
## Multi-Shot Reference Strategy
For a narrative or production sequence with multiple distinct shots, separate the shared continuity
anchor from shot-specific composition control. A character, product, location, or world reference may
be reused across shots to preserve identity, materials, palette, and art direction; it is not therefore
the default sole control image for every video node.
Before building the motion batch, determine whether the shots materially differ in action, pose,
camera angle, framing, location, lighting, time, scale, or story state. When they do, create or derive a
shot-specific storyboard/control frame for each shot through the normal staged and paid-confirmation
flow. Each `video_generate` node should use its matching shot control frame as the primary visual
reference. When the current model and Canvas contract support multiple references, the shared anchor
may also be included as an auxiliary continuity reference; when only one reference can be used, prefer
the shot control frame derived from that shared anchor.
Do not connect one shared anchor as the only visual reference to every distinct shot merely because it
is convenient or already provider-backed. That pattern over-constrains composition and makes prompts
fight the same starting pose, camera, and scene state. It remains valid for a quick consistency test,
variants of substantially the same shot, an intentionally repeated composition, or an explicit user
choice. Do not create redundant per-shot frames when those conditions genuinely apply.
Before paid preflight, disclose a multi-shot batch that still uses one identical sole reference as a
creative-control risk and distinguish it from a schema or submission error. This warning is
non-blocking: preserve the user's explicit choice, and never reject or silently rewrite an otherwise
valid graph solely because references are shared.
## User-Supplied Media Uploads
A user-supplied local file path or external URL is an upload input, never a Canvas provider path.
Before creating a Canvas source/reference node or placing that asset in a generation payload:
1. Keep the binding's `PIXVERSE_REGION` and the current active workspace for the whole upload and
Canvas workflow. Do not add a one-off `--region` or `--workspace-id` override.
2. Upload the input exactly once with:
```bash
"${PVX}" pixverse asset upload <input> --json
```
3. Read the structured result and require a concrete provider-backed `file_path`. Use only that
returned provider path when creating the source/reference node and when materializing downstream
`customer_img_path`, `customer_img_paths`, `customer_video_path`, `customer_video_paths`,
`customer_audio_path`, or `customer_audio_paths` fields required by the live Canvas contract.
4. Never persist the original local path, a `file://` URI, public or download URL, thumbnail URL, or
another local delivery/preview path in Canvas `file_path`, generation reference fields, or legacy
media aliases. Do not create or patch the Canvas node until upload has returned the provider path.
5. If the upload response lacks an unambiguous provider-backed `file_path`, stop before graph mutation,
report the unresolved upload result, and resolve its outcome before another upload attempt. Do not
guess a path from an asset ID or URL and do not blindly retry an uncertain upload.
6. If upload succeeds but `patch apply` conflicts, fails, or must be rebuilt from a newer checkpoint,
retain and reuse the returned provider path. Do not upload the same input again merely to retry the
graph mutation.
7. Reuse provider paths already present in the Canvas graph. Never re-upload an existing Canvas asset
or a local preview, QA copy, or delivery download of that asset.
The required order for a new user asset is: upload input -> retain provider `file_path` -> accept the
latest Canvas checkpoint -> build and apply the graph patch -> run paid preflight when generation is
requested.
## Canvas-Native Media Operations
Inside a Canvas-bound project, prefer an executable Canvas node over downloading media for local
processing. The current reviewed runtime exposes executable `video_compose` with a required `tracks`
payload. It also lists `video_compress` as a reserved compatibility type with both execution and graph
authoring unsupported; do not construct or dispatch it. Treat the remaining node list as runtime
capability data, not a permanent exhaustive list. Before an
editing route whose support can change, inspect the current capabilities contract and graph instead of
assuming that an old Canvas or FFmpeg recipe still applies. A capability record for the target node is
authoritative for this route; do not repeat it through `node schema`. Query schema only under the
explicit fallback conditions in the Operating Loop.
When Canvas can express the requested result:
- create or update the native editing node through the guarded graph mutation path, and connect the
existing sources only as that node's contract requires; for `video_compose`, put sources in
`payload.tracks[].segments[].material.node_id` and also declare every one in the enclosing node's
top-level `depends_on` array
- reuse existing graph edges and provider-backed references such as the source node's `file_path` as
allowed by the current schema; do not copy an existing Canvas asset into a new upload/reference node
- keep composition, sequencing, track mixing, compression, and any other capability-declared
transformation in the shared graph so the Web editor and Agent continue from the same assets
- apply the normal checkpoint, edit-version, verification, and paid-confirmation rules; native-first
changes the editing surface, not authorization or retry safety
For `video_generate`, follow the runtime adapter-v2 field mappings. The guarded Canvas Wrapper keeps
only narrow legacy-input normalization so old patches fail clearly or become canonical before the
single guarded submission:
- on guarded `patch apply`, canonicalize `gen_type=reference` to `reference_to_video` and
`resolution` to `quality`; conflicting aliases fail closed
- keep the verified Canvas Graph representation `audio=1/0`; canonicalize boolean patch input
`true/false` to `1/0`, and paid preflight accepts only numeric switches in the persisted graph. The
ordinary Create capability's boolean CLI flag is the adapter target type, not proof of the Canvas
source-payload type; do not reverse this rule until Canvas publishes a source schema or explicit transform
- materialize available provider-backed image/video/audio dependencies into
`customer_img_paths` / `customer_video_paths` / `customer_audio_paths` for
`reference_to_video`, and a single unambiguous image dependency into `customer_img_path` for
`image_to_video`
- one Canvas generation node produces one output because adapter v2 does not map
`create_count`, `count`, or `n`; do not promise or request multiple results on one node
- never substitute a public URL for a provider path, never guess among multiple image dependencies,
and never invent fields for an unknown generation mode
- this submission-only normalization does not rewrite the caller's patch file; the submitted graph is
the state that subsequent checkpoint and approval checks bind
Apply the same live-contract discipline to the other executable generation nodes:
- `image_generate` defaults to `text_to_image` only when `gen_type` is absent. Use
`image_to_image` when references are intended, materialize provider-backed dependencies into
`customer_img_path` or `customer_img_paths`, and validate model-specific quality, aspect ratio,
detail level, and reference-count limits against the offline Create `image` capability.
- `audio_generate` must declare `text_to_music` or `text_to_speech`. Validate only fields mapped by
that selector: music needs a valid lyrics/instrumental/auto-lyrics choice when the model requires
it; speech needs `voice_id` or `provider_voice_id` and model-supported voice controls. The current
music adapter does not map `duration_seconds`, so do not set `duration_auto=false` and assume a
manual duration will reach Create.
- `text_generate` currently publishes a complete payload schema without a Create adapter. Require its
prompt and model from that node schema; do not reject the contract merely because it has no
`capability_adapter` object.
- All executable Canvas generation adapters omit Create count mapping. Keep one output per node and
create separate nodes for multiple outputs.
For `video_compose` only, read `../../skills-shared/canvas-video-compose.md` before constructing its
payload. It defines the reviewed track/segment fields, millisecond time ranges versus second-based
media duration, source dependency declarations, related-node layout, render lifecycle, and a complete example. Reuse successful
source nodes by `material.node_id`; do not copy their URLs or file paths into a new reference node. Do
not infer nested fields or defaults merely from a capability record saying `tracks` is required.
Every newly created Canvas node should have an intentional local position. For a multi-step production
graph, prefer readable **production-stage columns**: derive the stage from graph edges, `depends_on`,
and capability-declared media references; keep nodes from the same stage in one vertical lane and keep
their shot/patch order from top to bottom. Reuse the existing lane for that stage when one is already
present. Use references mainly to align the lane vertically; do not create another horizontal offset
for every edge in a dependency chain. A typical controlled-video graph reads left to right as script or
creative input → reference/anchor media → storyboard/control frames → generated videos → composition or
delivery nodes.
For `video_compose`, use the bounding box of all
`payload.tracks[].segments[].material.node_id` sources and place the compose lane immediately beside
that source-video stage, centered on the group. Keep about 40–60 px of visual spacing and align generated
defaults to a 20 px grid. Do not use the whole graph's far-right edge as the default. Preserve every
explicit `position`, do not auto-move an existing node, and do not turn this missing-position default
into a full layout rewrite. The guarded Canvas Wrapper applies the same stage-column fallback to a
recognized new patch node whose position is omitted; only an unrecognized standalone node falls back to
nearby non-overlapping placement.
For ordinary composition covered by that contract, do not run a separate `patch dry-run` before
`patch apply`. Apply already performs CLI structural/context validation and checks the server's
`valid`/`applied` response. Use dry-run only for an explicit validate-without-saving request or a
specific unresolved validation question after reading the contract; do not discover fields by trial
and error. This removes a redundant Agent call, not apply validation, checkpoint/version guards,
post-read verification, or the separate `canvas paid preflight` before generation.
Do not download an existing Canvas asset, process it with FFmpeg or another local tool, and upload it
again merely to perform an operation supported by the current Canvas graph. A local file downloaded
for preview, QA, or final delivery does not become the preferred editing source and must not create a
duplicate Canvas asset.
Use local post-production only when at least one of these conditions is true:
- the resolved runtime contract cannot represent a required edit, output format, codec, burn-in, mix,
or delivery constraint
- the user explicitly requests an offline/local edit, a local master, or a tool-specific workflow
- a readback-supported validation result deterministically says the Canvas operation is unsupported;
a timeout, ambiguous mutation, transient network failure, or unfamiliar payload is not such evidence
State the missing Canvas capability before falling back. Keep local intermediates local. If the user
needs the finished fallback result back in Canvas, upload only the final required result once, connect
that single node to later graph work, and reuse it thereafter; do not upload per-step intermediates or
re-upload an asset that the graph already contains.
## Text Node Materialization
Treat Canvas as a production asset and dependency graph, not an Agent reasoning log or project-planning
board. Before a patch creates or updates a text node, classify the proposed content as either
`production_text` or `orchestration_text`. Only materialize `production_text` by default.
Materialize text nodes for editable creative source content that is ready to use in a concrete
production step:
- a ready-to-run image, video, or audio generation prompt authored or refined for the user's specific
requested output
- screenplay, scene, dialogue, narration, or other script content
- a story outline, beat sheet, scene breakdown, shot script, or similar script-like plan when it is the
creative source for the overall Canvas production, even if several downstream nodes will consume it
- lyrics, captions, or spoken copy when they are actual creative inputs to downstream generation
Do not materialize `orchestration_text`:
- user goals, audience, platform, constraints, brief summaries, or success criteria
- route options, model rationale, production plans, task checklists, dependencies, or next-step notes
- progress updates, assumptions, approval messages, billing/credits information, QA notes, or Agent
analysis
- scratch prompts, discarded variants, and intermediate drafts that are not intended production inputs
Keep those items in project memory and development artifacts described by
`../../skills-shared/project-memory.md`, and surface relevant status through normal user updates. A
route board, task checklist, or purely organizational storyboard table remains planning. A story
outline, beat list, scene plan, or shot list is `production_text` when it defines the narrative,
dialogue, narration, action, or shot content of the overall creation; materialize it even when it feeds
the production as a whole rather than one immediate generation node.
If one document mixes planning with production text, split it and create nodes only for the production
portion. Draft variants become text nodes only when the user explicitly wants to compare or edit those
variants in Canvas. A direct user request to place goals or planning on Canvas overrides the default,
but do not infer that request merely because the work is happening in Canvas.
Connect a production text node to the concrete generation/editing nodes that consume it, or place it
as the preserved source at the start of the relevant production lane when it governs multiple stages.
Do not create orphan text nodes merely to narrate what the Agent intends to do.
Once script-class creative content has been materialized, preserve it through image generation, video
generation, composition, follow-up, and QA. Do not automatically delete it after prompts are derived,
after dispatch, or during graph cleanup. Refine the existing node when it remains the same script, or
create a clearly named successor/version when history matters; keep the prior node unless the user
explicitly asks to remove it. Generated media does not replace its script source.
## Project Binding
Capability-compatible managed CLI project-scoped commands reuse these bindings:
- project scope: `projects/<slug>/.canvas-project.json`
- repository fallback: `projects/.canvas-project.json`
Canvas Wrapper activation is capability-based, not package-label-based. Before Canvas automation, the
Wrapper requires CLI `1.4.0+`, schema `1.2.0`, the reviewed runtime discovery declaration, and the exact
reviewed runnable Canvas command contracts from the active CLI's offline capabilities manifest. Apply
the same binding, checkpoint, mutation, paid-plan, and recovery behavior to online and local packages.
If the contract is missing or has drifted, fail closed with `canvas_wrapper_contract_unsupported`;
never fall back to an unguarded mutation merely because the package is online.
When no binding exists, the wrapper may create one empty project once, durably persist the returned
`project_id`, and inject it without polluting the requested command's stdout. This behavior is the same
for online and local packages when their active CLI satisfies the reviewed Canvas contract.
Use an explicit create only for an intentional new project:
```bash
"${PVX}" pixverse canvas project create --name "Agent Project" --json
```
Missing state is the only state that permits automatic creation. Unreadable, corrupt, unsupported, or
unwritable binding state fails closed. Creation intent is persisted before the remote request. If a
create may have succeeded but its final binding write failed, do not retry the create; repair the
binding with the known project ID. An unresolved replacement keeps the previous valid project usable.
Guarded Canvas calls reject `--workspace-id`. Select the intended active workspace first so graph,
account, balance, and reconciliation all refer to the same context. They also reject a one-off
`--region`: set `PIXVERSE_REGION=global` or `PIXVERSE_REGION=cn` for the whole workflow. Project
bindings persist that region, legacy bindings resolve to `global`, and a mismatch stops before graph
or paid work. The `--project-id` embedded in a bound paid confirmation command is approval data, not
permission to rebind that project under the current region; execution must leave the binding unchanged
and stop before local writes when its region differs. Help, capabilities, node-schema, and
authentication commands never create a project.
## Checkpoint And Concurrency
```bash
"${PVX}" canvas sync --format markdown
```
`canvas sync` is a finite read/compare/save checkpoint, not a watcher. It records accepted state in
`.canvas-sync-state.json`, reports semantic node and connection changes, and ignores layout-only noise
by default. Use `--include-layout` only when layout is part of the requested work.
Mutation is blocked when the checkpoint is missing, stale, or incompatible. If state is unreadable or
the comparison mode must change, inspect the cloud graph and then explicitly rebuild with
`canvas sync --reset-checkpoint`; never reset merely to get past a conflict. An OS-backed lock protects
the binding and checkpoint. On lock timeout, wait for the active operation and re-read; do not delete
the lock.
## Mutation Contract
All guarded Canvas mutations require `--json` or `-p`. Immediately before mutation, the wrapper reads
the graph and rejects unaccepted Web edits. It submits the mutation exactly once, reads once after a
successful receipt, and accepts only a verifiable resulting edit version.
- `patch apply` is atomic through `graph_patch.base_edit_version`.
- `graph reconcile`, `node rerun`, and `dispatch` are atomic through the current `--edit-version`.
- `dispatch rebind`, `node extract-audio`, and `node version apply` have no CLI edit-version
precondition and are blocked by default. Use `--allow-non-atomic-canvas-mutation` only after reviewing
the checkpoint and explicitly accepting best-effort pre/post verification.
For `patch apply` only, a post-read version newer than the mutation receipt may be accepted when the
normalized graph proves every requested patch semantic is present and no semantic change outside that
patch occurred. This covers a layout-only or volatile Web update racing the readback. It never applies
to dispatch, rerun, reconcile, a checkpoint that includes layout, an unreadable patch field, or any
extra semantic node, connection, or project change. Those cases remain verification-unknown and
require sync without resubmission.
On `canvas_external_changes_detected` or `canvas_edit_conflict`, run `canvas sync`, inspect the new
graph, and rebuild the operation from that state. Never repair a stale patch by replacing only its
version. On `mutation_applied_verification_unknown`, assume the mutation may already have applied: do
not resubmit it; sync and verify.
## Paid Generation And Credits
`canvas dispatch`, `canvas graph reconcile`, and `canvas node rerun` may create paid media. Do not use a
mutation command as their preflight. Prepare the intended operation with the dedicated read-only route:
```bash
"${PVX}" canvas paid preflight --operation dispatch --node-ids <a,b,c> --project <slug> --format markdown
"${PVX}" canvas paid preflight --operation graph-reconcile --node-ids <a,b,c> --project <slug> --format markdown
"${PVX}" canvas paid preflight --operation node-rerun --node-id <id> --project <slug> --format markdown
```
This command reads the accepted/current graph plus account, balance, active workspace, membership,
target nodes, and model routes. It never invokes the underlying mutation and its invocation must not
contain `--require-dispatch`. It writes an immutable one-time confirmation plan beside the Canvas
binding, then returns a confirmation sheet, `confirmation_required`, `confirmation_policy`, and the
exact `confirmation_command`. The confirmation sheet must visibly disclose each target's generation
mode, media-applicable duration, quality, aspect ratio, audio setting or image detail setting, output count,
image/video/audio reference counts and ordered reference fingerprints, resolved media source node IDs, and
prompt preview plus prompt fingerprint in addition to the planned node/task count, models, balance
snapshot time, and remaining stage. Do not label all dependency nodes as media sources, and do not
collapse approval to only task count, model, and balance.
The full-graph digest protects the approved content from hidden changes, but it does not replace this
user-visible parameter disclosure. Show the same sheet even when confirmation is skipped. Preflight
never starts generation itself.
Before account lookup or plan creation, paid preflight validates the reviewed `video_generate`
contract from the same merged `capabilities canvas` response. It resolves the selected
`nodes[].routes[]` entry, uses `nodes[].canvas` only as the Graph contract, and validates mapped Create
values against that route's `cli.capability`; it must not reopen offline `capabilities create` and use
its target types as the Canvas payload schema. Missing/noncanonical `gen_type`, `resolution` aliases, unmaterialized reference paths, a
missing `image_to_video.customer_img_path`, unsupported multi-output count, a nonnumeric Canvas audio switch, or an explicit
duration/quality/aspect/audio/reference
value outside the adapter-resolved Create contract returns `canvas_paid_target_contract_invalid` with
field-level issues and repair fields where deterministic. It validates values already present in the
graph and never invents Canvas fields or silently applies generic CLI defaults. `video_compose`
preflight also verifies that every material source is present in `depends_on`. Apply a
guarded repair patch, sync, then run a fresh preflight. Preflight is read-only and must not silently
repair the graph. Unknown generation modes are not guessed.
Treat saved composition configuration and rendered media as separate lifecycle states. A successful
`patch apply` is `configuration_saved`, never "composition completed" and never proof that rendering
started. Read each target's preflight `render_intent`:
- `first_render`: no prior task/history/output; the returned operation is the first paid render
- `resume_existing`: an active or dependency-blocked render already exists; the Wrapper refuses a new
plan, so follow/reconcile the existing run and do not dispatch. A durable submission ledger with no
terminal reconciliation enforces the same recovery path even before graph task metadata appears
- `retry_render`: a previous attempt failed; this is a new paid retry and needs a fresh plan
- `rerender_existing`: a result/history already exists; preflight only when the user explicitly asked
to regenerate or render a new version
Viewing, reusing, downloading, or continuing downstream from an existing output is not a rerender and
must not call dispatch. Native composition has no generation model; present its route as
`Canvas native compose (no generation model)`, not `unknown`. It remains subject to the same account,
balance, one-plan/one-submit, and confirmation policy as other paid Canvas work.
Use the existing quote-confirmation configuration; do not classify native composition or any other
Canvas operation as free or exempt it from these gates:
- Effective `require`: show the sheet, wait for one explicit approval, then run the returned command
with `--confirmed --confirmation-plan-id <id>`. A clear "continue composing" or "execute" response
to that sheet counts as approval; do not demand a literal confirmation keyword or another round.
An initial request before the sheet is not approval of the subsequently checked plan.
- Effective `skip`: report the sheet and proceed with its returned
`--run-if-allowed --confirmation-plan-id <id>` command without asking again. This follows the default automatic execution policy or an explicit user preference;
it does not claim that the user reviewed the individual plan.
- Default is `skip`, including the first batch. No prior generation receipt is needed.
Receipts remain submission/recovery evidence, never a confirmation prerequisite.
- If the user asks to control spending or confirm before generating, record `require`
on the bound named project before the next submission. “Allow future generation” restores
automatic execution. Follow `../../skills-shared/generation-confirmation.md`.
A newer project choice acknowledges the current global setting; a later global
choice supersedes older project choices. Re-preflight after a preference change.
A repository fallback binding has only global/default policy.
- Do not write preferences to apply the default. Change global settings only for an
explicit global request. Host tool permissions remain separate.
Never add, remove, or replace the returned execution flags manually. Execution rechecks the current
configuration. If stored skip is revoked or unreadable, stop and run a fresh preflight; do not replace
`--run-if-allowed` with `--confirmed`. The new plan determines whether a fresh approval is required.
Host execution-permission prompts are separate; stored skip does not bypass Codex's tool safety review.
The `confirmation_plan_id` binds the project, approved edit version, target nodes, model routes,
account, workspace, membership, balance, entitlement state, authorization basis/scope, complete normalized mutation arguments
(except the Wrapper-owned run ID), and a canonical full-graph content fingerprint. The fingerprint conservatively
covers the whole graph, including full prompts, parameters, upstream inputs, references, connections,
and ordered composition tracks. It excludes known presentation/metadata fields at their reviewed
paths and normalizes only reviewed Web-editor equivalences: a duplicated node title in
`data.extra.title`, empty materialized `customer_img_paths` / `customer_video_paths` /
`customer_audio_paths` on known
`image_generate` / `video_generate` nodes, and a text/script node's plain text rewritten as a simple ProseMirror paragraph document.
A different nested title,
non-empty reference array, unsupported rich-text shape, or changed visible text remains content. A
generation parameter named `style` or `position` is still content.
Never change a session, dispatch-plan, selection, version, or other mutation option after approval.
Execute the exact returned command. The Wrapper alone may advance its submission `--edit-version`
when the complete content and every other approval field still match, the checkpoint does not include
layout, and the version only moved forward. This retains the same approval and submits once with the
latest read version as the server's atomic precondition. It does not relax patch version checks,
post-submit verification, or permit retries on conflicts/timeouts. The ledger records both approved
and submitted versions. Do not request a second user confirmation merely for that verified rebase.
Changed content, account, membership, balance, options, version rollback, or missing content evidence
in an older plan returns `canvas_confirmation_plan_stale`; run and show a fresh preflight and follow its effective policy. Accepting a
new sync checkpoint cannot renew an old content approval. Plans are one-time: on
`canvas_confirmation_plan_already_used`, reconcile the recorded run instead of resubmitting.
The Wrapper checks consumption before graph/account drift, so a timed-out submission followed by a
node status change still routes to recovery, not to another paid confirmation.
An unconfirmed paid mutation still returns `canvas_paid_confirmation_required` for backward
compatibility, but it now persists and returns its own one-time bound plan. This Skill must never use that legacy two-call path. A direct `--confirmed` mutation without the returned plan is rejected with
`canvas_confirmation_plan_required`; there is no unbound-confirmation compatibility path. The same
plan requirement applies to `--run-if-allowed`; it cannot authorize a raw mutation without preflight.
Free/Basic accounts must choose upgrade or explicitly accept `gemini-3.1-flash-lite` 1080p / `v6` 540p before paid generation; show the subscription link and follow the shared quality policy. Unknown membership,
unreadable target routes, or unstable account/workspace identity fail closed. Fix the account or route,
then run a fresh preflight; do not retry the prompt as if entitlement were a content failure.
Confirmation plans are stored in `.canvas-paid-confirmation-plans.jsonl`. The wrapper owns the run ID
and durably records the consumed plan plus confirmed work in `.canvas-paid-runs.jsonl` before
submission. A timeout, partial response, or ambiguous result is not permission to resubmit.
The Wrapper accepts one exact JSON submission receipt from stdout or, for nonzero CLI exits, stderr.
When that receipt reports a failed/rejected dispatch, contains deterministic parameter-validation
errors for every selected node, and contains no dispatched node, new task ID, new history ID, or rerun
evidence, classify the batch as `not_started` with `rejection_class=deterministic_validation`. Do not
run a 300-second attribution wait for that case: report the concrete errors, repair the graph, sync, and
create a fresh paid preflight because the consumed plan no longer matches changed parameters. If even
one selected node lacks rejection evidence, any node may have started, the command timed out, or the
receipt is unreadable, keep the result `unknown`, preserve the ledger, and use bounded reconciliation.
Generation completion and credit observation are independent. A terminal `SUCCEEDED` node may unlock
ready downstream nodes immediately. Sync the completed graph and dispatch ready dependents without
putting reconciliation on their critical path.
After a confirmed Canvas batch, use one bounded long-lived call to follow cloud status and outputs:
```bash
"${PVX}" canvas paid follow --run-id <run-id> --project <slug> --format markdown
```
`follow` uses a finite 30-minute default deadline and backs unchanged status reads off from 2 seconds
to at most 30 seconds. A dependency-blocked node is not terminal while another followed node is still
running. The command emits `Canvas node ready`, returns `editor_url` and `cloud_outputs`, and does not
download media or query credits by default. Terminal generation does not wait for run/task attribution;
incomplete audit attribution must not hide the current cloud result or block a ready downstream node.
The run's current result and its ownership remain distinct; ambiguous submissions must not be retried.
For a requested download, add `--download`; restrict it with `--download-node-ids <a,b>` when the user
only wants specific outputs. This never guesses a task ID from node history: the current graph result
or this run's attributed IDs must identify the asset. A failed free download is retried at most three times
without regeneration. Downloading does not run local QA or query credits.
Do not query credits while a normally submitted batch is still running. More importantly, do not query
them automatically after completion either. Submission handling and default recovery preserve run IDs,
receipts, node states, output references, and confirmation plans without post-generation balance calls.
Pre-generation account checks and the next paid batch's preflight are unchanged.
Only for explicitly requested credit reporting, refresh once:
```bash
"${PVX}" canvas paid reconcile --run-id <run-id> --credits --deadline-seconds 0 --format markdown
```
Report `credits_consumed` with `credits_source=account_balance_delta` only for that request. Do not query or match account
usage rows. A balance delta can include unrelated spending, especially for an older run; disclose this
and do not describe it as exact task billing. An immediate zero delta can remain pending and must not
be described as final consumption. Without `--credits`, return `credits_status=not_requested` and null
consumption. `follow --credits` is also available for an explicitly requested combined wait/report.
Use bounded blocking only when retry safety or an ambiguous submission requires ownership proof:
```bash
"${PVX}" canvas paid reconcile --run-id <run-id> --deadline-seconds 300 --format markdown
```
Attribution requires matching run, task, history, node, account, and workspace evidence. If status
remains unresolved or unattributed, report the durable ledger path and do not resubmit the paid
mutation.
Default `reconcile` only recovers status and references; it does not download or query balances.
## Codex In-App Browser Handoff
Resolve the structured handoff immediately after binding the project, before ordinary preparation:
```bash
"${PVX}" canvas handoff
```
The result uses `pixverse.canvas_browser_handoff.v1` and provides `project_id`, `editor_url`, and a
fail-closed `browser_handoff` contract. Execute the exact reuse, visibility, retention, and verification
sequence from `../../skills-shared/web-handoff.md`.
Its `preview_policy` is an Agent instruction, not a browser-level fullscreen lock. Apply
`automatic_mode=preserve_current_view`, `visibility_scope=initial_handoff_only`, and the
non-interrupting refresh policy together with Preserve The User's Viewing State above.
In Codex desktop:
1. Use the current top-level Computer Use/CUA tool when exposed; in current Codex desktop this is
`mcp__cua_repl.js` with the `cua` JavaScript API. Invoke it directly instead of searching `ALL_TOOLS`
or counting browser-like nested tools. A zero result from those inventories is not evidence that
top-level browser control is unavailable.
2. Reuse a valid task-local `Tab` binding when it is already known to match `browser_handoff.url`.
Otherwise, because the handoff URL and browser are already known, make the first CUA call exactly
`cua.createBrowserTab("iab", browser_handoff.url, { visible: true })`. Do not put `cua.getState()`
or another inventory call in front of this visible handoff; use targeted tab listing only after the
returned binding exists.
3. Mark the returned tab with `markHandoff()` or `markDeliverable()` according to whether later
automation is expected. Verify its current page state and exact URL with the current CUA API; do not
call obsolete `visibility.get/set` methods that the active API does not expose.
4. If that known-URL create call fails during CUA startup or initialization before returning a `Tab`,
call `mcp__cua_repl.js_reset` once and retry the same create call once. After a second failure,
report the actual error and use the direct-link fallback. Do not describe the tool as
missing merely because an unrelated inventory omitted it. If only the legacy Browser API is exposed,
follow its returned documentation and still select exact `iab`.
5. After a successful graph mutation, inspect the currently visible preview without enlarging it.
Reload at most once if the visible editor is stale and this will not interrupt the user's active
viewing state; otherwise defer visual checking. Rely on CLI graph readback for graph verification.
Within one Codex task, keep the acquired CUA tab binding and exact target tab as task-local state. Do not
reinitialize the runtime, reopen the same URL, or reread the same returned documentation during the same
turn. In a later turn, use that binding directly and re-mark it; reinitialize only when the prior binding
is invalid. A different tab created by an unrelated browser test is not the Canvas binding. Turn-scoped
retention still must be renewed and is not a reason to create another tab.
Use structured timings returned by `canvas prepare`, mutations, and `canvas paid follow` to separate
CLI work, PixVerse API reads/submission, local I/O, and paid-generation waiting. The Agent should add
its own planning and IAB handoff elapsed times to the user-facing diagnosis without launching extra
network commands solely for measurement.
Never use `getForUrl`, `getDefault`, Chrome, Edge, an extension browser, URL auto-selection, Python
`webbrowser`, or `open` for this handoff. If `iab` is unavailable, return the direct `editor_url` and
stop browser automation immediately; do not infer success from a failed screenshot or switch surfaces.
A system browser is permitted only when the user explicitly requests `"${PVX}" canvas handoff
--open-system`.
The in-app Browser has a separate profile. If Canvas redirects to `/login`, ask the user to sign in in
that same Codex Browser tab. Never transfer CLI credentials, tokens, or cookies into the page.
## Reviewed Command Surface
| Class | Commands | Rule |
|---|---|---|
| project creation | `project create` | explicit user intent or one durable automatic bind |
| reads | `graph get`, `graph status`, `graph invalid-nodes`, `node get`, `node schema`, `node versions`, `node version`, `capabilities canvas`, `patch dry-run` | safe after target resolution |
| atomic mutations | `patch apply`, `graph reconcile`, `node rerun`, `dispatch` | accepted checkpoint plus current version condition |
| non-atomic mutations | `dispatch rebind`, `node extract-audio`, `node version apply` | blocked unless explicit best-effort opt-in |
If the bundled CLI adds a Canvas command, changes a mutation effect, or removes a version condition,
stop and update the reviewed command contract before exposing it through the plugin.
## Recovery Map
| Signal | Response |
|---|---|
| external changes or edit conflict | sync, review, and rebuild from the latest graph |
| invalid checkpoint or layout-mode change | review cloud state, then use `--reset-checkpoint` |
| binding update failed after create | do not recreate; repair with the known project ID |
| workspace override rejected | select the active workspace and remove `--workspace-id` |
| region mismatch | restore the binding's `PIXVERSE_REGION`, or explicitly bind the intended project in the selected region |
| mutation verification unknown | do not retry; sync and verify |
| legacy paid confirmation required | stop using the mutation preflight; use `canvas paid preflight` |
| confirmation plan required | run the read-only paid preflight; never add `--confirmed` manually |
| confirmation preference required | stored skip no longer applies; run a fresh preflight and follow its policy, never swap execution flags |
| confirmation plan stale | create and show a fresh read-only preflight |
| confirmation plan already used | reconcile its durable run; do not resubmit |
| confirmation plan state unreadable | repair or preserve the local plan/ledger evidence; fail closed and do not submit |
| billing context or membership blocked | repair account/workspace/model route and preflight again |
| deterministic validation rejection for every target | generation did not start; fix reported fields, sync, and run a fresh preflight without attribution waiting |
| status unresolved or unattributed | preserve/report ledger evidence; do not resubmit |
## Do Not
- Do not create a new Canvas project for every command or ask for a project ID when local binding can
safely resolve it.
- Do not mutate before accepting and reviewing the latest checkpoint.
- Do not use an unbounded watcher or repeated browser reload loop.
- Do not bypass edit-version conflicts, duplicate uncertain mutations, or treat a timeout as failure.
- Do not make credit observation block a ready downstream generation.
- Do not use account-usage row matching for ordinary Canvas credit reporting.
- Do not silently switch from the Codex in-app Browser to Chrome or a system browser.
SHA-256: 0e7bae29aaf34d3df2f0447fb62a50080b467ea44c2e9e66efa0475a49774453