Files
a298ed3fc1 feat: add the viewer's roles to unnamed data table connections (#11709)
* feat: connect to a data table with every role of the job's viewer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: key a cached result by the run's recorded viewer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pin the per-viewer cache key and fix a stale doc sentence

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: length-prefix the viewer in the result cache key

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: drop data table role set logins unused for 30 days

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: pin the ee ref with the set login sweep fixes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: pin the ee ref with the sweep retry fix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: give the cache key test its own migrated database

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: add the viewer's roles to a data table connection that names none

The default role of a data table under roles is now a floor: a connection
naming no role also holds the roles its viewer is a tenant of, unless the
default is admin. This replaces the explicit as=viewer option, which is
removed from the reference parser, the executors and both clients. The roles
drawer warns while the default is admin and confirms before removing the
default role.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: let a data table have no default role

A connection naming no role then holds its viewer's roles only and is
refused without any. The roles drawer offers the choice, states what an
unnamed connection gets for the current settings, says who gets admin while
it is the default, and notes who owns a table created over several roles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: start a data table's roles with admin open to everyone

Turning roles on from the drawer seeded admin with no tenant, which refused
every non-admin member's job as soon as it was saved. It now starts open to
everyone, so the toggle changes nothing until the roles are edited, and the
drawer warns for as long as everyone can connect as admin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: update ee-repo-ref to 40ab35596e6fcb82a4dfc56e186befb807f8e2af

This commit updates the EE repository reference after PR #873 was merged in windmill-ee-private.

Previous ee-repo-ref: a5def184a29fffab8ed42c70c5a7445b2b3cbe98

New ee-repo-ref: 40ab35596e6fcb82a4dfc56e186befb807f8e2af

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-10-13 09:01:21 +00:00
..

System Prompts

The single source of the AI guidance Windmill ships: what an agent needs to know about Windmill itself to write flows, scripts, apps, pipelines, resources, triggers and schedules. Two consumers are built from it:

  • the chat (the frontend AI chat: flow, script, app and global modes), through the $system_prompts alias to auto-generated/;
  • the CLI (wmill init / wmill refresh prompts), as the skills embedded in cli/src/guidance/skills.gen.ts, next to the AGENTS.wmill.md template in cli/src/guidance/core.ts.

Guidance written in one consumer only is guidance the other never gets. Write it here.

Structure

system_prompts/
├── base/              # Hand-written guidance, one file per topic (flow-base.md, raw-app.md, …)
│                      # plus CLI-only intros (flow-cli.md, script-cli.md, raw-app-cli.md, …)
├── languages/         # Hand-written per-language guidance (bun.md, python3.md, …)
├── generate.py        # Builds everything under auto-generated/ and cli/src/guidance/skills.gen.ts
├── utils.py           # Shared helpers, including the fence renderer
└── auto-generated/    # Generated — never edit
    ├── prompts.ts     # One export per base/ and languages/ file (chat render), SDK docs, schema
    ├── index.ts       # Chat helpers (getFlowPrompt, getScriptPrompt, …)
    ├── skills/        # One SKILL.md per CLI skill (cli render)
    ├── sdks/          # SDK reference extracted from the TypeScript and Python clients
    └── cli/           # CLI command reference extracted from cli/src/commands/

How a topic is assembled

TOPICS in generate.py lists, for each topic, the parts both consumers concatenate in order: a file under base/ or languages/, or a token filled per consumer ({lang}, {sdk}, {wac_sdk}, {openflow_schema}, {cli_commands}). The same entry produces the topic's chat helper in index.ts and its CLI skill, so a part added there reaches both. A part tagged ('chat', …) or ('cli', …) goes to that consumer only, and cli_intro heads the CLI skill with a CLI-only workflow file (flow-cli.md, script-cli.md, raw-app-cli.md).

Topic Chat helper CLI skill Shared parts
script getScriptPrompt(lang) write-script-<lang> script-base.md, the language file, its SDK
flow getFlowPrompt() write-flow flow-base.md, the OpenFlow schema
raw app getRawAppPrompt(lang) raw-app raw-app.md (chat adds the SDK)
resources getResourcePrompt() resources resources.md
workflow-as-code getWorkflowAsCodePrompt(lang) write-workflow-as-code workflow-as-code.md, the WAC SDK
pipeline getPipelinePrompt() write-pipeline pipeline-base.md
triggers, schedules, preview, CLI commands — same names CLI only

The app chat embeds RAW_APP_BASE directly (chat/app/core.ts).

Consumer fences

A sentence that only one consumer should see stays in the shared file, fenced:

A tool name outside that character set fails every run of the flow.
<!-- chat-only -->
The flow write tools refuse such a name.
<!-- /chat-only -->
<!-- cli-only -->
`wmill lint <flow folder>` reports it before anything runs.
<!-- /cli-only -->

render_for (utils.py) renders every file once per consumer: prompts.ts gets the chat render, the skills get the cli render, and the fence lines reach neither. Fences sit on their own lines and cannot nest; an unbalanced or misspelled fence fails generation (an HTML comment opening with cli or chat, or ending in only, is read as a fence attempt).

Use a fence for a sentence or a section. When most of a topic differs per consumer, a CLI-only file used as cli_intro reads better than a file that is mostly fences.

What goes where

  • Here: anything true of Windmill regardless of who is asking — module shapes, data flow rules, what the editor accepts, how data tables, secrets or app access work.
  • In the chat's TypeScript prompt builders (frontend/src/lib/components/copilot/chat/*/core.ts): only tool plumbing (which tool to call, its arguments) and values known at run time (the user's name, this app's data-table policy, session capabilities).
  • In cli/src/guidance/core.ts (AGENTS.wmill.md): project-level CLI workflow — which skill to use, deploying, debugging jobs.

A file in TOPICS names no chat tool, not even inside a chat-only fence: it reaches every chat mode (flow mode's system prompt, global mode's get_instructions), each with its own tool names, and global/sessionToolset.test.ts fails a prompt that names a tool its session lacks. Describe the action instead ("the flow write tools refuse such a name") and name the tool in TypeScript. The one exception is flow-chat-special-modules.md, which only flow mode reads.

Regenerating

After editing anything here, or when SDK methods, the OpenFlow schema or CLI commands change:

python system_prompts/generate.py

CI (.github/workflows/check-system-prompts.yml) runs system_prompts/check-freshness.sh, which fails when the committed output is stale.

To also refresh the standalone skills in a Claude plugin checkout:

python system_prompts/generate.py --plugin-dir ~/windmill-claude-plugin

--plugin-dir accepts:

  • the windmill-claude-plugin repo root
  • a plugin root such as plugins/windmill
  • a direct skills/ directory

To regenerate the public docs repo (consumed by context7):

python system_prompts/generate.py --context7-dir ~/windmill-cli-docs

--context7-dir writes a fully-rendered snapshot (AGENTS.md, cli-commands.md, skills/<name>/SKILL.md, README.md, manifest.json with the Windmill version) with all template placeholders resolved — suitable for ingestion by docs aggregators. In CI this runs from .github/workflows/publish-cli-docs.yml on every release tag. The generator refuses to wipe the target directory unless it's empty or has a context7 marker (context7.json, manifest.json, or a windmill-cli-docs git remote), so a typo can't delete unrelated files.