Add a "Windmill CI tests" GitHub check run on any PR against the tracked
branch, so a workspace-fork customer can mark it a required status check and
have Windmill's own CI tests (the `// test:` annotation) block the PR —
replacing the GitHub Action that polls the CI-test results API.
Driven by the pull_request webhook (same event as the Phase 4 diff check): a
new git_sync_ci_test_check table tracks one check per (fork workspace, PR head
commit); a per-ci_test-job completion hook (result_processor) and the git-sync
poller sweep (monitor) conclude it. Reuses the Checks: write grant from #9552;
token repos keep the documented Action. See docs Phase 7.
EE logic lives in windmill-ee-private (git_sync_ee.rs); see ee-repo-ref.txt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Existing installations don't have polling; their git-to-Windmill direction
runs on GitHub Actions today, so the approval text describes the update as
replacing those workflows and notes every feature is opt-in.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
A promotion deploy's wm_deploy/** branch exists to be merged; without a PR
it's an orphaned branch. Fork PRs stay opt-in. Also scope the sync-repo
auto-pull default to sync mode so promotion repos can't pick it up.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
GitHub App references point at integrations/git_repository#github-app, the
workflow hints at deploy_gh_gl#github-actions-setup, and the sync workflow
at git_sync#github-actions (all anchors verified against the live docs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
Merges the instant-pull recommendation with the GitHub Action conflict note,
shown only for non-app repos; app repos need neither, and the redundant
'instant webhook sync requires' line is gone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
- Pull-from-Git defaults on only for app-backed repos (applied when the
selected resource resolves); polling is opt-in for token repositories,
with a warning alert recommending the GitHub App (instant pull + in-app
PRs) or the sync GitHub workflow
- App repos always use webhook delivery with polling fallback: the delivery
selector is gone and a stored polling mode is normalized back to auto
- Post-save modal reflects the auto-pull state instead of telling the user
to turn on a toggle that is already on
- Non-app PR hints recommend the GitHub App explicitly
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
promotion_open_prs/fork_open_prs are rejected on save without an Enterprise
plan (like auto_pull), and the deploy completion hook re-checks the plan
before opening PRs so flags stored while licensed stop driving GitHub calls
after a lapse.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
A git resource without an explicit branch polled as the bare "HEAD" ref,
which the fork/dev-label fan-out cannot scope (wm-fork/<branch>/*), so fork
sync silently never ran on polling-only repos. Resolve the remote's default
branch name with `ls-remote --symref HEAD` (one call for name + head sha);
"HEAD" only remains when resolution fails. The polling e2e test now uses a
branch-less resource to cover this shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
- Page header and sync-card description mention the pull direction, not only
push-on-deploy
- Promotion description walks the actual flow (wm_deploy/** branch, merge to
promote, sync the target workspace) and points at the PR toggle / workflow;
the Git Promotion docs link now also shows on configured cards, not only in
the empty state
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
The full read-modify-write raced the poller's concurrent last_synced_sha /
last_pull_status writes on the same column; mirror the EE status writer and
update only the matching repository element's open_pr_error key.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
- addSyncRepository skips the auto_pull/fork_open_prs defaults on fork/dev
workspaces where the backend rejects them (saving a new sync repo from an
EE fork 400'd deterministically)
- change_workspace_id strips webhook id/secret/error from the copied git_sync
and deletes the stale GitHub hooks post-commit: they deliver to the old
(archived) workspace URL, so the new workspace would report a live webhook
while polling at the relaxed interval; next save re-registers cleanly
- EE: PR diff checks for contributor-fork PRs clone the synthetic
pull/<n>/head ref (head.ref doesn't exist in the base repo)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
- detach_dev_workspace clears parent_workspace_id for prefix-less (attached
standalone) workspaces so they stop classifying as forks and deploying to
wm-fork/** branches; wm-fork- re-designated forks keep their parent; cache
invalidations mirror attach
- dev-workspace create/attach reject an environment label that equals a
git-sync repository's tracked branch (prod's or the candidate's): deploys
would target the very branch the repo syncs from, and the CLI guard would
fail every push job after the fact
- CLI unit tests: prefix-less fork beats wm_deploy derivation; isForkWorkspace
parent-id argument
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(db-health): add connection sizing guidance
The Database Connections panel showed current/max connections but gave no
guidance on how to size max_connections for the deployment. Derive an estimate
from the live worker fleet: each worker instance shares a pool sized
DEFAULT_MAX_CONNECTIONS_WORKER + (workers - 1), and each server opens up to
DEFAULT_MAX_CONNECTIONS_SERVER (both overridable via DATABASE_CONNECTIONS).
The endpoint now returns live worker/instance counts, the default per-server
and per-worker pool sizes, the estimated peak worker connections, the reserved
superuser connections, and a recommended max_connections floor (workers + one
server + 25% headroom). Servers do not ping worker_ping, so the recommendation
assumes one server and exposes the per-server increment. The panel renders this
as a sizing breakdown and warns when max_connections is below the recommended
floor.
Fixes WIN-2147
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(db-health): single source for pool-size constants + sizing tests
Address review: db_connect.rs kept its own copies of DEFAULT_MAX_CONNECTIONS_*
that duplicate the windmill_common constants the sizing guidance reads, so
tuning the runtime pool size would silently leave the guidance stale. Re-export
the windmill_common constants from db_connect.rs so there is one source of truth.
Add unit tests for compute_connection_sizing covering the zero-fleet, single
worker, multi-instance, and reserved-clamp cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(db-health): 20% headroom and 200-connection minimum floor
Lower the sizing headroom from 25% to 20% and never recommend below 200
connections (postgres defaults to 100; cheap headroom for growth/bursts/psql).
Update the guidance message and unit tests accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(db-health): honor DATABASE_CONNECTIONS in sizing recommendation
Address Codex P1: the runtime caps every process's pool at DATABASE_CONNECTIONS
when set (db_connect.rs), but the sizing guidance always used the default 50/5
pools. For a tuned deployment this under-estimated worker demand and could hide
a genuine under-provisioning (e.g. DATABASE_CONNECTIONS=100 with 5 instances is
500 worker connections, not 25).
compute_connection_sizing now takes the effective DATABASE_CONNECTIONS override
(read the same way db_connect.rs reads it): when set, each worker instance and
server pool is that value and the worker estimate is override * instances. The
response exposes server_pool_size / worker_pool_size (effective) and
database_connections_override; the panel renders both pool rows and labels them
(default) vs (DATABASE_CONNECTIONS), and the message states which source is used.
Adds a unit test for the override path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(db-health): exclude agent workers from connection sizing
Agent workers reach the API over HTTP (MODE=agent, Connection::Http) and hold
no postgres pool, but their pings still land in worker_ping (written server-side
by /api/agent_workers/update_ping). Counting them inflated the connection
estimate. Filter the fleet query by the worker-name prefixes: DB-connected
workers use "wk-" (WORKER_NAME_PREFIX), agent workers use "ag-"
(AGENT_WORKER_NAME_PREFIX). Only wk- workers/instances feed the estimate; ag-
workers are counted separately and surfaced as context ("N agent workers
excluded — they use HTTP, not postgres connections"). Adds a unit test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(cli): clarify workspace fork naming and parent-workspace context
Expand `wmill workspace fork`'s help and interactive prompts so the two
positional arguments are self-explanatory:
- Command description now explains that the fork is created from the
currently active (parent) workspace, that `workspace_name` is a
friendly display name that may contain spaces (quote it), and that
`workspace_id` is a bare slug auto-prefixed with `wm-fork-` which also
determines the git branch name.
- Interactive name/id prompts reworded to match.
Regenerated system_prompts CLI guidance to reflect the new description.
Fixes WIN-2148
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(cli): default fork name to "<parent>'s fork", make it optional
The fork's display name is no longer effectively required — it now
defaults to "<parent workspace name>'s fork" (fetched via
get_workspace_name, falling back to the local profile name / id) and
stays fully overridable via the positional argument or interactive
prompt.
To produce this default, `setClient` and the parent-name lookup are
moved ahead of the name/id resolution. The id default is decoupled from
the possessive display name: when auto-naming, the id/branch slug is
derived from "<parent>-fork" (e.g. wm-fork-acme-fork) rather than the
awkward "<parent>-s-fork". Branch-rename forks keep their branch-derived
id.
Regenerated system_prompts CLI guidance.
Fixes WIN-2148
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(cli): fix workspace fork help — parent is branch-resolved, not active profile
Addresses the codex/pi review: the fork help said the parent is the
"currently active" workspace and told users to `wmill workspace switch`,
but createWorkspaceFork resolves the parent from the current git branch's
wmill.yaml mapping (tryResolveBranchWorkspace) and ignores the active
profile. Reword to describe the actual branch-based resolution.
Regenerated system_prompts CLI guidance.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(cli): note branch-derived fork id default in rename workflows
Addresses the codex review: the `[workspace_id]` help and the interactive
prompt said the default id is derived from the name, but rename workflows
(non-base branch / --from-branch) keep the branch-derived default
(`branchDefaultId ?? branchToForkId(idBasis)`) to keep the id/branch
aligned with the branch being converted. Document that special case
rather than changing the intentional behavior.
Regenerated system_prompts CLI guidance.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cli): cap auto fork name at 50 chars for long parent names
Addresses the codex review: the "<parent>'s fork" default appended
"'s fork" to a parent name that can itself be up to 50 chars (varchar(50)),
so a parent name over 43 chars produced a default exceeding the limit and
tripped the effectiveName.length > 50 guard — failing `wmill workspace
fork --yes` (or accepting the interactive default) for a valid parent.
Truncate the parent portion so the generated default stays within 50.
Verified end-to-end: a 48-char parent name now yields a 49-char default
("... Team's fork") and the fork is created successfully.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- edit_git_sync_repository re-checks the runtime Enterprise gate against the
EFFECTIVE repo state after preservation: the older-client arm copies the
existing auto_pull back, which the request-side check never saw
- attach_dev_workspace now mirrors the fork-creation copy on the attached
workspace's own git sync: promotion repos dropped, auto_pull/fork PRs/PR
error stripped, and any managed webhook deleted after commit (the attached
workspace is parent-managed and must not keep pulling its old tracked branch)
- integration test: attaching an auto-pull-enabled workspace strips it
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
- Fork sync toggle renamed and kept in the pull section; the fork PR toggle
moves to the push section with a note that push settings apply to forks
- Fork/dev workspaces' push section names their actual branch instead of the
tracked-branch line; promotion repos hide the pull direction (promotion
pushes deploy branches on top of a sync-mode setup)
- Promotion mode line describes the wm_deploy/** branch + merge-to-promote
flow; workflow-fallback hints lead with the how-to and link to the docs;
test connection button demoted from accent per brand guidelines
- New server-owned open_pr_error on repo settings: the deploy completion hook
records why a PR couldn't be opened (e.g. app permission not yet approved)
and clears it on the next success; shown as a warning under the PR toggles
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
A PR that only touches files outside the repository's include paths gets
"In sync", which reads as a wrong verdict; the check summary (and managed
comment) now name the filters, e.g. "Only files matching this repository's
sync filters deploy on merge: `f/**` (excluding `f/pat/**`)."
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix: preserve worker group tag override on 'Run again'
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: keep tag override in sharable hash on args change
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: disambiguate reserved __tag hash key from args named __tag
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: prefix carried tag in sharable hash and react to tag changes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: re-resolve dynamic tags on 'Run again' with an explanatory note
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: treat only $args-templated tags as dynamic on 'Run again'
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: let a carried tag coexist with an arg named __tag via duplicate keys
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* perf(sessions): scope preview-tab refresh to items a chat tool touched
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(sessions): drop dead editor pane, scope raw-app reload by path
Multi-target migration P0. SessionWrapper's inline editor pane was dead (the sessions page always mounts it with hideEditor); remove it and the single-target machinery (setSessionTarget/pickEditorTarget/target-keyed editor views). Scope the raw-app file/runnable preview reload to args.path (the app's workspace path) instead of the session target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(sessions): back editor state with per-(kind,path) cells
Multi-target migration P1. Replace the three per-kind singleton stores/slots with per-(kind,path) cell maps, created on demand and kept (eviction deferred to P3). The runtime's public interface is unchanged: the flowStore/scriptStore/savedScript/rawApp/... getters and slot(kind) now forward to the 'active cell' per kind (a single-target shim, tracked by activePath, removed in P2 when the UI mounts one editor per tab). loadFlow/loadScript/loadRawApp and syncPreviewWithDeployed operate on the resolved cell; load logic and semantics are otherwise unchanged, so loading one item no longer clobbers another's state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(sessions): mount every editable preview tab as its own live editor
Multi-target migration P2 — the behavioral flip. resolvePreviewTab no longer takes a target: any editable route (script/flow/raw_app) resolves to an in-process editor, so several items are live at once (iframes remain only for real pages and regular non-raw apps). Each editor binds its own per-(kind,path) cell; the draft codecs close over that cell's store so two editors never cross-write. The single-target shim (activePath + the flowStore/scriptStore/... getters + slot(kind)) is removed; runtime exposes flowCell/scriptCell/rawAppCell(path). Tab open/navigate dedupe by (kind,path) and no longer setTarget. setLiveEditorDraft is gated on the visible tab (isActiveTab) so N editors don't clobber the one-per-(workspace,kind) live-draft slot (path re-key deferred to P4).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* perf(sessions): evict unreferenced editor cells; drop dead warm-editor LRU
Multi-target migration P3. Bound the per-(kind,path) editor cell maps: pruneEditorCells drops every cell no open preview tab still references, wired to a new onTabsChanged adapter callback fired on each tab-set change — so closing or navigating a tab away from an item reclaims its cell (dedupe keeps <=1 editor tab per item, so a pruned item has no live editor to strand). Also remove the now-dead editorWarmIds/promoteEditorWarm/MAX_WARM_EDITORS warm-editor LRU: its only reader (SessionWrapper.mountEditor) was removed in P0, and mounted editors are already capped per-tab by mountedTabKeys.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(sessions): retire session.target; preview is fully tab-driven
Multi-target migration P4 (final). Remove the session.target field and setSessionTarget: the preview is driven entirely by the tab model now (P2). hydratePreviewTabs no longer seeds a tab from target (saved previewTabs only); openEditorInSession seeds the preview via resetSessionPreviewTabs; normalizeLegacySession drops the retired target field from old records. The setLiveEditorDraft focus gate (isActiveTab, one-per-(workspace,kind)) is kept as-is; a per-path re-key is a possible future refinement, not needed for correctness.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(sessions): describe editor cells as-is, not by their refactor history
Address standards review: AGENTS.md requires comments describe the code as it is, not its drafting history. Drop the 'used to be per-kind singletons' / 'pre-refactor empty editor' / 'now' phrasings from the cell comments.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(sessions): update stale runtime.rawApp.val comments to cell.store
Address spec review: two comments still referenced the removed runtime.rawApp.val accessor; the live code uses the per-cell store now.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(sessions): fix editor-cell comments after main merge
Main's #9993 added svelte-ignore comments describing the old
runtime.savedFlow.val / runtime.rawApp.val singleton bindings. The
multi-target refactor binds each tab's own editor cell (cell.store /
cell.saved), so update the comment text to match; the ownership_invalid_binding
directives themselves remain correct (the targets are still runtime-owned).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(sessions): restore data-pipeline preview as a live editor tab
The multi-target refactor removed the old single-target editor pane —
PipelineEditorView's only mount point — so open_preview(kind="pipeline")
opened nothing, even though the chat tool and system prompt still make it
the first step of pipeline authoring.
Route a /pipeline/<folder> preview tab to the in-process graph editor:
- previewRouter: parsePipelineRoute + resolvePreviewTab map the folder to a
pipeline editor slot; PreviewSlot.editorKind gains 'pipeline'.
- previewTargetForSessionTarget('pipeline') returns the folder route target
(was undefined); open() keeps a single pipeline tab and retargets it to the
requested folder, since all pipeline tabs share one runtime.pipelineEditorState.
- PreviewTabHost mounts PipelineEditorView for the pipeline slot.
- PipelineEditorView gains an `active` prop; AI-helper registration and the
live-badge poll now gate on isActiveSession && active.
Register the pipeline tools on the session's own chat, not the singleton:
PreviewTabHost mounts the view outside the SessionWrapper subtree that
provides the scoped aiChatManager context, so getAiChatManager() fell back to
the app-wide singleton — build_pipeline_node / edit_pipeline_node never
reached the session chat and the model fell back to write_script (whose draft
never appears on the canvas). Use runtime.manager directly instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(sessions): scope list-page preview refresh to the page each tool changes
The scoped-refresh pass reloaded every open list-page preview tab on any
workspace mutation (reloadPages: boolean), so creating a schedule also
refreshed the Resources / Variables tabs.
Replace the blanket flag with the specific page paths each tool can change:
write_schedule → /schedules, write_resource → /resources, write_variable →
/variables, create_folder → /folders, write_trigger → the trigger kind's page;
delete/deploy/discard/rebase map their `type` to its page (none for
script/flow/app). Item-editor writes now reload no pages — their live editor
self-syncs. reloadTabs refreshes a list-page tab only when its own path is in
the touched set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(sessions): drop the inert item-reload path; extract a tested previewReload module
Post multi-target, every editable item is a live editor whose reload() no-ops,
and the one iframe item kind (legacy drag-drop apps) is never emitted as a
scope — so the whole `scopes` half of the preview-reload machinery could never
fire. Remove it (PreviewKind, PreviewScope, scopeKey, itemTypeToPreviewKind,
pendingScopes, and the item-route branch of reloadTabs); the `pages` path
already covers every real reload.
Lift the surviving pure logic out of the 900-line route component into
previewReload.ts — toolReloadEffect(name,args) -> {pages} and a new
tabsToReload(tabs,pages) mirroring selectPreviewTabsToClose — and cover it with
previewReload.test.ts (per-tool page mapping, item kinds reload nothing, the
unknown/local-tool silent-stale guard, loc-over-url matching).
Also clear session.target leftovers: delete the unread EDITOR_TARGET_KINDS
export and rewrite five comments that still described the removed single-target
pane / target-record write.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(sessions): state the preview-reload self-sync invariant once
Consolidate the "live editors self-sync, only list pages reload" rationale
to previewReload.ts and drop the drafting-history phrasings the review
flagged: the update_user_instructions incident and the "(not the runtime)"
contrast in sessionDraftCodecs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(sessions): follow the editor cell when a live tab retargets
Address PR review findings on the multi-target preview.
P1 (Codex) — draft sync stayed bound to the old cell after an in-place tab
retarget. useUserDraftSync captured `codec` once, but navigate() re-points a
live editor tab (script/flow/raw_app) to another item without remounting, so
path/workspace/ready followed the new item while the codec still read/wrote the
previous cell's store — cross-writing drafts. Make `codec` a reactive getter
like the hook's other inputs; SessionEditorTarget rebuilds it per path.
P2 (Claude) — navigate() now enforces the single-pipeline-tab invariant that
open() does: retargeting to a /pipeline/<folder> route focuses and re-points the
existing pipeline tab instead of turning the active tab into a second editor
racing the shared pipelineEditorState.
P2 (Claude) — the deploy-in-session handler peeked an editor slot via the
create-on-miss cell accessors, allocating an empty cell for items with no open
tab. Add a non-creating runtime.loadedEditorPath(kind, path) and use it.
P2 (Claude) — correct a SessionPicker comment left stale by the session.target
removal (the preview no longer seeds from a target).
Tests: two navigate() pipeline-invariant cases. npm run check 0 errors; 167
session unit tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: constrain script/flow/raw-app editors to container height in session preview
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: clip session preview picker dropdown to popover so it stops overflowing the page
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: universal styled markdown code blocks with copy button and subtle scrollbar
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(sessions): use the shared DraggableTabs for the preview tab strip
The session preview tabs were bespoke markup; converge them onto the same
DraggableTabs component the raw-app editor uses, gaining drag-reorder and
keyboard nav. The active tab keeps its breadcrumb/router picker via a new
tabAccessory snippet, and tabs persist their new order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(sessions): keep the new-tab + button right after the last tab
Add an afterTabs snippet to DraggableTabs that renders inside the scroll row
after the tabs (unlike trailing, which stays pinned outside it), and move the
session preview "+" there so it sits next to the last tab.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(tabs): use the subtle ScrollableX scrollbar for Tabs/TabsV2 headers
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(sessions): use bg-surface for the preview tab strip
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(tabs): add subtle shadow-sm to the selected DraggableTabs tab
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(tabs): drop selected-tab shadow; session strip bg-surface-secondary/50
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(sessions): drop persistent bg on preview bar buttons, hover-only
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(scrollbar): share a .scrollbar-subtle utility across tabs and chat
Extract ScrollableX's hover-revealed scrollbar styling into a global
.scrollbar-subtle utility (both axes, size via --wm-scrollbar-size), have
ScrollableX consume it, and apply it to the AI chat message list so the chat
scrollbar matches the tabs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: address review — scope HighlightCode copy button, plaintext unknown fences, Tailwind ScrollableX
- HighlightCode: keep the subtle CopyButton + surface chip behind buttonsOnHover
so the ~20 non-markdown callers keep the original light copy Button.
- MarkdownCodeBlock: unlabeled/unknown fences render as plaintext instead of
being mis-colored as TypeScript; added common language aliases (ts/js/py/...)
so real languages still highlight.
- ScrollableX: replace the custom <style> block with Tailwind overflow classes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style: make chat and session-sidebar typing dots slightly smaller
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: address auto-review — powershell fence to plaintext + reorder tests
- MarkdownCodeBlock: drop 'powershell' from the sql group so it renders
plaintext instead of SQL-colored (no powershell highlighter in the map).
- sessionPreviewTabs.test.ts: cover reorder (reorders+persists, ignores
unknown ids / keeps omitted at end, no-op when unchanged).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: keep scrollbar-hidden on Tabs row as TroubleshootFlowTutorial selector hook
codex-review: removing scrollbar-hidden broke the tutorial's '.border-b.flex
.flex-row.whitespace-nowrap.scrollbar-hidden.mx-auto' selector. The class is
inert on the non-scrolling row (ScrollableX owns the scroll) but is kept as the
tutorial's stable hook.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: preserve raw <pre> content in MarkdownCodeBlock (codex-review)
As the universal pre renderer, MarkdownCodeBlock also handles sanitized raw
HTML <pre>text</pre> from rehypeRaw, where the text is a direct child of <pre>
(no <code>). Fall back to that text child so raw pre content isn't dropped to
an empty block. Kitchen-sink sample gains a raw <pre> case.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CE: the auto-pull and fork-PR toggles are disabled with an EE badge, and
new sync repos only default them on when licensed (basic git sync is
available on CE since #8493, but auto-pull is EE and the backend rejects it)
- The pull modal passes clone_ref for wm-fork- forks (wm-fork/<tracked>/<id>)
so a manual pull fetches the fork branch instead of the tracked branch head
- PR-on-deploy skips no-op pushes: when the push script reports pushed=false
(e.g. the deploy was caused by an auto-pull), the completion hook no longer
ensures a PR, so closed PRs aren't recreated by the sync loop
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(ai-chat): background jobs tray with detach, approval and preview
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai-chat): route exec_datatable_sql through the jobs tray
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai-chat): jobs tray — orange queued badge, 5-recent pagination, drop remove button
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(sessions): silence dev-only false-positive binding warnings
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(sessions): silence dev-only false-positive binding warning in FlowEditorView
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai-chat): auto-expand jobs tray on approval, close modal on resume
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai-chat): let the AI set a per-call inline wait before jobs detach
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai-chat): auto-resume the chat when a background job finishes while idle
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai-chat): merge jobs tray and edits bar into a segmented session bar
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai-chat): address review — canceled-job handling, cross-chat poll guard, tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai-chat): gray chip dot for canceled-only jobs instead of green
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai-chat): keep jobs segment right-aligned when there are no edits
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai-chat): address /review — drain snapshot, live region, a11y, leading-ellipsis, remove dev harness
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai-chat): announce all same-tick job completions; drop redundant aria-live
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai-chat): guard poller re-entrancy; datatable error fallback (auto-review P2/nit)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai-chat): honor tool formatter on detached job completion; coalesce poller
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai-chat): persist tool result formatter so rehydrated detached jobs keep contract
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- The PR diff completion hook maintains one managed comment on the PR
(Cloudflare deploy-preview style: workspace, status, commit, collapsible
change list), upserted per synchronize via a hidden marker. The check run
stays for required-check gating.
- A settings difference in the diff summary is worded by cause: the PR
changes wmill.yaml, vs pre-existing drift between the repo's wmill.yaml
and the workspace, vs undetermined (neutral wording).
- Deploy-status check titles name the target workspace ("Deployed 2
change(s) to staging"), since GitHub shows a head commit's checks on any
PR containing it and a bare "Deployed" read as if the PR had deployed.
- Token-based repos see a hint pointing at the open-pr-on-commit /
open-pr-on-fork-commit workflows where the app-only PR toggles would be;
an API-set toggle on a non-app repo now logs a warning naming the
fallback; the design doc lists app-only features and their degradation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
The runtime image bundles several Go CLIs whose pinned versions were built
with an outdated Go toolchain (go1.21.7 for kubectl/helm), which image
scanners flag for fixable Go stdlib CVEs. Bump each to the latest release
built on the current patched Go 1.26.4:
- kubectl 1.28.7 (EOL) -> 1.36.2 (latest stable)
- helm 3.14.3 -> 3.21.2 (latest v3; staying on v3 to avoid the Helm 4
breaking changes for a bundled CLI users depend on)
- crane v0.20.6 -> v0.21.7
crane is also updated in DockerfileSlim/DockerfileSlimEe (the slim images
don't bundle kubectl/helm). The docker client comes from the floating
docker:29-dind tag, which already rebuilds to a current Go toolchain.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ai-evals CI job's `npm ci` (frontend) failed with:
npm error `npm ci` can only install packages when your package.json
and package-lock.json are in sync.
Missing: picomatch@4.0.5 from lock file
`picomatch` is a floating transitive: svelte-check pulls it as an
`optional peer` at `^4.0.4`, and vite/vitest/tinyglobby at `^4.0.x`. The
lock pinned 4.0.3/4.0.4, but 4.0.5 was published upstream. On a cold-cache
CI runner npm re-resolves those ranges against the registry and picks the
latest (4.0.5), which isn't in the lock — so `npm ci`'s sync check fails.
It passes locally only because a warm npm cache still serves 4.0.4.
Fix: `npm update picomatch --package-lock-only` (npm 10.9.8, matching CI's
node 22) to refresh every picomatch node to 4.0.5 (and the 2.x line to
2.3.2). Lockfile-only, all semver-patch; no package.json change. Verified
`npm ci --dry-run` is back in sync with a cold cache.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(pipelines): live materialize/dataset edits reflect on the graph; no phantom draft after deploy
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(pipelines): Save all deploys the open pane's live buffer, not the stale draft snapshot
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(pipelines): pin deployedFromPane to the shipped content so mid-deploy keystrokes still promote to a draft
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(pipelines): draft rename ping-pong loop, stale rename deploys, inactive-draft input lineage
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(pipelines): dedupe inferred-lineage overlay against accumulated edges; first draft teardown still captures reads
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(pipelines): record an authoritative empty read capture on uncaptured draft entries
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(pipelines): teardown skip compares lineage too, so access-only overrides still persist
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* style(pipelines): compress persist-back guard comments to the invariant
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(ai): flow writer builds approval steps as scripts with getResumeUrls, not identity
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(ai-evals): accept rawscript or script for approval step type
The flow-writer prompt allows an approval step to be `type: rawscript`
or `type: script`, but the topLevelStepTypes check pinned an exact
`rawscript` match, so a valid `type: script` approval would fail
deterministically. Let the check accept a list of allowed types.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* perf(sessions): don't mount preview tabs when side panel is collapsed
* fix(sessions): cap metadata max_tokens so Anthropic auto-rename works
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(sessions): drop redundant -fork suffix from auto-generated fork names
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(sessions): default 'also delete forked workspace' to false
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(copilot): apply metadata max_tokens cap on the OpenAI Responses path
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hover-revealed copy button in the workspace picker sat flush against
the menu's right edge on fork-less rows, because only forked rows render
an expand chevron that insets the copy button. Reserve the chevron's slot
on fork-less rows so copy buttons align across rows and keep right padding.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(copilot): open runs/schedules pages in session preview tabs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(copilot): drop buggy in-place nav, always chip outside a session
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(copilot): open_page covers variables/resources/assets/audit-logs/settings, perm-gated
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(copilot): open_page adds folders, groups and all trigger kinds (EE-gated)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(copilot): close_page tool to close session preview tabs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(copilot): fail-closed on unavailable trigger_kind in open_page handler
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(copilot): gate open_page on operator_settings, keep open_preview mention preview-only
Gate the open_page page set on the workspace operator_settings for operators
(mirrors OperatorMenu) instead of hardcoding runs/assets, with an empty-enum
guard. Also move the open_preview cross-reference out of the always-on prompt
line into the preview-gated block so it isn't advertised when preview tools
are off.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(copilot): gate open_page on the session's operating workspace
A session chat targets its own (possibly forked) workspace while $workspaceStore
stays on the navigation workspace, so operator_settings must be read for the
operating workspace, not the global store. Thread it through GlobalToolHelpers
so both setSchema (advertised enum) and the handler guard gate on the same
workspace; the global side-panel chat still follows the live store.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>