Skip to main content

Kanban HTML Projects

A Kanban project is a static project made of HTML, CSS, JavaScript, images, and fonts. It renders the HTML entry point in its configured artifact directory. Kanban projects do not create conversations, run history, Workflow Output, Diagrams, or Logs, and they do not need an executor. A restricted JavaScript bridge lets the page read and write the current project's KV data and knowledge base, and access related-project KV according to declarations on both projects. The page cannot choose a project or data scope for itself.

Minimal Project

A project without parameters needs only two files:

roadmap-kanban/
|-- package.json
`-- index.html
{
"id": "8bd4fef2-e083-4201-a89f-6f7d0676df53",
"name": "@workflow-code/roadmap-kanban",
"version": "0.1.0",
"private": true,
"type": "module",
"devDependencies": {
"@workflow-code/cli": "0.2.0",
"workflow-code": "0.2.0"
},
"workflowCode": {
"projectType": "kanban",
"projectInfo": {
"dataStorage": {
"mode": "both"
},
"relatedProjects": []
}
}
}

Core and CLI 0.2.0 in the example are the current exact compatibility baseline. New templates write the current Core and CLI versions. Existing Kanban projects must add missing dependencies and the dataStorage declaration, then update their lockfile; do not use ^, ~, workspace:*, or another version range. A Kanban page does not import or execute the Core runtime, but the server still uses devDependencies.workflow-code to determine host compatibility.

When no entry point is configured, the artifact directory defaults to the project root (.) and the HTML entry point defaults to index.html. Existing projects therefore do not need to migrate their entry-point configuration. A Kanban project should not include an index.ts, interface.ts, or workflow.defineExecutor(...) solely to make it runnable. Without kanban.json, the host hides all configuration controls and lets the HTML canvas fill the workspace.

Desktop generates a UUID and immediately writes it to package.json.id when creating or cloning a Kanban project. A manually imported legacy empty-ID Kanban can still be registered and edited for migration, but it cannot access project data or publish until the UUID is written to source. When creating and binding the cloud application, Desktop uses the existing package UUID, or the stable registry UUID for a legacy project and writes it to package.json.id. Server does not allocate another UUID.

Build Artifacts and Entry Points

Vue, React, Vite, and other frontend projects can put browser-ready files in a separate directory and configure Kanban to load its HTML entry point:

{
"workflowCode": {
"projectType": "kanban",
"projectInfo": {
"dataStorage": { "mode": "both" },
"relatedProjects": [
{
"alias": "stocks",
"projectId": "f4ee23d7-abbb-4ba3-8baf-75b7c6c0b964",
"grantToRelatedProject": {
"read": { "mode": "all" },
"write": { "mode": "none" }
}
}
]
},
"kanban": {
"artifactDir": "dist",
"entry": "index.html"
}
}
}
  • artifactDir is relative to the project root and defaults to ".".
  • entry is relative to artifactDir, defaults to "index.html", and supports .html and .htm files.
  • projectInfo.relatedProjects is the shared relationship declaration for every project type, not part of kanban. A relationship is confirmed only when the target also declares the current project; effective access comes from the target's grantToRelatedProject.
  • Neither path may contain an absolute path, drive letter, UNC path, .., directory escape, or symbolic-link escape. .git, .hg, .svn, and node_modules cannot be artifact or entry-point directories.
  • The iframe reads only files inside artifactDir; relative paths cannot reach source files, package.json, or files outside the project.
  • ES modules, dynamic imports, WASM, and fetch() can read resources inside the artifact directory. CSP connect-src permits only same-origin resources for the current short-lived preview capability plus data: and blob:; public network access is blocked.

Workflow Code Desktop and Server do not install frontend dependencies, run build scripts automatically, or connect to a dev server, port, or HMR. Build with your own tooling. When the Desktop assistant edits framework source for a project with artifactDir and entry, it may run the project's existing one-time build command and inspect the entry artifact only when dependencies are already installed. It never starts a dev/watch server, installs dependencies, or invents a build script. The Desktop refreshes the workspace only when the artifact directory, the entry configuration in package.json, or the parameter list in kanban.json changes. Source-only changes do not refresh a preview. Server Web does not watch local files; upload again through the Desktop or CLI after rebuilding.

Plain HTML, CSS, JavaScript, images, and fonts normally work across operating systems. Output that depends on native modules, an absolute local path, node_modules, or a specific development server is not a supported static browser artifact.

Parameter Manifest

To expose multiple presentations of the same page, add kanban.json in the project root:

{
"version": 1,
"params": [
{
"name": "title",
"type": "string",
"label": "Board title",
"defaultValue": "Product roadmap"
},
{
"name": "accent",
"type": "string",
"label": "Accent color",
"control": "radio",
"defaultValue": "#2563eb",
"options": [
{ "value": "#2563eb", "label": "Blue" },
{ "value": "#0f766e", "label": "Teal" }
]
},
{
"name": "columns",
"type": "string",
"label": "Visible columns",
"control": "checkboxes",
"multiple": true,
"options": [
{ "value": "todo", "label": "To do" },
{ "value": "doing", "label": "In progress" },
{ "value": "done", "label": "Done" }
]
}
]
}

version must currently be 1. params directly uses WorkflowParamDefinition:

CapabilityFields
Text and long texttype: "string", optionally control: "textarea"
Numbertype: "number"
Toggletype: "boolean"
Positional-argument compatibilitytype: "positional"
Filetype: "file", optionally accept, multiple, or format: "json"
Single-select enumoptions with control: "select" or "radio"
Multi-select enummultiple: true with control: "checkboxes"

Each field name must be unique within the manifest. Defaults live in defaultValue; the host converts number, Boolean, and multi-select values to native JSON values before injecting them into the page. A file parameter receives only safe fields from a managed platform file reference, never a local absolute path or server storage path.

Use kanban.json for a page title, theme, filters, visible columns, and layout modes. Let the HTML interface create, edit, and delete business data such as tasks, cards, and records, then persist it through the KV bridge. Do not encode a business list into comma-separated textarea parameters just so it can be maintained in the configuration panel.

When saving binary static assets such as images and fonts through the Server file API, set encoding: "base64" on content. Reading, publishing, and source downloads preserve that encoding and the original bytes. Text files do not need encoding.

Read the Current Configuration

The host injects a read-only interface before the page loads:

function render(configuration) {
const params = configuration?.params ?? {};
document.querySelector("h1").textContent = params.title || "Board";
document.documentElement.style.setProperty("--accent", params.accent || "#2563eb");
}

render(window.workflowCodeKanban.getConfiguration());

window.addEventListener("workflow-code:kanban-configuration-change", (event) => {
render(event.detail);
});

Both getConfiguration() and the event's detail return:

{
id: string;
name: string;
params: Record<string, unknown>;
}

Changing a configuration or parameter does not reload the iframe. Listen for workflow-code:kanban-configuration-change and update your DOM, canvas, or other in-page state. Persistent parameters can be changed only in the host configuration panel. Use the KV or knowledge-base APIs below for business data created inside the page.

The Desktop assistant can create, read, update, and delete host-managed named configurations with host_project_configuration. Before a write, it reads the parameter definitions and current configuration. Create accepts a name and initial parameters; update can rename a configuration and submit a partial parameter update by configuration ID, name, or the active configuration; delete must identify an ID or name and cannot delete the final configuration. The tool is registered by project type and loads only when the assistant opens a local Kanban project. Workflow and Conversation projects neither display nor start it. It is bound to the current Kanban target and accepts no project ID, database path, or other scope. Writes still pass through Desktop Main validation and SQLite storage, and immediately notify the parameter panel and iframe. The tool cannot write existing configurations back to kanban.json or create or forge a file reference.

KV and Knowledge-Base Bridge

window.workflowCodeKanban also exposes an asynchronous data interface scoped to the current project:

window.workflowCodeKanban = {
getConfiguration(): { id: string; name: string; params: Record<string, unknown> },
getRuntimeLocale(): Promise<"zh-CN" | "en">,
kv: {
getValue(key: string): Promise<unknown | undefined>,
setValue(key: string, value: JsonValue): Promise<void>,
},
relatedProjects: {
get(alias: string): {
kv: {
getValue(key: string): Promise<unknown | undefined>,
setValue(key: string, value: JsonValue): Promise<void>,
subscribe(
listener: (event: { alias: string; revision: string }) => void,
): () => void,
},
},
},
knowledge: {
listDocuments(options?: { pageSize?: number; cursor?: string }): Promise<KnowledgeDocumentPage>,
getDocument(id: string): Promise<KnowledgeDocument | null>,
createDocument(input: { id?: string; title: string; markdown: string }): Promise<KnowledgeDocument>,
editDocument(id: string, input: { title?: string; markdown?: string }): Promise<KnowledgeDocument>,
deleteDocument(id: string): Promise<{ id: string; deleted: boolean }>,
searchDocuments(options: {
query: string;
documentId?: string;
beforeLines?: number;
afterLines?: number;
maxMatches?: number;
pageSize?: number;
cursor?: string;
}): Promise<KnowledgeSearchResult>,
readDocumentLines(id: string, options: {
startLine: number;
endLine: number;
}): Promise<KnowledgeDocumentLinesResult>,
},
};

getRuntimeLocale() asynchronously returns "zh-CN" or "en" selected by the current Desktop, Server Web, or Embed host. A Kanban that explicitly supports localization should switch page copy, errors, ARIA labels, date formatting, and number formatting together. It must fall back to the project's default Chinese when an older host lacks the Bridge or the call fails. kanban.json remains a static manifest and cannot be dynamically replaced by the runtime language.

The page defines KV keys, while the host always confines the current-project bridge to project scope. A setValue value must be a finite number, string, Boolean, null, array, or plain object containing only those values. Do not pass undefined, a function, a circular reference, or another non-JSON-serializable value. The current version has no KV list or delete API; overwrite a state value by writing a replacement.

relatedProjects.get(alias).kv resolves only a direct relationship declared in this Kanban's projectInfo.relatedProjects and confirmed by both projects. getValue and setValue check the target's read and write grants to the Kanban. When the target uses prefixes, each key is checked on every call. Iframe requests carry only alias, key, and a write value; they cannot provide the target-project UUID, scope, target, or database path, and expose no related-project list/delete or PersistentValue operation. See KV and session for alias, UUID, count, and grant limits.

Use relatedProjects.get(alias).kv.subscribe(listener) to observe target-project KV revisions. If the host already has a current revision, it calls the listener asynchronously with an initial snapshot. Later events contain only the fixed alias and an opaque revision, never a specific key. Server does not send the initial revision again as a change and coalesces consecutive writes from the same target into the newest revision. Debounce nearby events into a getValue() refresh and keep the last successful data on screen. If an automatic refresh fails, show a non-blocking warning instead of clearing current content. Call the returned unsubscribe function when the page unloads or configuration changes.

const unsubscribe = window.workflowCodeKanban.relatedProjects.get("stocks").kv.subscribe(
({ revision }) => {
scheduleRefresh(revision);
},
);

window.addEventListener("pagehide", unsubscribe, { once: true });

In Desktop, the related project must already be imported into the local registry, and values come from that project's own KV directory. Desktop watches the authoritative value file during an active Kanban session. Server Web previews and public Embeds require only read access to the current Kanban; the visitor need not be a target-project member, but both current declarations and the target's grant to the Kanban must remain valid. A related write also requires the host to allow that visitor to manage the Kanban. Authenticated SSE sends a complete revision snapshot on connection and reconnect. A missing target, pending relationship, insufficient grant, invalid alias, or out-of-range key returns only a safe "related project unavailable" error. Revoking the relationship or read grant, or invalidating the binding, closes the revision stream. Server reads the relationship from the page's fixed resolvedTarget; target KV is project-level data and does not switch with the target project's version.

Automatic propagation occurs only within the same host backend: an Desktop-local Workflow update notifies Kanban pages open in that Desktop; a Server manual run, schedule, Webhook, or External API update notifies Server Web previews or Embeds connected to that Server. Desktop and Server KV are separate stores and never synchronize automatically.

The following pattern stores board state per named configuration and falls back to in-memory data when the page is opened directly without a bridge:

const configuration = window.workflowCodeKanban?.getConfiguration();
const storageKey = `kanban.board-state.v1:${configuration?.id || "default"}`;
const kv = window.workflowCodeKanban?.kv;

let board = { version: 1, tasks: [] };
if (kv) {
try {
board = (await kv.getValue(storageKey)) ?? board;
} catch {
showStorageError("Unable to load. Try again.");
}
}

async function saveBoard() {
if (!kv) return;
try {
await kv.setValue(storageKey, board);
showStorageStatus("Saved");
} catch {
showStorageError("Unable to save. Try again.");
}
}

Every call returns a Promise. A page can have at most 16 pending requests at once; a request taking more than 15 seconds is rejected with KANBAN_BRIDGE_TIMEOUT. Non-JSON KV values and other unclonable inputs are rejected in the page with KANBAN_BRIDGE_INVALID_REQUEST, rather than exposing the browser's raw DataCloneError. Errors use fixed safe codes and copy, never a database path, stack trace, or host internals. Late responses from an old page are discarded after an iframe refresh.

The knowledge-base list reads at most 100 documents at a time. Search caps pageSize and maxMatches at 1000 and beforeLines and afterLines at 50. A single readDocumentLines call reads at most 1000 lines, and endLine cannot be smaller than startLine. Out-of-range calls are rejected before reaching storage.

Named Configurations

Whenever kanban.json.params is non-empty, the host manages named configurations for the current user:

  • The first open creates a Default Configuration.
  • A new configuration starts with parameter defaults.
  • Duplicating a configuration copies its current values.
  • Parameter changes notify the page immediately and are saved with about a 300 ms debounce.
  • Configurations can be renamed, selected, and deleted, except that the last one cannot be deleted.
  • Configurations are isolated by project and user, are not written back to source, and are not shared between logged-in users.
  • The Desktop assistant can read or update existing named configurations within the current project; kanban.json still defines only defaults for new configurations.
  • When kanban.json parameters change, existing configurations receive defaults for new parameters and drop obsolete fields. The Desktop also releases local files referenced only by removed file parameters.

On desktop widths, the workspace places parameter settings on the left and the HTML canvas on the right. The selector at the top of the settings area switches configurations; a separate action creates one, while rename, duplicate, and delete live in the current configuration's action menu. Parameter controls fill the available width below and save status stays in the title bar. Below a 760px workspace container, the UI switches to a top configuration control and parameter drawer, so the same Embed adapts even in a narrow panel. A load, save, or delete failure keeps current values and offers an in-place retry.

Relative Resources and Security Boundaries

CSS, JavaScript, images, and fonts must use paths relative to the artifact directory:

<link rel="stylesheet" href="./assets/board.css" />
<img src="./assets/board.png" alt="Board preview" />
<script src="./assets/board.js"></script>

The page runs in sandbox="allow-scripts" with an offline Content Security Policy:

  • Resources inside the current artifactDir, plus required data: and blob: content, are allowed.
  • Public-network requests, parent-page DOM access, Node, Electron preload, popups, downloads, and top-level navigation are blocked.
  • Static resource requests are pinned to the current draft or published version. .., absolute paths, symbolic-link escape, .env, project metadata, and cache paths are rejected.
  • KV, knowledge-base, and related-project requests are verified against the current iframe, a random nonce, a request ID, and an operation allowlist. The page cannot choose a target-project UUID, scope, database path, or publication target.
  • A single preview resource can be at most 20 MB.

Do not depend on a CDN, remote fonts, remote APIs, or browser capabilities requiring allow-same-origin. The page can react to clicks, dragging, and DOM changes freely; write business data to the bridge explicitly when it must survive a refresh, restart, or configuration switch.

Upload Ignore Rules

The root .workflowignore is the only ignore configuration used when the Desktop or CLI packages files for upload. It is independent of Git: .gitignore files, including nested ones, do not participate in upload matching. Server receives the selected archive, and Server Web never reads a local directory.

Rules support comments, directories, globs, **, and ! re-inclusion. Paths are always matched with /. When .workflowignore is missing, empty, or contains only comments, every regular file under the project root is uploaded; the Desktop or CLI shows the file count and total size first. .workflowignore itself is always included in the upload package.

These files cannot be excluded: package.json, the Workflow or Conversation entry point, the configured Kanban HTML entry point, and an existing kanban.json. If a rule excludes a required file, upload identifies the matching rule. Symbolic links, special device files, directory escape, individual-file limits, and package-size limits are independent security checks and cannot be bypassed with ignore rules.

Recommended rules for a Kanban project:

.git/
node_modules/
coverage/
.cache/
.turbo/
*.log
*.tmp
*.tsbuildinfo
.env
.env.*
!.env.example

src/
tests/
dist/**/*.map

Do not ignore the actual artifactDir. For example, when artifactDir is dist, .gitignore may exclude dist/ to keep it out of Git, but .workflowignore must retain it so the build artifacts are uploaded.

Desktop, Server Web, and Embed

The Desktop can create a default Kanban project or a parameterized sample and edit and save HTML, CSS, and JavaScript. A root-directory artifact project refreshes after a successful save or an external file change. With a separate artifactDir, the artifact contents, entry configuration, or kanban.json parameter manifest trigger a refresh; ordinary source changes do not. Unsaved Monaco content never enters the preview. Project details include Knowledge Base and KV Data views backed by the same local project data as the page bridge. KV and knowledge are stored in separate project-isolated local KV and PersistentValue directories. Rebinding the project to a cloud UUID keeps the same local namespace.

The Desktop or CLI can select local Kanban text and binary static resources with .workflowignore and upload them to Server. Server does not run tsup, install dependencies, or create a Node runtime for Kanban. It validates the configured HTML entry point and retains the static artifacts; Server Web only inspects and manages uploaded server versions.

The Server Web preview allows current-project KV and knowledge-base writes only for users with project-management permission, and pins calls to the selected project and target. Related-project access follows both current declarations and the target project's grant to the Kanban; the visitor need not be a target-project member. Public Embed always uses a fixed published version. A Kanban Embed visitor must both be logged in and have a valid Embed token; a project relationship does not open anonymous access. After verification, the site issues a short-lived signed static-preview capability tied to the project, user, and fixed version, so iframe resource URLs do not expose the long-lived Embed token. The signature uses WORKFLOW_AUTH_COOKIE_SECRET, falling back to WORKFLOW_SERVER_ADMIN_KEY when unset. Every instance in a multi-instance deployment must use the same stable key so preview capabilities remain valid across instances and restarts. Server Web previews and Embeds refresh the capability before it expires. Refresh reloads only the iframe and keeps the named configuration; temporary failures retry, while an expired capability that cannot be refreshed shows a clear error. Bridge calls and configuration create, duplicate, rename, save, switch, and delete operations all carry the resolvedTarget obtained on first load. If the Embed token now points to a new published version, the old page never combines a new preview with its old bridge target; it asks the visitor to refresh the whole page. Named configurations remain user-specific, while project KV and knowledge are shared by every visitor meeting the login and token requirements, and those visitors may edit or delete shared content. A preview URL contains a short-lived capability, CSP nonce, and fixed version and cannot read another project or version.

Project Relationships and Delivery

workflowCode.projectInfo.relatedProjects is the shared relationship declaration for every project type. A Kanban project group generates dependency locks only from relationships confirmed by both projects. Pending relationships may be saved and published with the project, but grant no KV access and do not enter the included/external dependency group. For each confirmed relationship, the publisher chooses a delivery mode that is recorded in the Kanban's exact published version:

Delivery modeVersion dependency lockDownload behavior
includedPins the related project's exact version and source hash; that version must retain source.Downloads with the root project when the user has source permission; otherwise it is skipped with an application entry. It never falls back to the related project's latest.
externalKeeps only alias and related-project UUID; it does not lock a source version.Does not download with the root project. The consumer prepares the related project independently.

A project group still consists of independent projects, each with its own UUID, version, members, and permissions. Server verifies every project and relationship and reserves versions, then prepares and publishes included projects before activating the Kanban root version. A failure never takes the root version online. An interrupted publication can continue the same deployment and does not republish exact dependencies that already succeeded.

Source delivery and runtime data are separate. Project-group archives, uploads, publications, and downloads reject .env*, KV, schedules, run history, SQLite data, and host-user configuration. Desktop-local KV and Server KV are also separate databases. After deployment, a developer must initialize Server data by running the source Workflow entry point, calling the Server Run API, or using their own synchronization interface. The same applies after downloading locally. The platform provides invocation and bridge APIs; it never copies data automatically.

KV access comes entirely from both projects' projectInfo; Server keeps no separate data-grant record. The target can grant the Kanban none/all/prefixes read and write access. This does not permit source download, execution, or project management. Revocation immediately invalidates new bridge access and current revision subscriptions. Included-source download permission still uses a separate download_source approval request. Desktop enables Copy to Local Project only when every included dependency is downloadable. Copying creates new UUIDs for the whole group, rewrites relatedProjects[].projectId for every relationship between copied projects, and preserves original UUIDs for external relationships.

Publish, Download, and CLI

Kanban publication always retains static source and uses source mode, ignoring a bundled-mode choice. The file API transfers binary files such as images and fonts with encoding: "base64"; CLI and Desktop restore their original bytes when downloading. For related projects, the Desktop publish dialog shows the local project, Server project, confirmation state, source permission, and Server data-initialization state. A confirmed project that exists locally but not on Server is recommended as an included upload by default, but the publisher must still confirm it. A pending relationship is marked clearly and cannot be selected as included.

workflow-code structure ./roadmap-kanban
pnpm exec workflow-code workspace pack roadmap-kanban --path ./roadmap-kanban
pnpm exec workflow-code workspace upload roadmap-kanban --path ./roadmap-kanban --create
pnpm exec workflow-code workspace publish roadmap-kanban
pnpm exec workflow-code workspace download roadmap-kanban --target latest --path ./roadmap-kanban-copy

# Package and publish the stocks related project; both package.json files already declare the relationship.
pnpm exec workflow-code workspace upload roadmap-kanban \
--path ./roadmap-kanban \
--dependency stocks=./stock-workflow \
--publish-group \
--release-log "Publish the market board and its related Workflow"

workflow-code structure reads projectType: "kanban", artifactDir, entry, and the optional parameter manifest. Kanban is a static project: workflow-code run, workflow-code json, remote Run, debug node, Webhook, and external run all return an explicit execution-not-supported error.

Use the Generator

python .agents/skills/workflow-code-generator/scripts/create_workflow.py roadmap \
--dir ./roadmap-kanban --project-type kanban

python .agents/skills/workflow-code-generator/scripts/create_workflow.py roadmap \
--dir ./roadmap-kanban --project-type kanban --with-kanban-params

Both commands generate a complete editable Kanban project with .workflowignore, package.json, index.html, board-state.js, board-storage.js, a README, and pure-logic tests. --with-kanban-params also creates kanban.json with only a title and accent color. Tasks are always created, edited, dragged, and deleted in the page, then persisted per configuration through the KV bridge.

The generator creates a Simplified Chinese project by default and does not automatically add translation resources, a language switch, or locale detection. It adds those adaptations only after you explicitly request internationalization or multilingual support and identify the target languages.