mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-29 16:17:39 +09:00
docs
23
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
13eb7527ef |
fix: route deprecated Webhook Forms through Form backend (#6553)
Deprecated Webhook Forms reuse the existing Form execution pipeline without teaching the modern backend about WebhookForm. ## User-facing behavior and how to test it - Existing saved Webhook Forms keep their markup, action settings, and success/error feedback. Submit a published Webhook Form and check both outcomes. - URL and Resource actions preserve configured methods, headers, dependencies, body overrides, and the last submitted value for repeated field names. Test a configured action and inspect the received request. - Default owner notifications use the Email Worker, including submitted files. Submit a Webhook Form without a custom action and inspect the owner email and attachment. - Configured Email actions validate recipients and retain threading metadata. Test valid recipients and a rejected recipient limit. ## Implementation - One deprecated Webhook Form adapter calls existing bot validation, Email preparation, recipient validation, resource loading, and response handling. - Both generated route templates call that adapter. Both paths reuse an internal resource-graph traversal helper; modern submission behavior is unchanged. - Root actions use the protected, uncached request override. Dependencies reuse existing generated Assets and Resource caching behavior. - Keep reachable-resource filtering because Email preparation runs before loading: unrelated Email settings must not break the selected action. - Remove dead `getDefaultActionResource` wiring from generated templates. ## Verification - Focused SDK tests: 116 passed (11 deprecated Webhook Form adapter and 105 modern submission, Email, and handler tests), including unrelated resources, missing dependencies, cycles, Email preflight/threading, and single-attempt Email failures. - Resource generator tests: 26 passed. - Full CLI prebuild suite: 139 passed after tracked fixture regeneration; focused deprecated Webhook Form adapter tests: 9 passed. - Repository fixture pipeline (`pnpm -r fixtures:build`) passed. All tracked generated `getDefaultActionResource` references were removed, and no Builder `N8N_FORM_EMAIL_HOOK` reference remains. - Final MCP-generated local Preview E2E rerun: 3/3 passed. - Focused saved Webhook Form custom-action E2E rerun: 2/2 passed (setup and test), including a single POST through the protected test receiver after Builder reload. - SDK, CLI, and Builder typechecks, focused lint and formatting, and `git diff --check` passed. - Deprecated component picker regression test passed (2/2); verified it fails when WebhookForm deprecation is disabled, then passes with deprecation restored. - Independent Astra review found no blocker, including the 62 generated fixture changes. No production deployment or live secret change was performed. - Required staging CI checks passed on previous head `15635fc` (Main workflow, visual regression, submodules, and lint). An unrelated existing MCP success-redirect E2E assertion failed once in shard 5, passed in a focused local rerun (2/2 including setup), then shard 5 passed on GitHub rerun without code changes. - Terminology pass (`4a69ab573`): renamed the dedicated SDK adapter and migration helper, updated both route templates and generated fixtures, corrected the active identity migration marker comments, and synchronized docs and test names. Focused tests passed: SDK 35, CLI prebuild 12 plus 2 after final title change, MCP 7, migrations 8, React browser 7, CLI docs 9. Seven affected package typechecks, fixture builds, formatting, and `git diff --check` passed. That historical head was superseded; latest-head CI results are recorded below. - ResourceForm identity migration (`29a21edbb`): focused migration (23), DB patch (25), project-build DB/session (93), Builder API/import/auth/patch (60), React SDK (162), runtime/MCP (239), registry (11), CLI evaluation (90), CLI session/sync (55), and protocol (15) tests passed; full CLI prebuild (139) passed. Affected typechecks, lint, formatting, and `git diff --check` passed. Independent Astra review found no blocker. No deployment was performed. - Prepublish Webhook Form warning (`25855e8f00`): audit checks every non-draft published page and links nested/shared instances to the correct page; modern ResourceForm, native forms, draft-only forms, and orphans are excluded. Focused audit tests (13), Builder navigation tests (6), project-build and Builder typechecks, lint, formatting, and `git diff --check` passed. Independent Astra review found no blocker; publishing remains nonblocking. No deployment was performed. - ResourceForm test and CI expectation repair (`e1a527fbcc`): focused React SDK (24), Builder preview/registry (38), project-build pages (1), and CLI fixture API (8) tests passed; Remix and React Router targeted browser suites, affected package typechecks, full formatting (3375 files), lint, and `git diff --check` passed. Astra review found no blocker. Persisted identity and runtime exports were unchanged. - Project bundle fixture repair (`70889ef27d`): eight bundles now carry the current contract version; the two unmarked historical Form instances in `webstudio-features` normalize to WebhookForm. Protocol fixture tests (17), protocol typecheck, CLI fixture build, formatting, lint, and diff check passed. Independent Astra review found no blocker. - Story/MCP E2E CI repair (`483614e6c5`): SDK CLI story tests (7), ResourceForm fragment tests (34), and focused local managed-form E2E (4 including setup) passed. SDK CLI and Builder typechecks, lint, formatting, and diff check passed. Astra review found no blocker. The initial local Preview build used an ignored stale React Router `lib` artifact; rebuilding it restored the passing E2E run. - Latest PR head `483614e6c5`: Main workflow [run 39317777032](https://github.com/webstudio-is/webstudio/actions/runs/39317777032) passed, including all package matrices and six Builder E2E shards. Visual regression [run 39317796922](https://github.com/webstudio-is/webstudio/actions/runs/39317796922), submodules, and PR lint checks passed. The overlapping unapproved visual run was cancelled by the approved rerun. - Shared bot-value generator (`ecc4cad1e9`): SDK helper tests (3), React Router WebhookForm/ResourceForm browser tests (5), SDK/Router/Remix typechecks and builds, SDK browser-boundary check, formatting, and diff check passed. Brave bypasses media queries, working media queries produce a fresh timestamp per submission, and failed queries produce `jsdom`; each managed/legacy submission has one bot field. Independent Astra review found no blocker. Latest-head Main workflow [run 39325677117](https://github.com/webstudio-is/webstudio/actions/runs/39325677117) passed, including all six Builder E2E shards; approved visual regression [run 39325702835](https://github.com/webstudio-is/webstudio/actions/runs/39325702835), submodules, and PR lint passed. The superseded visual run was cancelled by the approved rerun.\n\n- Historical Form clipboard guard (`e864283d1c`): instance/page transfer parsers reject ambiguous legacy `Form` before asset import; direct fragment/page/folder mutations preflight before changes. Canonical ResourceForm, WebhookForm, and NativeForm remain accepted. Project-build tests (89), Builder paste browser tests (69), both typechecks, focused lint/formatting, and diff check passed. Independent Astra review found no blocker. Latest-head Main workflow [run 39329184890](https://github.com/webstudio-is/webstudio/actions/runs/39329184890) passed, including all six Builder E2E shards; approved visual regression [run 39329562692](https://github.com/webstudio-is/webstudio/actions/runs/39329562692), submodules, and PR lint passed. The earlier unapproved visual run reported two expected story differences.\n\n## Compatibility notes - Root HTTP actions reject private and same-origin destinations. Dependencies retain their existing fetch/cache behavior. - Routes retain the 25 MiB request limit. Shared execution applies bot validation, a 10-second Resource timeout, and one retry for eligible HTTP failures; an ambiguous failure can duplicate an HTTP action. Email actions are not retried. - Default email uses the Email Worker's sender, plain text, recipient limits, and attachment limits instead of the legacy n8n HTML/basic-auth path. - Self-hosted sites need normal Email sender configuration. Direct Builder draft Preview handling is outside this published-route change. - Coordinate this CLI release with [SaaS PR #669](https://github.com/webstudio-is/webstudio-saas/pull/669), which removes the n8n helper and `N8N_FORM_EMAIL_HOOK` from new published-site builds. Publish with a CLI artifact containing this adapter. Existing deployed workers retain their bundled code and injected hook until republished, so keep the actual n8n endpoint available while those sites remain active. ## Implementation checklist - [x] Align saved Webhook Form custom-action E2E with the protected receiver and verify one submission. - [x] Regenerate tracked published-site fixtures with the updated CLI and confirm no `getDefaultActionResource` or `N8N_FORM_EMAIL_HOOK` references remain in Builder source/config/fixtures. - [x] Review authoritative `docs/` impact of removing the obsolete Builder hook wiring; related deprecation and integration docs were updated. - [x] Route deprecated Webhook Form submissions through the shared execution pipeline with one adapter. - [x] Preserve configured actions, dependencies, repeated fields, body overrides, and saved feedback. - [x] Prepare and validate Email actions using existing shared logic. - [x] Remove duplicated orchestration and dead generated context wiring. - [x] Verify generated routes and MCP-generated Preview submissions. - [x] Mark the Webhook Form component and its dedicated dependencies visibly deprecated, while leaving shared Form infrastructure active. - [x] Group Webhook Form-only SDK code for straightforward removal while preserving public APIs and generated output. - [x] Use deprecated terminology consistently for Webhook Form-only PR code, and clarify the active identity migration marker. - [x] Confirm required staging CI checks. - [x] Use ResourceForm as the one active modern component identity across Builder, SDK, runtime, templates, generated code, and docs. - [x] Migrate historical Form, NativeForm, ResourceForm, and WebhookForm identities in one idempotent pass using the historical pages marker. - [x] Remove the marker from ordinary project and snapshot APIs while preserving historical input readers. - [x] Persist pages and instances together when a historical marked project is saved, including a pages-only edit. - [x] Verify migration, export/reimport, old CLI and server snapshots, page-only save, generation, and affected typechecks. - [x] Complete an independent architecture review before commit or push. - [x] Warn at prepublish for deprecated Webhook Forms on published pages, with a link to each affected element. - [x] Verify modern ResourceForm is excluded and nested/shared Webhook Forms navigate to the correct page. - [x] Complete independent review, focused tests, and affected typechecks before push. - [x] Remove local `ResourceForm as Form` aliases from React SDK tests and fixtures; use the canonical component name throughout. - [x] Fix stale identity expectations found in CI and verify affected tests/typechecks. - [x] Complete independent review before push. - [x] Refresh tracked project bundle fixtures to the current bundle contract and canonical WebhookForm identity; verify protocol and affected CLI flows. - [x] Align SDK CLI story assertions and MCP E2E fragment fixtures with the persisted ResourceForm component name; rerun affected checks. - [x] Share one browser bot-value generator across ResourceForm submission and deprecated Webhook Form wrappers, preserving field ownership and behavior. - [x] Verify bot timestamps, Brave/media failure handling, repeated submissions, affected typechecks, and independent review before push. - [x] Reject ambiguous historical `Form` clipboard fragments before any insertion or transfer mutation, with clear recopy guidance; preserve canonical ResourceForm action remapping.\n- [x] Verify instance/page/template/folder paste regressions, independent review, and latest-head CI.\n\n - [x] Explain Email attachment size limits in the Email action UI and give a useful error when the aggregate encoded attachments exceed the local guard. - [x] Check Preview and published behavior, boundary tests, and authoritative Form docs; verify the focused change and get independent review. Ref #4093 - [x] Preserve safe email codes/messages through MCP, including local attachment and upload limit errors. - [x] Correct Form documentation for each enforced limit and verify Preview/published/MCP paths. - [x] Verify Builder SDK and MCP focused tests, typechecks, and independent review before push. - Email limits and failure copy (`6d215f3ec3`): the shared SDK checks 32 attachments and 7 MiB aggregate Base64 data before dispatch; the 25 MiB multipart response and Email action guidance use approved copy. Managed Email error codes pass through Form and MCP results with resource IDs, while MCP still omits resource bodies. Form docs distinguish the Worker 7 MiB JSON cap from Cloudflare's 5 MiB complete message and verified-destination exception, and correct the no-retry behavior. Focused SDK tests 89, Builder API tests 44, UI browser tests 42, SDK/Builder/CLI/docs typechecks, lint, formatting, and generated MCP docs check passed. Astra found no blocker. No real email or production deployment occurred. - Latest Email limits head `6d215f3ec3`: Main workflow [run 39393037290](https://github.com/webstudio-is/webstudio/actions/runs/39393037290) passed on rerun, including all six Builder E2E shards; approved visual regression [run 39393456618](https://github.com/webstudio-is/webstudio/actions/runs/39393456618), submodules, and PR lint passed. The initial unapproved visual run showed the same two previously accepted story differences. - [x] Address review: normalize previous-Builder `Form` instance patches before persistence, preserve historical WebhookForm identity, and cover marked/unmarked cases. Documentation impact reviewed: this restores intended save behavior, so no user guide change is needed. - Review finding follow-up (`ce05dd733a`): three Form identity patch regressions passed red/green; full project build-patch tests 28/28, project typecheck, lint, formatting, and diff check passed. Independent Astra review found no blocker. Incoming PATCH `Form` is treated as the immediately previous Builder's modern form alias; already stored unmarked historical `Form` continues to migrate to WebhookForm. The old patch protocol has no client-release identifier, so pre-October-14 live clients adding new ambiguous IDs cannot be identified separately. - Latest review-fix head `ce05dd733a`: Main workflow [run 39397337356](https://github.com/webstudio-is/webstudio/actions/runs/39397337356) passed, including six E2E shards after a targeted shard-4 rerun for an unrelated MDX slash-menu timeout. Approved visual regression [run 39397395206](https://github.com/webstudio-is/webstudio/actions/runs/39397395206), submodules, and PR lint passed. The unapproved synchronize visual run was cancelled by the approved rerun. |
||
|
|
33f7b350bc |
fix: Improve Form setup and email settings guidance (#6536)
## User-facing features and how to test them - Project Settings → Emails → Reply to has an info tooltip explaining how its display name is used in visitor confirmation subjects and messages, and when “Site Owner” is shown. Test it by hovering or focusing the info icon. - When a Form has no resources available for its Action, clicking the Action + button explains that a resource must first be created in Variables. Test it with a Form that has no action-capable resources, then create a resource and verify it can be added to the Action. - Success redirect is shown inside the Action control only when Resource actions are connected. Test by switching between navigation mode and a Form with a Resource action; the redirect field should appear only for Resource actions, and its saved value should return when a Resource action is added again. - The Action control no longer offers Disable/Enable. Connected Resource actions run in their configured order. The Form Action model has no `enabled` field; older saved JSON with that extra key is read as an ordinary connected action. ## Verification - Focused UI test for the Reply to tooltip. - Focused UI test for the empty Action resource tooltip. - Manually verify Success redirect visibility and value retention while switching modes. - Manually verify all connected Actions execute in order, including older saved Actions carrying an ignored `enabled` key. ## Review follow-up - Address all current open code review comments on this PR, verify the fixes, and reply to each addressed thread before resolving it. ## CI repair follow-up - Inspect the latest failing checks on this PR, fix the causes on the existing branch, and verify the affected checks locally and in CI. ## Follow-up audit (2026-10-14) - Audited head `8fb0b44`, all inline review threads and the PR conversation. The earlier Reply to tooltip comment was answered and resolved. Two P1 comments remained: required boolean `enabled` validation in CLI evaluations, and execution of saved disabled actions. - `builder-e2e (shard-6)` failed because Preview returned `Disabled` between `First` and `Last` in the configured failure results test. The failure matches the security review finding. `builder-e2e (shard-2)` was still in progress at audit start; the staging deployment workflow was cancelled. - The follow-up filters saved disabled actions from execution and restores boolean `enabled` validation in both CLI evaluations. Builder labels saved disabled actions. Focused SDK (55), CLI (222), and Builder (45) tests, plus all three affected package typechecks, passed locally. - Pushed `7c2b90e082`; replied to both P1 review threads, then resolved them. Its main CI run passed all 25 test/check jobs, including all six Builder E2E shards and the previously failing shard 6. - A later P2 review identified stale CLI/MCP guidance. Commit `160a9d1707` corrects the source and generated docs; focused docs tests (12) and CLI typecheck pass locally. Replied to and resolved that thread. On this latest head, the main CI run completed successfully: 27 check jobs passed, including all six Builder E2E shards; one visual-regression job was skipped. All three required checks passed. The automatically triggered staging deployment workflow was cancelled under the no-deployment instruction. ## Input type default follow-up - Show `text` as selected in the Props panel for both the HTML `input` element and the Input component when their `type` prop is unset, matching their rendered default. Verify both variants in the Builder and retain explicit type choices. ## Current review-thread follow-up (2026-10-14) - Recheck every unresolved review thread against the current PR head, make and validate any required fixes, and reply with the resolution before closing each thread. Keep this work on the existing PR branch; do not deploy. ## Input type metadata follow-up (2026-10-14) - Move the unset input `type` display default into component/element property metadata for both the Input component and native HTML `input` element. Remove the Props-panel special case, keep the implicit default out of saved props, and cover both variants with regression tests. - Run focused checks, push the fix to this PR, and cancel any automatically triggered staging deployment workflow. Do not deploy manually. ## Props metadata selector simplification follow-up (2026-10-15) - Refactor `$selectedInstancePropsMetas` after the input default metadata change. Preserve the current metadata, ordering, and attribute override behavior while simplifying the selector's structure and abstraction boundaries. - Run focused checks, commit and push to this PR branch. Cancel any automatically triggered staging deployment; do not deploy manually. ## Latest review follow-up (2026-10-15) - Audit the latest PR head, all unresolved inline review threads, and general PR comments. Address actionable feedback on this branch, run focused validation, then reply and resolve each addressed thread. Cancel any automatically triggered staging deployment; do not deploy manually. ## Form Action model decision (2026-10-15) - Remove the Form Action `enabled` field completely from the type, serialization, validation, execution, fixtures, and current docs. Accept older saved JSON containing that extra key by ignoring it, including `enabled: false`; action presence and valid IDs determine execution. - Update the CLI evaluation to assert intended action IDs and order without using `enabled`. Address the latest P2 review finding against this model. Run focused checks, push on this PR branch, and cancel any automatic staging deployment. No manual deployment. ## Action control visual follow-up (2026-10-15) - Match the Form Action plus button to the established Variables section plus button style and implementation where appropriate. Keep the existing icon. - Give the Action resource dropdown a stable width comparable to the Variables resource dropdown, including when available resource names are short. - Add focused UI coverage, validate locally, commit and push to this PR branch, and cancel any automatic staging deployment. Do not deploy manually. ## Action plus hover follow-up (2026-10-15) - Match the Action `+` button's hover and focus styling exactly to the plus buttons in panels such as Variables. Compare their actual implementations, preserve the existing icon, and add focused UI coverage where appropriate. - Run focused checks, commit and push to this PR branch. Cancel and confirm any automatically triggered staging workflow; do not deploy manually. ## Current CI fix (2026-10-15) - Inspect the latest check rollup and failing job logs on the current PR head. Fix all current failures on this branch, run focused local validation, push the changes, and monitor the new head until checks complete. - Cancel and confirm any automatically triggered staging deployment. Do not deploy manually. |
||
|
|
59d4f00ce7 |
feat: new Form component, all features and improvements you dreamed of (#6485)
## User-facing features and how to test them In Components → Forms, insert **Form (Beta)**. The existing Webhook Form remains a separate component. - **Insert a ready-to-use contact Form.** In Components → Forms, insert **Form (Beta)**. It starts with accessible Name, Email, Subject, Message, and optional Attachment fields; sends submitted field values and uploaded files to the account holder; sends a customizable confirmation to the visitor without attaching the submitted file; and includes success and error feedback. The two Actions are named **Email to owner** and **Email to visitor**. *(#3023, #5499)* - **Use the contact Subject in both email subjects.** Owner and visitor subjects bind the submitted Subject input; the visitor subject also identifies the sender. Template and SDK tests pass. - **Format submitted Form data readably in Email bindings.** `formData.toString()` now renders labeled lines, nested values, and uploaded filenames in any Email Resource expression, while excluding passwords and internal Form fields. SDK and independent-bundle tests pass. Published staging delivery is verified in Batch 7. - **Choose Form actions.** Select the Form and open Properties & Attributes → Action. Add up to ten visible HTTP, GraphQL, or Email Resources; reorder, disable, or remove them. Submit and confirm enabled actions run together. *(#5499)* - **Send submitted fields and files.** Submit repeated fields and a file input to an HTTP Resource. Check the received multipart fields, order, filename, type, and bytes. Email Resources attach submitted files by default; turn off **Attach submitted files** to omit them. *(#3023, #5432)* - **Make the visitor confirmation friendly and signed.** Inherited content gets the approved friendly message and resolved sender name; custom text remains intact. Email Request preview and delivery use the same resolver. SDK and Builder preview tests cover defaults, custom overrides, blank content, sender identity, and preview parity. Suggested body: > Thank you for your message. > > We received it and will respond as soon as possible. > > Webstudio Team - **Identify the sender in the visitor confirmation.** The resolved sender name appears in the visitor subject and body. Focused tests pass. - **Include source page context in the owner email.** `browserInfo.pageUrl` is included in the owner message and the Email Request preview. Focused SDK and Builder tests pass. - **Use Form and visitor data.** In a Resource defined on the Form, bind `formData` and `browserInfo` in its URL, headers, or body. Local MCP Preview tests verify submitted fields, browser, and language. *(#5428)* - **Use page and query values.** On a dynamic page with query parameters, bind `system.params` and `system.search` in a Form Resource, then navigate and submit. A query key with one value is a string; repeated values are an ordered array. Confirm the current route values are available after navigation. Text bindings render repeated values as comma-joined strings. *(#5512, #3891)* - **Use Navigation mode for search and filters.** Remove every Resource from the new Form’s Action list. Its action becomes `?__webstudio__=navigate`, and submitting named fields updates the URL query and page Resources without a full page reload. Reconnect a Resource to return to Resource submission mode (`?__webstudio__=resources`). A plain Element with tag `form` keeps native browser behavior. - **Customize notification email.** In Project Settings → Emails, set recipients, sender, and Reply to. In each Email Resource, choose project recipients, custom addresses, or a visitor email input, and configure its Reply to, subject, body, and attachment setting. Verify a per-Resource override does not change another Form’s settings. *(#4414, #4385, #5658)* - **Choose Resource types from one ordered list.** In Variables → Type, find REST resource, GraphQL resource, Assets resource, Email resource, Sitemap resource, and Current date resource in that order. Confirm the labels and order in the Type dropdown. - **Inspect Resource requests and responses.** Open Variables → a Resource → Edit variable. Use **Load data** on Request when no request is shown; inspect HTTP request, response, and diagnostics. For Email, load the request preview and confirm it does not send an email. After submission, inspect saved HTTP and GraphQL exchanges without triggering another request. *(#4093 related; new Form Resource inspector)* - **Explain the Email Response before a submission.** Refresh builds the local request preview without sending an email; the Response tab tells users to submit the Form in Preview to see the Email Service response. Focused inspector test passes. - **Use action results in the Form.** Bind `formState`, `results`, and `errors` in the success and error content. Submit successful and failing actions and verify each result or error includes its action’s response details in configured order. *(#5499)* - **See submission feedback.** Submit a Form whose success or error message is outside the viewport and confirm that message scrolls into view. Repeat with feedback already visible, reduced motion, partial failure, and a configured success redirect; visible feedback should not move, and redirect should happen without scrolling first. *(#6487)* - **Redirect after success.** Set Success redirect and submit both successful and failing Forms. Only success follows the redirect. *(#3530)* - **Keep separate email threads.** Retries preserve a submission reference, while separate submissions appear in separate Gmail conversations; Batch 7 verifies both. *(#5018)* - **Keep existing Webhook Forms.** Insert the existing Webhook Form and verify its configured behavior remains intact. For its documented query-binding fixes, verify route params and repeated search values after navigation. *(#5512, #3891; #4093 and #5432 remain related legacy issues)* - **Keep native Form props out of the new Form editor:** The new Form metadata hides generated rendered-tag HTML props such as `method`, `encType`, `target`, and ARIA attributes, while its own Action, Success redirect, and visibility controls remain. The legacy Webhook Form behavior is unchanged. A Builder property-selector regression test verifies this. - **Allow up to 10 Form Actions:** The shared validator and Action picker now allow up to 10 Resource actions, and the Form control description reflects the limit. SDK tests accept 10 and reject 11; the generated CLI prebuild rejection coverage passes (130 tests). SDK and SDK Components typechecks, formatting, and diff checks pass. - **Make the default Form ready to use without edits:** The inserted contact Form has required Name, Email, Subject, and Message fields; its project Email Action resolves the current project contact address or account holder dynamically, with no personal data in the template; its visitor Email Action has a confirmation subject and message; success and error states give actionable feedback. `core-templates.test.tsx` verifies fields, routing, and copy; focused registry tests and typecheck pass. - **Make the default Form usable as a contact form:** Its Email Actions are named “Email to owner” and “Email to visitor.” The owner body binds the complete submitted `formData` object rather than manually listing fields, and an optional Attachment input sends its file only to the owner. Email Resource expressions render `formData.toString()` as readable labeled lines. The visitor receives a confirmation without submitted files. Registry/template tests and MCP E2E verify the inserted Form, owner message and attachment, visitor message, and Request preview. - **Format Form action errors clearly:** The default Form shows each action error as a spaced bullet inside a red-tinted alert panel. Registry template tests verify the alert and list structure; 8 template tests pass. - **Deprecate the existing Webhook Form before its next-PR removal:** The component metadata and compatibility exports in the React, React Router, and Remix packages are marked deprecated, and the guide says removal is planned. Keep form submission helpers still used by NativeForm active; remove legacy-only wrappers and template in the follow-up PR. ## MCP feature parity test plan Run these cases through Webstudio MCP on a disposable project. Create and configure the Form with MCP, save and read back the configuration, then test its generated Preview. Use only a controlled test mailbox for real email delivery. This plan mirrors the user-facing UI cases above. - Discover **Form (Beta)**, inspect its schema, insert the default contact Form, and read back its fields, Actions, success/error content, and absence of personal data. - Change the submitted Subject and Name. Verify owner and visitor subjects, visitor sender identity, friendly confirmation text, and the owner page URL. - Bind the complete `formData` object. Verify readable lines, nested values, filenames, and exclusion of password/internal fields. - Add HTTP, GraphQL, and Email Actions; reorder, disable, and remove them. Verify concurrent dispatch, result order, partial failure, and the 10/11 Action limit. - Submit repeated fields and one or multiple files to HTTP. Verify multipart order, filename, MIME type, and exact bytes. Verify Email attachments default on, then turn them off for the visitor Action and confirm the visitor receives no file. - Configure project email settings and Resource overrides. Save/reload each recipient mode, Sender, Reply to, subject, body, and attachment setting; verify overrides do not leak between Forms. - Bind `formData` and `browserInfo` from a Form-scoped Resource. Verify page URL, browser, language, and hosting-provided IP/referrer where the test environment supplies them. - Test dynamic route params and single/repeated query values before and after navigation. Verify Navigation mode updates the URL and bound page Resources without a full reload; reconnect an Action and verify Resource mode. Check native `form` Element behavior separately. - Verify Resource discovery order and method labels, then exercise HTTP/GraphQL Request, Response, Diagnostics, load/refresh, and saved exchange inspection. For Email, verify request preview sends nothing and the Response appears after actual submission. - Bind `formState`, `results`, and `errors`. Submit success, validation failure, partial Action failure, and full failure. Verify feedback content, visible/off-screen scrolling, reduced motion, and success-only redirect behavior. - Submit twice and verify separate email threads and stable retry identity, with no duplicate delivery after an ambiguous send. - Verify the existing Webhook Form remains editable and its route/query behavior remains intact, while new Form discovery/creation and native Form prop exposure match the documented component contract. ### Execution status and MCP limitation The `mcp-*` suite authors through a current-source MCP server and submits generated Preview Forms through `preview.submit-form`. Playwright covers Builder UI, navigation/rendering, and viewport assertions. The separate `forms.simulate-submission` operation submits synthetic values from the saved canvas Form directly to the shared SDK server Form engine, without starting Preview or a browser. Both paths use the same saved-draft preparation and SDK submission handler. Most automated email tests use a loopback receiver. The controlled branch-source MCP test below separately verifies delivery to a real test mailbox; it does not test the user’s connected MCP release. On 2026-10-14, the branch-source MCP was tested against a running local Builder and a copied disposable project. No npm package publish or connected-MCP release was needed for this local verification. - [x] Add a bounded `preview.submit-form` MCP operation for session-owned generated Preview Forms. It uses native form controls and validation, supports repeated values and bounded file uploads, returns state/results/errors/exchanges, and never replays an ambiguous send. The MCP-client E2E verifies one owner Email delivery and one HTTP delivery with ordered results against local receivers. Focused Vision, Project Build, CLI, and Builder E2E tests pass. - [x] Migrate browser Preview Form runtime cases to `preview.submit-form`; retain Playwright for Builder UI, navigation/rendering, and viewport assertions. The 52 MCP-named Builder E2E cases passed across the full 2-worker run (50/52) and a focused rerun of its two timed-out Email cases (2/2), including MCP submission and local receiver assertions. - [x] Add `forms.simulate-submission` to the current-source CLI MCP session. It reads the saved canvas Form, calls the Builder server route, and reuses the Preview Form engine and SDK submission handler. The older local CLI adapter rejected Email; the current server route supports it. Static Checkbox and multiple Select array names come from saved controls; dynamic array names may be supplied explicitly. Focused tests and independent Astra Medium review pass. - [x] Run the Luna Low `managed-form-runtime-v1` scenario against `forms.simulate-submission` through a real MCP client on the current source. `gpt-5.6-luna` at low reasoning passed 10/10 checks in 55 seconds. Three simulation calls produced success, an explicit 503 failure, and recovery against the controlled local HTTP receiver in the expected sequence. The agent used no Preview/browser submission or Email action. One `templates.get` MCP error was recovered before submission. - [x] Verify `forms.simulate-submission` from branch-source MCP against the local Builder server and a disposable saved Form. The supported local database was started, and `EMAIL_SERVICE_URL` plus `TRPC_SERVER_API_TOKEN` were supplied from the staging Infisical environment without printing values. Luna Low used MCP discovery, Form/resource inspection, `update-resource`, and simulation. Both Email Resources resolved only to `testing@webstudio.is`; simulation returned success, HTTP 200, two Email results with status 200, and no errors. Gmail independently confirmed both owner and visitor messages delivered to that controlled inbox at 15:46:51 UTC on 2026-10-14. The shared server route and CLI transport received independent Astra Medium review; focused CLI (164) and HTTP-client (45) tests, package typechecks, formatting, and diff checks pass. No npm release or production deployment was involved. - [x] Rename the new Form component label to **Form (Beta)** in the component picker and MCP discovery. Component IDs stay unchanged; focused registry, Builder, MCP, and CLI tests pass; Astra Medium reviewed the change. Pushed `592589ccfe`. ## MCP end-to-end verification ### Verified against this branch These checks use the current-source MCP session server to author disposable local Builder projects and `preview.submit-form` to submit generated-site Forms to local test receivers. Playwright verifies Builder UI, navigation/rendering, and viewport behavior. They verify this branch's code, not the connected MCP release, staging services, or real mailbox delivery. Current branch rechecks (2026-10-14): Project Build MCP tests 142/142, CLI MCP/evaluation harness tests 60/60, Vision CDP/Form tests 48/48, and the React Router Navigation browser test 4/4 pass. Builder, CLI, Project Build, Vision, and both React Router fixture typechecks pass; scoped lint, formatting, and diff checks pass. The earlier head passed PR CI; checks on commit `7438692ef5` are running. The separate full CLI prebuild suite now passes 131/131 after adding the required `statusText` to its private Email binding fixture. The main-merge CI failures in Project Build expression expectations, CLI Preview config import, formatting, and the SSG fixture size cap were fixed in `0272768260`; checks on that commit are pending. - [x] Verify all 52 MCP-named Builder E2E cases on the current head. The complete 2-worker run passed 50/52; two complex Email cases exceeded Playwright's 120-second per-test limit under concurrency, then both passed in a focused one-worker rerun (44.4s and 31.0s; 2/2). A serial full rerun reached 43/52 without assertion failures before the wrapper's 900-second command limit. An earlier full run passed 52/52 before the final Preview readiness fix. Coverage includes contact Forms, Email delivery and partial updates, attachments, readable `formData`, HTTP and GraphQL Actions, navigation and repeated query values, legacy Form editing, Resource inspectors, Form properties and type choices, feedback, and redirects. Full-run command: `E2E_TEST_COMMAND_TIMEOUT_SECONDS=1800 E2E_SKIP_BUILDER_BUILD=true E2E_INSTALL_PLAYWRIGHT=false pnpm --filter @webstudio-is/builder e2e -- 'mcp-' --project=chromium --workers=2`. - [x] Run the Builder Preview Form E2E suite serially: 5/5 passed, including authenticated Builder Preview submission, error rendering, redirects, submitted browser `FormData`, off-screen feedback scrolling, and Navigation mode without a Resource POST or canvas reload. - [x] Verify the local Email inspector server path. The route integration suite calls the real local `/rest/preview-form` handler and passes a controlled Email Service binding response through it; 28/28 tests pass. The Email Service boundary is stubbed, so this does not claim a staging Worker run. - [x] Verify the Form Action picker browser tests: 2/2 pass for add, disable, reorder, remove, the ten-Action limit, and rejection of an eleventh Action. The MCP runtime E2E separately verified ten deliveries and ordered results. - [x] Verify local Preview request context across two pages: scoped `formData`, `browserInfo.pageUrl`, trusted IP, user-agent, and language all remain associated with the correct Form Resource. - [x] Verify partial Email body updates through MCP. Custom and explicitly blank bodies preserve the other recipient, sender, and attachment settings; local Preview uses the requested body. Focused E2E passes 2/2. - [x] Verify the default Form template has no hardcoded email address, sender, or recipient, and the error Collection renders each message as a separated bullet in a red-tinted alert. Registry tests pass 11/11. - [x] Verify the Action picker UI adds, disables, reorders, and removes HTTP, GraphQL, and Email Resources; accepts 10 and blocks 11. Component browser tests pass 2/2. Runtime MCP E2Es separately verify Action order and concurrent dispatch; the picker test does not claim ten live deliveries. - [x] Verify the Luna Low managed-Form authoring evaluation: 20/20 checks pass. Validator tests pass 83/83. - [x] Verify the legacy Webhook Form is excluded from new MCP creation/copy paths while saved instances remain editable. Source tests and the existing 139 MCP checks pass; this does not mean the published MCP catalog has been refreshed. - [x] Formatting, `git diff --check`, and Builder, CLI, and SDK Components Registry typechecks pass. ## Runtime correctness - [x] Prevent automatic retry of an Email Action when the Email Service send may already have succeeded; report an ambiguous response without sending the same email again, and verify one Form submission cannot produce duplicate delivery. Pushed `07627e44fa`: Email Actions never automatically replay; HTTP retries remain. RPC and Preview accept legacy `{id}` and canonical replies, including successful canonical replies with an empty provider ID. Regression tests cover one dispatch after failures, timeouts, and malformed replies, response compatibility, and HTTP retry. All 143 focused SDK tests, SDK typecheck, scoped lint, diff checks, and independent Astra review pass. - [x] Keep ambiguous Email Action outcomes non-retriable in Builder so a possibly successful send is never replayed; preserve compatibility with both deployed legacy `{id}` and canonical `{ok, status, statusText, data}` Worker replies. Pushed `578a1a99`: canonical delivery IDs must be nonblank, while legacy replies remain supported. SDK regressions cover one dispatch after ambiguous RPC/Preview failures, timeouts, malformed replies, and empty IDs. ## New Form navigation mode and MCP E2E (2026-10-11) - [x] Give the new Form two submission modes using its valid action URL: `?__webstudio__=resources` when one or more Resources are connected and `?__webstudio__=navigate` when none are connected. Removing the last Resource switches to Navigation mode. Keep a plain `form` Element native and leave the legacy Webhook Form available but marked deprecated until the next release. Runtime tests cover both action markers; the MCP-authored browser E2E verifies navigation query updates and Resource revalidation. Legacy Webhook Form metadata, compatibility exports, component code, and docs now state its deprecation and support window. - [x] In the new Form’s Action control, when no Resources are connected, show centered “Navigation mode” with an info tooltip: “Submits the form fields as URL query parameters and navigates without a full page reload. Use this for search and filters that update page Resources from the URL.” - [x] Add MCP-authored end-to-end coverage for both modes and their distinctions. Luna/low authored Resource and Navigation Forms plus a native `form` Element through MCP, with separate registry searches for Form (Beta), Webhook Form, and Element; the final project passes the corrected structured evaluator. The MCP-authored Playwright E2E removes all Form Actions and verifies repeated and single query values update the URL and page Resource without a document reload or Resource POST. Existing local MCP Preview coverage verifies Resource submission, native GET behavior, legacy Webhook Form compatibility, and component discovery. ## Ports from the earlier implementation - [x] Resource selection and scoped `formData`/`browserInfo` access, parallel dispatch, protected egress, server destination limits, and bot-field validation are already in the new Form. - [x] Resource editor URL autofocus and known header/value suggestions are already reused. - [x] Centralize the `formData` and `browserInfo` parameter names for Builder, generator, and runtime checks. - [x] Default HTTP body forwarding and automatic multipart for uploads are now ported to the new Resource graph. - [x] Success/error state slots and success redirect are now ported into the new Form and Router/Remix adapters. - [x] Add response `results`/`errors` bindings and one retry per failed or timed-out destination. - [x] Port feedback scrolling to the new Form in Router and Remix adapters, with browser regression coverage. - [x] Adapt the existing bot-field protection for the new Form; saved Webhook Forms keep their prior bot-field behavior. The narrowly scoped route/query fixes for #5512 and #3891 are tracked separately below. - [x] Local new-Form bot checks: one fresh hidden value per submission, five-minute expiration, malformed/missing rejection before dispatch, Brave handling, and stripping internal form ID/bot fields before Resource delivery. Evidence: `packages/sdk/src/managed-form-submission.ts`, `managed-form-submission.test.ts`, `packages/sdk-components-react/src/form-submission.browser.test.ts`, and managed generated-route cases in `packages/cli/src/prebuild.test.ts`. ## Batch 1 — New Form foundation - [x] Make the existing Forms-section **Form** tile insert the new Form component while retaining the existing Webhook Form tile and behavior. - [x] Make the new Form a managed Resource submission component. With no destination, it shows a configuration error and cannot dispatch or navigate. Use Element with `tag=form` for ordinary browser form behavior. - [x] Exclude unrelated legacy-only runtime work from this feature. Existing component metadata is deprecated; the new Form has no legacy compatibility or migration contract. The targeted #5512/#3891 route/query fixes are tracked separately in Batch 2a. - [x] Verify the Resource-only component with focused render tests, package typecheck, and component story generation. - [x] Verify the Batch 1 new Form acceptance criteria in the Builder and browser. ### Acceptance criteria - Inserting the new Form creates the default contact Form with named inputs, a submit button, and two selected Email Resource Actions: one for project recipients and one for the visitor email field. If all destinations are removed, it shows a configuration error and cannot dispatch or navigate until a destination is configured. - Element with `tag=form` retains browser validation, method, encoding, action, and navigation. - Acceptance covers the new Form only. Apart from the explicitly tracked #5512/#3891 route/query binding fixes in Batch 2a, legacy compatibility, old native-mode settings, and old-component migration are outside the feature contract. **Verification:** Builder catalog and insertion tests; Chromium empty-Resource blocking test; generated Element `tag=form` Playwright test with a local receiver (`packages/sdk-components-react/src/element-form.test.tsx`, `40c6f972c8`, focused 1/1) proves invalid input sends nothing and valid native multipart POST navigates using the configured action, method, and encoding; generated published output for the new Form; relevant typechecks and lint. The user confirmed the test inbox received the test email attachment. Staging Builder Preview verified Email and Webhook delivery. The user confirmed the published staging route is live; the route returns HTTP 200 and includes a file input. Published delivery to both Webhooks and final Form-state behavior are verified in Batch 7 below. Live Production Form submissions are outside this PR’s verification scope. ## Batch 2 — Resource-only submission, scoped data, and Resource selection - [x] Give the new Form one submission path with one to ten selected Resource destinations. Keep Actions and Success redirect as its Form controls; plain HTML forms use Element with `tag=form`. An empty selection reports an error without dispatch or navigation; tests cover this path. - [x] Expose one submission-only `formData` object, keyed by HTML name. Repeated names are ordered lists; a known unchecked checkbox group is an empty list. Preserve files and hidden inputs. - [x] Expose safe `browserInfo`: trusted IP, user agent, language, and referrer. Never expose raw request headers, cookies, or authorization. - [x] Make explicit Resource expression bindings to a Form's `formData` and `browserInfo` available only to Resources defined within that Form; a selected HTTP Action with an unset or empty body implicitly receives submitted Form data regardless of Resource scope. - [x] Resource variables are created in **Variables** at their normal scope. The Actions selector lists only action-capable Resource variables visible in the Form's variable scope: defined on the Form or one of its ancestors. Exclude sibling, descendant, and unrelated scopes. Resources defined on the Form can bind its `formData` and `browserInfo`; ancestor Resources cannot bind values from a descendant Form. Carry forward URL autofocus and known header/value autocomplete. Enforce up to ten Actions per Form in the Builder and server. - [x] Keep **Actions** as a control inside **Properties & Attributes**, with a plus menu and selected Resource rows. The plus menu offers only existing action-capable HTTP, Email, and GraphQL Resource variables visible in the Form's current variable scope (defined on the Form or an ancestor); exclude System data variables and resources from sibling, descendant, or unrelated scopes. Resource variables are created in **Variables** at their normal scope; Actions only selects existing variables. Disable already-selected Resources in the menu. Selected rows expose enable, disable, and remove controls. A simple click on the Action name opens the existing Resource editor through selection/open state, leaves its Variables row visibly selected, and scrolls it into view, while preserving page and canvas selection; drag, enable/disable, and remove controls must not open it. The eye toggles enabled state; the minus removes the Action from this Form only. Preserve explicit selection, ordering, and the ten-Action limit. Published-site submission checks are recorded in Batch 7. - [x] Cover scope, expressions, repeated names, renames, files, copying, deletion, save/reload, and unavailable bindings with focused tests. - [x] Ensure `mapResourceExpressionsMutable` remaps Email `senderExpression` and `recipientsExpression` data-source IDs; `fragment.test.tsx` verifies copy/paste/fragment rebinding (focused regression 1/1, full file 34/34). Project-build typecheck and `git diff --check` pass. - [x] Add focused fragment coverage for Email `senderExpression` and `recipientsExpression` bound to variables inside a copied Form, asserting references remap to the cloned variable IDs (not only restoration of external parent bindings by name). `fragment.test.tsx` passes 34/34 and combined project-build data+fragment tests pass 165/165; assertions verify both expressions remap to cloned variable IDs distinct from the source IDs. - [x] Implement and locally test Batch 2a current-page parameters and repeated query values in managed and legacy Form Resource bindings (#5512, #3891). - [x] Add generated published-route loader integration coverage for a native GET Form with repeated query keys; repeated `system.search` values reach an expression-bound Resource in both `defaults` and `react-router` templates. Focused prebuild tests pass 2/2. - **Issue scope:** #5512 is fixed for legacy generated HTML Form actions and the new Form path; generated-route tests cover both route templates. #3891 exposes repeated values as ordered arrays through `system.search` while preserving strings for single values; SDK, Builder Preview, generated action, and generated native-GET loader regressions cover the behavior. - [x] Local #5512/#3891 implementation and regression: the new Form's generated action uses current `system.params` and `system.search`, where repeated values are ordered arrays; SDK search tests and the Builder native GET preview test preserve repeated values (`packages/cli/src/prebuild.test.ts`, `packages/sdk/src/system-search.test.ts`, `apps/builder/app/canvas/interceptor.test.ts`). - [x] Locally verify #5428: bind trusted `browserInfo` values into `X-Forwarded-For`, `User-Agent`, and `Accept-Language` Resource headers, inspect the generated action’s outbound request, and document the setup and trusted client-IP source for each hosting adapter without forwarding cookies or authorization. - [x] Local generated-route verification (`e9d5209882`): Remix and React Router send the three configured headers to a test destination; Cloudflare context uses `CF-Connecting-IP`, Node omits unavailable IP, and arbitrary forwarding, cookie, and authorization headers are not forwarded. Setup and hosting sources are documented. ### Form property inventory - [x] Expose only the Actions control and **Success redirect** as Form controls. The generic HTML attribute picker and native `id`, `class`, `action`, `method`, `target`, and `encType` controls are unavailable for this component. - [x] Rename the Form control label from **Actions** to singular **Action**. Staging verification for the Form property panel is recorded in Batch 7. ### Acceptance criteria - Action and Success redirect are the only Form controls. The component label `Form` is system metadata and is not editable as an HTML attribute; the Form has no `id`, `class`, `action`, `method`, `target`, or `encType` controls. - **Action** is a control inside **Properties & Attributes**. Its plus menu offers only existing action-capable HTTP, Email, and GraphQL Resource variables visible in the Form's current variable scope (defined on the Form or one of its ancestors). It excludes System data variables and Resources in sibling, descendant, or unrelated scopes. Resource variables are created in **Variables** at their normal scope; Actions only selects existing variables. Already-selected Resources are disabled in the menu. Selected Resources appear as rows with enable, disable, and remove controls, and can be reordered. Clicking an Action name opens the corresponding Resource’s existing Edit variable dialog and highlights its Variables row by setting the existing selection/open state directly, without synthetic clicks; preserve the current page and canvas selection. Drag, enable/disable, and remove controls do not open the editor. The ten-Action limit is enforced. Resources defined on the Form can bind that Form's `formData` and `browserInfo`; ancestor Resources are visible but cannot bind values from the descendant Form. An empty selection shows no canvas warning before submit; attempting to submit shows the configuration error, dispatches nothing, and does not navigate. The Form has no native or direct-URL submission mode. - The new Form supports Resource Actions only; do not add a legacy/native-mode compatibility path or migration requirement. - A Resource defined on the Form can bind that Form's `formData` and safe `browserInfo`; a visible ancestor Resource cannot access values from its descendant Form. Resource variables are created in Variables at their normal scope. The plus menu lists only existing action-capable Resources from the Form and ancestor scopes, excluding sibling, descendant, unrelated scopes, and System variables. Forms explicitly select each action; none are auto-added. Already-selected Resources are disabled in the menu. Bindings survive editing and save/reload. - On a `/products/:slug` page visited as `/products/chair?source=newsletter`, a submitted Resource can use `system.params.slug` as `chair` and `system.search.source` as `newsletter` in its URL, headers, or body. Values come from the current submission's page, including after navigation; missing parameters remain absent. - Repeated names retain form order, unchecked checkbox groups resolve to empty lists, and files stay file values. The Resource editor focuses URL and suggests known header names and values. ## Batch 2a — Keep page parameters and every selected query value (#5512, #3891) New Form Resource bindings must receive the current page's parameters, and a multiple-select GET form must not lose selections when a binding reads the URL. - [x] Extend #5512 route/query propagation to legacy native Form actions: generated routes pass route params and ordered query values while preserving the managed Form path. The regenerated runtime contract and 26 fixture routes are pushed in `8c4bda7791`; `check:generated-api`, fixture builds, and generated-route regressions pass. - [x] Implement route/query propagation in both managed and legacy Form Actions: populate `system.params` from the current route, preserve single-value `system.search` strings and expose repeated query values as ordered arrays through the same binding. Generated action and loader regressions cover both templates; the consolidated CLI prebuild suite and focused native-GET loader tests pass. - [x] Verify #5512 with the new Form's Resource bindings using `system.params` and `system.search`. Cover URL, header, and body bindings, dynamic pages, missing values, and submission after navigation in React Router and Remix. - [x] Reproduce #3891 with a native GET form and a multiple-select input. Keep the browser's standard repeated query keys; do not join values with commas or rewrite the submitted URL. - [x] Use `URLSearchParams` to preserve every value in order, including empty values, repeated identical values, and values containing commas or encoded characters. Keep an object-based binding interface so property autocomplete still works. - [x] Inspect existing `system.search` consumers and record the compatibility choice here. Keep existing single-value bindings working, expose all repeated values to bindings, and make any changed behavior for existing repeated-key bindings explicit. Do not silently discard values or break existing expressions. - [x] Apply the agreed representation consistently in the Builder, published page data, client navigation, and new Form action path. Update affected types, binding suggestions, generated code, and fixtures. - [x] Add passing SDK, Builder, and generated-route regressions for current page parameters and repeated query values in new Form bindings; document single-value and ordered multi-value query bindings with working examples. - [x] Local new-Form generated-route regressions cover current page parameters and repeated query values in URL, headers, and body after navigation. A red-before/green-after test proves configured body bindings survive alongside submitted fields; CLI binding examples document single and repeated query values. - [x] Record regression coverage for #5512/#3891 on new Form bindings: generated-route and Builder tests cover current-page parameters and ordered repeated, empty, comma, and encoded query values, with SDK coverage for scalar compatibility. **Compatibility choice:** `system.search[name]` is a string when the query key occurs once and an ordered string array when it occurs more than once. Existing single-value expressions are unchanged; text bindings stringify arrays as comma-separated values. The relevant consumers in Builder system/preview and generated page/action templates were reviewed. Focused SDK, Builder, and generated-route tests cover the behavior. ### Acceptance criteria - On a `/products/chair?source=newsletter` page, a new Form Resource can bind `system.params.slug` as `chair` and `system.search.source` as `newsletter` after navigation. Existing Webhook Form behavior is unchanged except for the explicitly tracked #5512/#3891 route/query binding fixes in Batch 2a. - Selecting two options in a native GET form leaves both values in the URL and makes both available to bindings in their original order. Single-value access still works, and property autocomplete remains available. - Empty values, identical repeated values, commas, encoded characters, reloads, and client navigation do not drop or corrupt query values. - Verify current-page and repeated-query bindings for the new Form and ordinary Element `tag=form` browser behavior. Legacy Webhook Form changes are limited to the explicitly tracked #5512/#3891 route/query fixes in Batch 2a; other legacy compatibility is not a closure condition. ## Batch 3 — HTTP Resource submission - [x] POST to each selected HTTP Resource. A selected Action whose Resource body is unset or empty implicitly receives all submitted fields regardless of Resource scope; an edited body can omit, rename, add, or compute fields. Explicit `formData` expression bindings remain limited to Resources defined within that Form. - [x] Use JSON for text-only data and switch to multipart when the resolved body contains a file. Set JSON content type unless configured otherwise; preserve repeated fields and file bytes, and remove stale `Content-Type` so fetch writes the multipart boundary. - [x] Add an explicit `auto`/JSON/multipart choice. Reject an incompatible body before that destination's fetch; dependency-free requests are preflighted before any destination dispatch. - [x] Resolve bindings and validate final outbound URLs and redirects before sending. Block internal and private destinations; keep credentials and sensitive request configuration off the public page. - [x] Add generated-site and server tests for JSON, multipart, mapping, files, repeated fields, invalid configuration, and destination safety. - [x] New Form route proof: compiled Remix and React Router managed actions forward repeated same-name text values and uploaded files as separate multipart entries, preserving order, duplicate values, filenames, MIME types, and bytes (`3c5b72a`; focused CLI tests passed 2/2). Existing Webhook Form behavior remains at its pre-PR baseline; migration is out of scope. - [x] Make file inputs easy to configure in the Builder: choose the file input type and set its field name, required state, accepted file types (`accept`), and multiple-file selection (`multiple`). Keep these settings after saving, reopening, and publishing. Verified on `d7f67307f9` with Builder insert/save/reopen, generated page, Chromium required/optional/multiple upload, SDK template, typecheck, lint, and format checks. ### Acceptance criteria - Every selected HTTP destination receives POST. A Resource with an unset or empty body receives all Form data implicitly, regardless of where the Resource is defined. Explicit `formData` expression bindings remain limited to Resources defined within that Form. Text-only bodies arrive as JSON; bodies with uploads arrive as multipart with field names, filenames, MIME types, file bytes, and repeated values. - A site creator can add and configure a file input using the Builder without custom code. Published forms support one or multiple selected files, block submission when a required upload is empty, and submit normally when an optional upload is empty. The `accept` setting guides the file picker; it is not server-side file validation. - An edited body can rename, omit, add, or compute fields. A Resource outside the Form sends only its own configured data. - An incompatible explicit body format fails before its destination fetch. Dependency-free requests are validated before any dispatch; a body that depends on another Resource is validated once that dependency resolves, before its own destination fetch. Resolved destinations and each redirect reject private or internal addresses, and secrets stay off the public page. ## Batch 4 — Project Emails settings and Resource editor - [x] Add Project Settings → **Emails**. Move the current Contact email list without changing its stored value. - [x] Configure site-wide defaults for Sender (name and email, or email alone), recipients, owner subject, and plain-text owner body. Published sites receive project default changes after republishing. - [x] Add the Email Resource as a normal Resource, creatable inside or outside a Form. It can inherit, override, and reset project defaults. Only a Form-scoped Email Resource can bind that Form's data. Surface an unavailable Form-only binding clearly; never send incomplete template text silently. - [x] Resolve team recipients from the project list or owner fallback, support a custom comma-separated override, and default Sender to the owner with a per-Resource override. Form data cannot choose team recipients. - [x] Configure the subject, plain-text owner body, and whether submitted files should be attached. With no project body or Resource body, any selected non-Visitor Email Resource uses readable submitted fields and optional `browserInfo` interpolation, regardless of Resource scope. Automatic owner-email text includes hidden fields while excluding passwords and internal bot fields. - [x] Test project settings storage, inheritance/override/reset, recipient fallback, Form scope, expression validation, and preservation of the existing Contact email setting. A complete project owner body containing `${...}`, backslashes, or backticks renders as literal text; an empty project body retains the automatic `formData` and `browserInfo` interpolations. ### Acceptance criteria - Existing Contact recipients appear in Project Settings → Emails without data loss. Existing legacy forms retain their pre-PR behavior except for the explicitly tracked #5512/#3891 current-page route and repeated-query bindings. - New Email Resources inherit published project defaults until overridden; reset restores inheritance, and settings edits affect live sites only after republishing. - Team recipients use the project list or fall back to the owner. A custom comma-separated recipient list and Sender override work; submitted values cannot choose recipients or From. - With no project owner body or Resource body, a selected non-Visitor Email Resource uses readable submitted-field and `browserInfo` content regardless of whether it is defined on the Form, an ancestor, or globally. Explicit expressions can bind Form-only values only from Resources defined within that Form. A nonempty project body is the complete literal owner message, inherited until a Resource body expression overrides it; reset restores inheritance. Automatic text includes hidden form fields but excludes passwords and internal bot fields. ## Batch 4a — Sender and recipient email format (#4414, #5658) Use one **Sender** field that accepts either `Acme Support <support@example.com>` or `support@example.com`, using the same address format as recipients. The optional name becomes the displayed sender name, and the entered email receives replies. Webstudio's authenticated From address stays fixed. - [x] Add the Sender field in Project Settings → Emails, with inheritance, override, and reset in each Email Resource. Default it to the project owner's email. This single field supplies the display name and Reply-To; do not add separate sender-name and Reply-To inputs. - [x] Parse Sender as exactly one mailbox and retain its optional name and reply address; a plain email has no custom display name. - [x] Use the site owner profile username as the default visible From name when Sender has no explicit name; explicit Sender names take precedence, and missing usernames fall back to “Site Owner”. Keep the authenticated From address fixed. - [x] Accept plain and named mailboxes in the Contact email list, project recipients, and Email Resource recipient overrides. - [x] Verify existing project address-count limits remain enforced through both Builder and server updates; the separate five-team-delivery runtime cap is tracked in Batch 5. - [x] Use `Acme Support <support@example.com>` as the Sender placeholder and `Acme Support <support@example.com>, team@example.com` in email-list inputs. - [x] Use an established email-address parser in Builder and server validation. Handle quoted names containing commas, such as `"Example, Alexs" <support@example.com>`, reject malformed addresses and header-injection input, and count parsed recipient mailboxes including duplicates for the runtime delivery cap. - [x] Preserve parsed display names in generated Email Resource requests and keep existing plain-address settings readable. - [x] Test both Sender formats, mixed recipient parsing, inheritance/reset, generated request values, and the five-team-delivery runtime cap. - [x] Local Builder E2E verifies Project Emails Sender and named recipients save to persisted project settings and remain after reopening (`b9ebf21fee`; `apps/builder/e2e/tests/project-settings-runtime.[shard-2].[shard-5].[shard-6].e2e.ts`; focused run 2/2). - [x] Document the Sender/recipient input format, reply behavior, inheritance, republishing, visitor Email Resource delivery, and the current Cloud-unavailable delivery status without claiming that saving settings sends email. - [x] Local generated-graph verification (`1730840feb`): a translated project body is the entire literal request body, a Resource body expression overrides it, reset restores inheritance, and an empty project body retains automatic `formData`/`browserInfo` text. Project Emails help and docs explain plain-text behavior. ### Acceptance criteria - Entering `Acme Support <support@example.com>` in Sender displays “Acme Support” as the sender and directs replies to `support@example.com`, while From uses Webstudio's sending address. - Entering only `support@example.com` works without a separate name field and directs replies to that address. - With a plain Sender address, the visible From name uses the site owner profile username, or “Site Owner” if unavailable; a named Sender overrides it. The From address remains Webstudio’s fixed sending address. - Plain and named recipients can be mixed in a list; a quoted comma inside a name does not create another recipient. - Names and addresses survive saving, publishing, and delivery, and existing plain-address settings continue working. - The project owner confirmed that the email customization already planned in Batches 4 and 4a is sufficient to close #4414 and #5658. Sender supplies the display name and reply address; Webstudio remains the authenticated sender. Do not add custom From addresses, sending-domain verification, or a separate follow-up requirement to close these issues. Do not describe Reply-To as a custom From address. - Site creators can edit the notification subject, plain-text template, content, and language, with project defaults and per-Resource overrides. Together with Batch 4, this covers the email customization requested in #4414. ## Batch 5 — Email delivery and visitor confirmation - [x] Implement the Cloud-only `EMAIL_SERVICE` adapter for new Email Resources. Attach every submitted file by default; preflight the 5 MiB encoded-message and 32-attachment limits and return explicit service errors. Staging published-Form Email delivery is verified in Batch 7. - [x] Implement visitor confirmation as an ordinary Email Resource selected in Actions, addressed through a submitted visitor email field and dispatched alongside other Actions. Its optional body starts empty and uses the existing Resource editor and expression bindings. Email Service prepends a mandatory confirmation preamble with the site URL. Remove the Form-level confirmation toggle and email-field selector; SDK and generated-route tests cover delivery. - [x] A failed visitor-addressed Email Resource appears in `errors` and does not change aggregate Form success; SDK tests cover this. - [x] Before any destination runs, count team-recipient deliveries across all reachable Email Resources, counting duplicates and shared dependencies once. Reject the whole submission above five at runtime; permit at most one visitor-addressed receipt separately. No cross-Resource Builder or publish-time cap substitutes for this preflight. - [x] Local subject validation (`1fa8e66d9d`): resolved Email Resource subjects must be strings without CR/LF. Validate after expressions are evaluated and before any selected destination dispatch. SDK tests cover CR/LF from direct `formData` and `browserInfo` subject bindings and zero provider or selected fetches (`managed-form-resources-generator.test.ts`, `managed-form-submission.test.ts`, `resource-loader.test.ts`; focused tests 88/88 passed). Dependency-bound Email is rejected after any required lookup and before selected destination dispatch. - [x] Verify resolved owner and visitor-confirmation subjects in the generated server path with provider fakes. Owner subject preflight covers CR/LF from visitor-derived bindings before any selected destination or provider send; confirmation subject preflight likewise sends no HTTP or email request. Published staging delivery is verified in Batch 7. - [x] Test the local private-service envelope and response path with a provider fake: recipients, Reply-To, optional Sender display name, plain text, attachments, recipient and size/count preflight, privacy, confirmation ordering, partial failure, quota error, and zero sends on failed preflight. Compiled Remix and React Router action tests cover confirmation with an HTTP sibling and Node's explicit unsupported response. ### Cloudflare Email Sending integration plan Companion SaaS PR: [webstudio-is/webstudio-saas#666](https://github.com/webstudio-is/webstudio-saas/pull/666). The Email Service contract uses the canonical Worker `webstudio-email-service`, Cloudflare's native per-location Rate Limiting binding, and the private `EMAIL_SERVICE` service binding for published-site sends to `https://email-service.internal/v1/send`. Builder Preview uses the authenticated shared Email Service endpoint `https://apps.webstudio.is/v1/preview-send` with server-side `TRPC_SERVER_API_TOKEN`. The shared Email Service Worker remains deployed at version `8769175b-15d2-4c09-a312-b78dc93fd2f3`; this PR did not deploy or change it. The named staging Preview was deleted and its former URL returns 404; staging published-site Workers use the canonical Worker through the private service binding. An authenticated malformed production Preview request returned `400 invalid_request` / `Invalid email fields` before send. This proves only authentication and validation, not successful submission or delivery. Staging published-Form delivery is verified in Batch 7; limiter verification is tracked in companion SaaS PR #666. A separate direct Email Service test delivery was confirmed received; no email was sent by the malformed-request preflight. - [x] Enforce Cloudflare's 5 MiB **total encoded message** limit and 32-attachment cap for ordinary outbound email, accounting for base64 expansion; report provider quota, suppression, sender, size, and count errors without claiming delivery. These email limits are separate from the Form's 25 MiB incoming upload limit and Webstudio's five team-delivery preflight. Local Builder preflight/provider-fake tests and the companion SaaS Worker tests cover the limits and stable error mapping; staging published-Form delivery is verified in Batch 7. - [x] Test the local generated-site private-service contract with provider fakes for recipients, Reply-To, optional Sender display name, body, attachments, confirmation timing, partial failures, quota/limit errors, and zero sends for failed preflight (`packages/sdk/src/managed-form-email.test.ts`, `packages/cli/src/prebuild.test.ts`; `7507c3fbf7`, `ab04be99d4`, `7c950eecec`). #### Acceptance criteria - Staging verification must prove the selected private call path works for a Workers for Platforms published site; the central Worker alone owns `send_email`, and a submitted Form never receives provider credentials or direct sending capability. - A Cloud published Form delivers owner mail and the optional confirmation with the configured envelope and content; 5 MiB/32-attachment violations and provider failures produce clear `errors`. HTTP siblings retain the existing partial-failure semantics. - The Email Service contract is for the new Form only. Existing Webhook Forms do not use this Email Service as part of this feature; no legacy Email Service compatibility or migration is included. ### Acceptance criteria - A Cloud Email Resource sends the configured plain-text subject and body from Webstudio's sending address with the configured display name, with every submitted file attached under its original filename and MIME type, or returns an explicit size/count error. - More than five total team-recipient deliveries across Email Resources rejects the whole submission before any destination runs; duplicate addresses count again. - A visitor receipt is a normal Email Resource explicitly selected in Actions and addressed to the submitted visitor email field. It runs in parallel with the other selected Actions, with no separate Form-level toggle or email-field selector. Its Resource body is empty by default; the Email Service always prepends the fixed platform receipt and published site URL before the Resource body. The Resource body uses the normal editor and full existing expression/binding support. A failed visitor Email Resource appears in `errors` but is nonfatal to the aggregate Form result. - Resolved owner and confirmation subjects are strings without CR or LF. An invalid subject, including one produced from visitor data, is rejected before the email provider is called; the provider fake records no send. - Provider integration checks exercise the central Email Worker request/response contract. Mocked Builder delivery alone does not satisfy this gate. - Confirmation failure appears in `errors` while the aggregate submission remains successful; environments without Cloud email do not silently claim delivery. ## Batch 5a — Keep separate submissions in separate email threads (#5018) Site owners should see each form submission as a separate email conversation, including when several visitors submit the same form close together. - [x] Append a short, unique submission reference to each new Form owner-notification subject, keeping the configured subject text. Generate it once per submission and reuse it for that submission's delivery retries; submissions in the same second get different references. At `52fcec3640`, SDK tests cover default/custom subjects, two submissions at one mocked time, retry stability, empty/overlong preflight with zero HTTP/email sends, and the 979-character boundary; Form and Project Emails docs describe the suffix. Batch 7 verifies that separate submissions create separate Gmail conversations. - [x] Apply per-submission subject references to new Form owner notifications (`52fcec3640`). - [x] Add local regressions for new Form default/custom subjects, same-time identical submissions, stable retry references, and subject-length preflight (`managed-form-email.test.ts`). Document the suffix in the new Form and Project Emails docs. ### Acceptance criteria - Two separate submissions of the same form appear as separate conversations in Gmail with conversation view enabled. - Both default and custom subjects keep their configured text and include a different reference for each submission. - Retrying delivery within one submission keeps its reference. This does not add delivery deduplication or submission storage. - This submission-reference behavior applies to new Form owner notifications only; it does not add delivery deduplication or submission storage. ## Batch 6 — Parallel delivery and Form feedback - [x] Resolve dependency Resources needed for bindings, preflight every selected primary destination’s body format and deterministic URL parse/protocol/credentials/denied-hostname policy before dispatching any selected primary destination, then start selected primary destinations in parallel. Dependency Resources may make outbound requests during resolution. DNS/public-IP pinning and redirect checks remain in protected transport at each request hop; other transport failures can occur after a sibling dispatches. Overall success requires all primary destinations to succeed. - [x] Local deterministic group preflight (`e9b72d5d78`, `8bf4f4c8bf`): red-before generated actions in Remix and React Router prove a dependent JSON+File body or a denied selected URL blocks all selected POSTs; SDK policy tests cover allowed URLs and denied scheme, credentials, and host cases. Nonselected dependencies may run once to resolve bindings. Provider and ingress behavior are covered by service tests; staging verification is recorded in Batch 7 below. - [x] Until Cloud email delivery is integrated, reject any selected Email Resource or reachable Email dependency before dispatching an HTTP sibling. Return an explicit unsupported-delivery error without claiming success. - [x] Retry only a failed or timed-out destination once within that submission, including non-2xx responses. Never resend a known success within the same submission. A new browser submission starts over; keep no delivery history or cross-submission deduplication. - [x] Keep aggregate HTTP status as transport metadata only; expose no `status` variable. Bind each Resource's final status code and response body through ordered `results` and `errors` arrays. Expose no response headers. - [x] Redirect only after overall success when configured. Render the initial, success, and error states in the inserted Form template. - [x] Fix #6487 feedback auto-scroll regression: success and error feedback now scrolls into the viewport only when needed, uses instant behavior for reduced motion, and success redirect skips scrolling. MCP-authored generated-Preview tests assert actual viewport position for off-screen feedback, no movement for visible feedback, partial failure scrolling, and no scroll before redirect; the full serial MCP E2E suite passes (36/36 including setup). - [x] Copy the existing Webhook Form spam protection into the new Form's managed submission path: generate the hidden browser field, validate it on the server before delivery, preserve the Brave handling, and remove internal fields before sending to Resources or email. **Email Service rate-limit checkpoint:** The pushed SaaS code uses Cloudflare's native Rate Limiting binding only on email sends: 50 recipient checks per project UUID per Cloudflare location per 60 seconds. Each recipient consumes a check; over-limit sends return 429 before provider delivery. Preview and published sends carry the trusted project UUID. This is approximate per location, with no Durable Object, SQL storage, or account-wide quota. The 60-attempt direct Worker diagnostic and isolated non-production check are tracked in companion SaaS PR #666; no live-user Form submission is part of this Builder PR. - [x] Locally test concurrent starts, retries, timeouts, partial success, results and errors bindings, redirect, and feedback scrolling in both generated adapters and browser components. - [x] Local new-Form behavior: generated Router and Remix adapters test concurrent selected starts, one retry after non-2xx/network/timeout failure, final partial-success `results`/`errors`, and no replay of successful siblings or dependencies (`packages/cli/src/prebuild.test.ts`). Builder insertion tests verify `results` and `errors` bindings and confirm aggregate `status` is not exposed as a variable (`apps/builder/app/shared/instance-utils/insert.test.tsx`); browser tests cover success-only redirect and visible/offscreen feedback for the new Form. ### Acceptance criteria - Primary destinations begin in parallel. Each failed or timed-out destination gets one retry within the same submission; known successes are not resent, and overall success requires every primary destination. - Ordered `results` and `errors` expose each Action Resource's `resourceId` and `resourceName`, final status code, and response body to bindings, without response headers. Both arrays preserve configured Action order even when responses finish out of order. Aggregate HTTP status remains transport metadata and is not exposed as a Form variable. - Redirect occurs only after overall success, without scrolling to inline feedback first. Otherwise the correct feedback state renders and scrolls into view only when outside the viewport. Already-visible feedback does not move the page, and animated scrolling respects reduced motion. This applies to the new Form. - New Form submissions use the adapted hidden browser check: valid browser and Brave submissions pass; missing, malformed, or expired evidence stops delivery before any destination runs; internal bot fields never reach Resources or email. - The Email Service code returns 429 for recipient checks over the native per-project-UUID, per-location 50-per-60-second limit before provider delivery. HTTP/Webhook Actions and webhook-only Forms are not throttled by it. The 60-request direct Worker diagnostic and webhook control are tracked in companion SaaS PR #666; no production Form submission is part of this Builder PR. ## Batch 6a — Legacy-only work excluded from this feature ### Acceptance criteria - The new Form accepts a fresh valid submission after an earlier failed attempt without stale bot data. - No legacy compatibility path is added to the SaaS Email Service. ## Batch 6b — Shared submission runtime for the new Form (related to #4093) Use shared new-Form submission behavior across supported hosting runtimes while keeping page data current after a submission. This batch does not complete #4093's remaining legacy Webhook Form work. - [x] Move new Form submission, pending/error/success handling, and response handling into shared code that does not require Remix or React Router hooks or their form components. Generated hosting adapters call the shared managed-Form handler; existing Webhook Forms keep their original behavior. - [x] Local new-Form lifecycle: router-free Form submission uses shared `submitFormData`; generated Router and Remix adapters supply the server request and page-data refresh behavior. Legacy compatibility adapters and lifecycle changes are not included. - [x] Submit through a normal HTTP endpoint with a defined JSON response and small Router/Remix request and page-data-refresh adapters. Keep current HTTP Resource credentials, validation, bot checks, and destination protection on the server. - [x] Local managed-Form route: generated same-origin `/__ws-form` POST returns structured JSON for new Form submissions in compiled Remix and React Router handlers; route coverage includes root, nested, and dynamic pages, repeated multipart fields/files, bot rejection, and Resource results. Staging Cloud email delivery is verified in Batch 7; the Email Service rate-limit verification is tracked in companion SaaS PR #666. - [x] Integrate the Cloud `EMAIL_SERVICE` adapter for new Email Resources into the generated server action, with provider-fake response and preflight tests. The Email Service per-site rate limit is tracked separately below. - [x] Replace the automatic page-data refresh currently supplied by Remix. After a successful submission changes data, refresh the current page's Resources without a full-page reload. Handle navigation during submission and refresh failures without resending a completed submission. - [x] Test the new Form's shared component without router context against a test HTTP endpoint, then test both hosting adapters. Cover response bindings, page-data refresh, files, redirects, errors, and retries. - [x] Local browser and generated-route matrix: router-free Form tests cover pending/results, files, and HTTP/network failures; generated Router and Remix adapter tests cover retries, redirects, loader refresh, navigation, and refresh failure without replaying POST. Compiled-handler tests cover both adapters; release/staging verification is complete in Batch 7. - [x] Real router-free HTTP test (`679eb8e0c275`, `packages/sdk-components-react/src/native-form-http.test.tsx`): the shared new Form runs without a router provider in Chromium and sends multipart POST to a local same-origin JSON endpoint without mocking `fetch`. It reports pending while the response is held, then success with `results` and a later error with `errors`, without native navigation. Release/staging verification is complete in Batch 7. - [x] Page-data-refresh red-before/green-after proof: with the exact `native-form-refresh.browser.test.tsx` files from `20989a2d64` placed temporarily in detached pre-fix `41580dbe9c`, React Router and Remix each failed 3/5 tests: successful submissions left displayed data at `before`, and the refresh-failure path never ran its second loader call. On PR head `679eb8e0c275`, both adapters pass 5/5. The temporary checkout was restored cleanly; the red/green regression evidence is recorded here. - [x] Local new-Form regression tests cover pending/results, errors, redirect behavior, selected destinations, retries, and page-data refresh. Remove legacy Webhook-specific retry, feedback, lifecycle, and converter claims; they are outside the PR scope. - [x] Update the hosting and Form docs to explain the generated server endpoint, dynamic hosting requirement, and static-export limitation. The generated Cloud action calls the private `EMAIL_SERVICE` binding when present. Staging published-Form delivery is verified in Batch 7. ### Acceptance criteria - The shared submission component works without a Remix or React Router provider; supported hosting adapters connect it to server delivery and page-data refresh. - A successful data-changing submission refreshes the displayed data without reloading the whole page or sending the submission again. - Resource response values remain available through the bindings from Batch 6, with consistent success/error behavior across supported runtimes. - The generated new-Form submission path uses the shared handler; existing CLI conversion and legacy submission behavior remain at their pre-PR baseline except for the #5512/#3891 route/query binding fixes in Batch 2a. - Server protections and secret handling remain intact. Static exports do not silently claim managed submission support. ## Batch 7 — New-Form integration, docs, and release gate - [x] Mark only the legacy `Form` and `RemixForm` component metadata deprecated and keep `NativeForm` as the sole Forms insert tile. Preserve the old component IDs, registrations, templates, and supported settings for saved instances (`820aed68ed`; `apps/builder/app/shared/nano-states/components.test.tsx`, focused Chromium 2/2). - [x] Local file-input and HTTP upload segments: Builder insertion/save/reopen retains name, required, accept, and multiple settings (`apps/builder/app/shared/instance-utils/insert.test.tsx`); browser tests cover required, optional, and multiple files (`packages/sdk-components-react/src/native-form.browser.test.tsx`); compiled Remix/React Router generated actions preserve repeated multipart fields and original file names, MIME types, and bytes (`packages/cli/src/prebuild.test.ts`, `3c5b72a`). - [x] Partial published-site staging check: one controlled `/copy-1` submission with unique test value `Staging E2E 2026-10-05` entered the managed Form success state for the selected project-owner Email Resource configured with subject `New form submission`. The exact wire HTTP response and owner inbox receipt are not independently verified; the user is checking that inbox. This does not close the full multi-destination, file, confirmation, coverage. - [x] Update Form, Resource, Project Emails, and hosting docs for File Input settings, HTTP uploads and limits, visitor-addressed Email Resource with mandatory confirmation preamble/site URL and optional body, and plain HTML forms using Element with `tag=form`. - [x] Document the current managed Form's combined 25 MiB upload-request limit, multiple-file failure guidance, and separate HTTP Resource body limit (`33b2c7eab9`); Email provider attachment limits are documented in the Email Service README, and published staging delivery is verified in Batch 7. - [x] Document local Resource selection/scope and Auto/JSON/multipart upload behavior, plus Project Emails Sender, recipient, owner-body, inheritance, and visitor Email Resource settings (`b9dbb6ee55`, `7507c3fbf7`). Form, Project Emails, and self-hosting docs now describe conditional Cloud `EMAIL_SERVICE` delivery, explicit missing-service failure, and self-hosted integration requirements (`61fcf3f3d5`); published staging behavior remains unverified. - [x] Document Cloud Email attachment count and encoded content/request limits in Form docs (`61fcf3f3d5`). - [x] Document provider/quota errors and the Cloud email hosting requirement. Builder Form, Project Emails, and self-hosting docs cover new-Form visitor behavior and hosting limits. Sending-domain setup and companion SaaS deployment are tracked in the companion SaaS PR. - [x] Refresh the current bundle-version metadata in all eight static project fixtures and synced user metadata in seven; `packages/protocol/src/fixtures.test.ts` passes 17/17 locally and Linux/Windows fixture CI both pass on head `70a2091c69`. - [x] Complete relevant local checks on current PR work. Focused tests, typecheck, lint, formatting, and diff checks pass locally; full GitHub CI passes on `84f24e8d12`. - [x] Reviewed each new-Form issue row against current code/tests; no additional legacy compatibility beyond the explicitly tracked #5512/#3891 route/query fixes is a closure condition. Evidence: #3023 file and attachment checks in `packages/sdk/src/managed-form-email.test.ts` and `packages/cli/src/prebuild.test.ts`; #5512 and #3891 page parameters and all query values in `packages/cli/src/prebuild.test.ts`; #5432 repeated multipart values/files in the same generated-route suite; #5499 parallel destinations and five-recipient preflight in `packages/sdk/src/managed-form-handler.test.ts`, `managed-form-resources-generator.test.ts`, and `managed-form-submission.test.ts`; #3530 redirect behavior in `packages/sdk-components-react/src/native-form.browser.test.tsx`; #5428 trusted browser info headers in `packages/cli/src/prebuild.test.ts`; #5658 sender/reply-to validation in `packages/sdk/src/managed-form-email.test.ts` and the companion SaaS Worker tests; #6487 feedback scrolling in `packages/sdk-components-react/src/form-feedback-scroll.browser.test.tsx`; #4414 and #4385 project defaults, overrides, and recipient isolation in `packages/sdk/src/email-resource.test.ts` and `apps/builder/app/builder/features/settings-panel/resource-panel.test.ts`; #5018 unique submission references and retry behavior in `packages/sdk/src/managed-form-submission.ts` and `managed-form-email.test.ts`. These are merge-time issue mappings, not production delivery evidence. ### Acceptance criteria - The insert menu offers one new Form. Deprecated old components and their migration are outside acceptance; no legacy compatibility path is added. - Staging end-to-end coverage exercises an empty Form blocked from navigation and a configured Form submitting to its Resource destinations, plus two HTTP Resources, two Email Resources, files, confirmation, and partial failure through Builder → publish → submit. - Form, Resource, Project Emails, and hosting docs match the released behavior, including the Cloud email requirement. Generated contracts, focused checks, full CI, and staging tests pass before the PR is ready. ## Batch 8 — Submit current-draft Forms from Builder Preview - [x] Add an authenticated Builder server endpoint for Preview submissions. It checks the Builder session and exact project build permission before effects; route tests cover authorized and rejected requests. - [x] Load current draft Form configuration and invoke the shared SDK runtime handler without a prior publish or draft/published contract comparison; Preview route and graph tests cover changed drafts. - [x] Build the draft Resource graph with publish-equivalent scope, dependency, and defaults rules. Reuse the existing `evaluateExpressionSync` implementation for supported expressions; do not add QuickJS, WASM, AST, or another expression engine. Focused graph and expression tests cover parity. - [x] Reuse the shared SDK managed-form handler in Builder Preview and generated published-site routes. Their request, auth, and configuration entry points remain runtime-specific; SDK and generated-route tests cover both paths. - [x] Execute HTTP Resource Actions from the Builder server in Preview, and from the published-site Worker after publish. The Email Service handles email only; Preview route tests assert the outbound HTTP boundary. - [x] Route Preview Email Resources server-to-server through the Email Service with a server-only credential; published-site Workers use the private `EMAIL_SERVICE` binding. Builder checks user and project permission first; tests cover the Email Service call contract and rejected auth. - [x] Removed the unapproved Preview warning that submissions send real emails and webhook requests; new user-facing warning copy requires explicit approval. - [x] Node tests execute the shared handler locally with a stubbed Email Service boundary, without Cloudflare infrastructure. Staging published-Form Email delivery is verified in Batch 7; the production Worker limiter check is tracked in companion SaaS PR #666. - [x] Keep a permanent Builder Preview transport Playwright E2E that submits a current draft through the Preview backend and verifies the response without publishing or triggering email/webhook side effects (`apps/builder/e2e/tests/preview-managed-form.[shard-2].e2e.ts`; focused 2/2). - [x] Preview converts rejected `onManagedSubmit` callbacks into a structured 500 failure, clears stale result and pending state, and shows the current error. Browser regression covers a prior structured failure followed by a rejected callback (`packages/sdk-components-react/src/native-form.browser.test.tsx`). The new Form also cancels native submission when its `onSubmit` callback throws; a Chromium browser regression clicks a submitter with `formAction`/`formMethod` overrides and verifies cancellation, no Resource dispatch, and visible failure (`28f7a93eed`). ### Acceptance criteria - A Form in an unpublished project can be submitted from Builder Preview without publishing first. - The Preview endpoint rejects unauthenticated users and users without edit access to that project before any HTTP request or email send occurs. - Preview uses the current draft. Changes to controls, Actions, Resource dependencies, effective email settings, visitor-addressed Email Resources, or redirects can be tested immediately without republishing or a published-contract comparison. - Draft expression results match published Resource evaluation for supported syntax using the existing `evaluateExpressionSync` implementation; no second expression engine is introduced. - HTTP Resource requests go directly from the Builder server to their configured destinations. Email Resource requests go through the central Email Service Worker, which rejects requests without the server credential; that credential never appears in browser traffic. - Results, errors, uploads, response bindings, feedback, and redirects match published submission behavior while keeping the Builder open. - The generated published-site Worker continues to use the same shared handler and public visitor submission path, and calls Email Service through `EMAIL_SERVICE`. - Local Node E2E tests exercise the shared handler and Builder Preview authorization without Cloudflare; a separate integration check verifies the real Email Service contract. ## Production release boundary **The production Email Service Worker was deployed at version `8769175b-15d2-4c09-a312-b78dc93fd2f3` under the user's explicit request to update the code and Worker to use `TRPC_SERVER_API_TOKEN`. An authenticated malformed validation request returned `400 invalid_request` before send; that request sent no email.** This Builder PR makes no deployment to the live site; live-user Form submissions are outside its verification scope. ## Boundaries and known tradeoffs - No old-component migration, submission storage or history, account-wide email quota feature, CC/BCC, custom From address or customer sending-domain verification, HTML email, preview text, or separate branding editor. Builder Preview submission is included in Batch 8 and runs against the current draft without requiring publication; it is a real submission, not a separate email test-send feature. Cloudflare account sending quotas still govern delivery; migrating Plunk authentication or other Plunk operations is separate follow-up work. - The Discord requests for an explicit CAPTCHA challenge, direct Google Sheets connection, multi-thumb/vertical slider, and an exact submission copy to an unverified visitor are not covered here. A visitor-addressed Email Resource always gets the fixed platform preamble and site URL; its optional builder-configured body may use normal Resource bindings. - A timeout retry can duplicate a delivery that succeeded remotely. A visitor-addressed Email Resource can send to an unverified submitted address; the native Email Service limit of 50 recipient checks per project UUID per Cloudflare location per minute can reject legitimate local bursts; it is approximate and not a global cap. - The five team-delivery cap is a server preflight across the whole Form; it is intentionally not calculated across independent Resources in the Builder. - Project defaults apply site-wide. Form-only expressions resolve only in Resources scoped inside that Form. ## Additional Builder and Resource behavior ### Builder UI - [x] Add the `formState` bullet to the Action tooltip after the exact user-facing description is approved; verify it with the focused tooltip test. The exact wording was approved; covered by focused Action tooltip tests. > Sends each form submission to all selected destinations at the same time. > - formData — submitted fields from this Form. > - browserInfo — visitor details. > - formState — current Form state: initial, success, or error. > - results — each action’s response, in order. > - errors — failed actions and their messages. - [x] In the Variables list and Edit variable preview, show server-provided `browserInfo` after an authenticated Builder Preview submission when available. Use only trusted `cf-connecting-ip`, ignore spoofed `x-forwarded-for`, and sanitize Referer by removing query and fragment before exposing it. Keep `previewBrowserInfo` private to Builder memory and strip it before Canvas pubsub. Route, bridge, Variables list, and Edit variable preview tests cover these paths; focused preview tests pass (2/2) and bridge tests pass (7/7). Published Form behavior stays unchanged. - [x] Recognize supported Chromium browsers without requiring `window.chrome`; user-agent detection and unsupported-browser regression coverage are committed. - [x] Add the approved “Clear all” button below multiple visible toasts and dismiss the stack; shared toast implementation and browser regression are committed. - [x] Remove the background from the maximize button in code and expression editors; keep the icon button transparent, consistent with other editor controls. `code-editor-base.browser.test.tsx` verifies transparent hover styling. - [x] Implement separate tooltips for the Form variables `formData`, `browserInfo`, `formState`, `results`, and `errors`; exact wording was approved on 2026-10-08, and `variables-section.test.tsx` verifies the displayed descriptions. - [x] Add a description to the Action label tooltip and show a yellow warning triangle with “Add at least one action” when the Action list is empty. The warning disappears once an Action is added; covered by the Form Action control browser test. - [x] Reset the Form Preview runtime state to `initial` when leaving Preview, so the Builder returns from success/error feedback to the editable Form state without changing saved bindings or published behavior. Add regression tests. - [x] Route every managed Form submission error through the user-styled Error Message instance. Make its message dynamic from the submission error result, remove static fallback text and raw React alerts, and cover validation, partial Action failure, and service errors. - [x] When a Form, Form content, Success Message, or Error Message instance is selected on the canvas, show that selected instance in Builder regardless of its bound `show` value. This is an editor selection override only; preserve the saved binding and runtime behavior. Add regression tests for each state. - [x] Add the approved description “Sends each form submission to all selected destinations at the same time” and a multiline list for `formData`, `browserInfo`, `formState`, `results`, and `errors` to the Action label tooltip; use lowercase “action” in descriptions. Remove the aggregate `status` variable; Form state remains `initial`/`success`/`error`, and each Action’s HTTP status stays in its result/error entry. Retain the approved “Add at least one action” warning when no Action is selected. The Action reset button says “Reset.” Focused insertion and Action UI tests cover these behaviors. - [x] The Action plus-button tooltip says “Add a resource to submit this Form”; the focused Form Action control browser test covers it. - [x] Rename the Form’s component prop from `submission` to `action`, matching the singular **Action** control. Store selected Actions as an ordered array of Resource references with enabled state. Execute enabled requests in parallel while preserving Action order in `results` and `errors`. Update generated metadata, runtime handling, and tests; do not add legacy compatibility. - [x] Add Delete, Copy, and Paste to each variable row menu and its right-click context menu. Within Variables, right-click anywhere allows Paste; target-specific commands are disabled without a variable row. Remove the Variables section header menu. The Edit variable dialog menu has Delete, Copy, and Refresh only; remove its redundant quick-copy button and do not offer Paste there. Serialize definitions as versioned, namespaced JSON; omit internal IDs, create fresh IDs on paste, preserve and remap expression references, and report unresolved references. Base menu and transfer tests pass. - [x] Rename user-facing Builder UI section and control labels from `Data variables` to `Variables`; retain `CSS variables` for clarity. Update UI tests. - [x] Give variable labels a tooltip that shows defining scope, type/value-kind details, and Delete with its shortcut. Option-click deletes the local variable; deleting a shadow reveals its ancestor. UI tests cover the tooltip and Option-click deletion. - [x] Clicking the rendered “Value comes from” source label navigates to and focuses its defining instance. `variables-section.test.tsx` exercises the real ancestor “Body” source label and Global root label; the full focused file passes 9/9. - [x] Label a Global Root user variable source as “Global root,” not “System,” and keep the source label clickable to navigate to and focus its definition. `variables-section.test.tsx` verifies the “Global root” label, absence of “System,” and source navigation. - [x] Fix the verified source-label behavior defect: clicking “Value comes from” must reliably focus its defining Variables row after ancestor or Global root navigation. Regression tests pass both standalone and in-suite: `variables-section.test.tsx` passes 12/12, and isolated ancestor/Global root navigation-focus cases pass 3/3 each. - [x] Prevent deletion of the managed Form’s required `formState`, `results`, and `errors` variables by stable identity in the Builder UI, including after renaming, through Variables row menus, the Edit variable menu, right-click context menus, and Option-click; ordinary user variables remain deletable. `variable-menu.test.tsx` covers the menu paths and ordinary deletion; focused suite passes 21/21. The required-variable rule is enforced in both the Variables UI and the mutation/domain layer. - [x] Show a yellow warning triangle only when a variable name matches an ancestor; allow the child-scope name. Tests cover an existing shadow and an ancestor rename. - [x] Keep the variable row highlighted while its Edit variable dialog is open; use the shared list-row open state and cover it with a regression test. - [x] Use the shared warning color token for variable-shadow triangles in both the Variables list and Edit variable dialog; the Builder UI test verifies the token. - [x] Reject duplicate Resource variable names on the same instance while allowing a nested scope to shadow a parent name; add validation and regression tests. - [x] Use sentence case for the Form setting label: `Success redirect`. - [x] Expose Form-scoped runtime values as inspectable JSON in Builder Preview. `formData` reflects the current named controls and values before submission, including empty values; `browserInfo` shows its safe context values. Add focused UI and runtime tests. - [x] Add `resourceName` alongside `resourceId` in each ordered result/error and relevant inspection entry; verify naming, partial failure, retries, and bindings without exposing secrets. - [x] Independently rechecked #3891 against the implemented contract: `system.search` returns a string for one value and an ordered array for repeated query values. `packages/sdk/src/system-search.ts` and its regressions cover repeated, empty, comma-containing, and encoded values; Batch 2a retains the generated-route and documentation evidence. - [x] Verify that a selected Email Resource defined on the Form, an ancestor, or global scope uses the existing generated submitted-fields default body when both its Resource body and Project Emails body are unset. Preserve explicit Resource/Project body overrides, including an explicitly empty Resource body, Visitor receipt behavior, and rejection of explicit out-of-scope `formData` bindings. Draft and published graph tests cover all three scopes and both override types in `managed-form-resources-generator.test.ts` and `managed-form-draft-graph.test.ts`; focused SDK tests pass (73 tests). ### Resource editor - [x] In Project Settings → Emails, show all long explanations in the existing info tooltips beside their related controls, preserving the exact existing copy. Specifically verify the Recipients and Owner plain-text body explanations; `section-emails.test.tsx` currently covers tooltip content and keyboard access. - [x] Restore the approved attachment behavior sentence in the Project Settings → Emails documentation, in the Visitor email paragraph only: “When a Form includes file inputs, submitted files are attached to the outgoing Email Resource message by default. Turn off ‘Attach submitted files’ to omit them.” Do not add this text to the settings UI. Added in `c96fb7c409`; `pnpm check:generated-docs` and `git diff --check` pass. - [x] Replace the outdated Visitor attachment statement in the Form guide with the same approved sentence used in Project Settings documentation: “When a Form includes file inputs, submitted files are attached to the outgoing Email Resource message by default. Turn off ‘Attach submitted files’ to omit them.” Added in `40dd8c4af3`; `pnpm check:generated-docs` and `git diff --check` pass. - [x] **Email Resource Type description:** The exact description was approved, and `variable-popover-type.test.tsx` asserts the Email option displays “Send a plain-text email through Webstudio Cloud when a Form is submitted.” Focused type-selector suite passes 7/7; Builder typecheck passes. - [x] **Method dropdown copy approval:** Exact copy is approved and tested: GET “Read data from a server.”; POST “Send data to create or process something.”; PUT “Replace data on a server.”; DELETE “Delete data from a server.” Keep the existing Form-submission note with POST; focused Resource editor tests verify all four descriptions and the POST note. - [x] Keep Email Resource field labels as `Sender`, `Recipients`, `Subject`, and `Body`; use the existing binding control and Expression Editor for supported values, and show validation errors in the standard input tooltip rather than below the field. `resource-panel.test.ts` asserts each label appears exactly once, covers the binding controls and Sender/Custom recipient Expression Editor save/unmount/remount/reopen, and verifies validation errors appear in standard input tooltips; the focused suite passes 30/30. - [x] Give Email Resource recipient controls programmatic accessible names by associating the existing visible “Recipients” label with its selector and using existing text for the Custom recipient input and Visitor email selector. Keep visible copy unchanged. `resource-panel.test.ts` passes 32/32; Builder typecheck, changed-file `oxlint --deny-warnings`, and `oxfmt --check` pass. - [x] Remove the separate Project default reset button; switching away from Custom recipients clears its value. Show the custom address input directly below the mode selector and the Visitor field selector without redundant field labels. `resource-panel.test.ts` covers literal and bound recipient clearing when switching to Project and Visitor modes, mode-specific controls, and no duplicate Recipients label; the focused suite passes 30/30, including Sender and Custom recipient bindings through the Expression Editor and save/unmount/remount/reopen. - [x] Verify and test the REST and GraphQL type-option descriptions against the approved wording: “A REST resource is a configuration for secure data fetching.” and “A GraphQL resource is a configuration for secure data fetching with GraphQL.” Preserve the existing “You can safely use secrets in any field.” note. `variable-popover-type.test.tsx` covers both descriptions. - [x] **Visitor email field info tooltip:** Approved exact copy for the info tooltip beside the Visitor email field selector: “Adds a note with this site’s URL to help recipients identify where the message came from and discourage spam.” Keep it visible only when Visitor email field is selected; verify the text opens on hover and keyboard focus and closes on blur in `resource-panel.test.ts`. - [x] Match the Edit variable dialog menu trigger icon size to the other header controls and make the menu’s minimum width about twice its current width; `variable-menu.test.tsx` covers the header icon and 360px minimum width. - [x] Simplify Builder Email Resource preflight: remove duplicated Worker-authoritative mailbox and attachment metadata policy checks; retain a small pre-Base64 attachment size guard, subject safety, and aggregate recipient protections. Tests verify early size rejection, valid attachment delivery, and Worker rejection propagation. - [x] Replace the superficial Headers and Search params label info-tooltip descriptions with clear technical explanations of their purpose; propose and obtain approval for exact user-facing copy before implementation. The approved label info icons explain these sections; no additional plus-button tooltips are needed. Exact wording approved; covered by focused Resource editor tests. - [x] **Recipient selector descriptions:** Add the approved descriptions exactly: Project recipients — “Send to the addresses in Project Settings, or to the account holder if none are set.”; Custom recipients — “Send to the addresses entered below.”; Visitor email field — “Choose a Form input to use as the recipient’s email address.” Verify copy and visibility in `resource-panel.test.ts`. - [x] **Move Visitor email guidance into its recipient option description:** Removed the extra info icon next to the selected Visitor email field input. The Recipients dropdown option now explains that the confirmation email goes to the selected Form input address. `resource-panel.test.ts` passes (41/41); formatting, lint, and diff checks pass. Pushed as `a85c77308f`. - [x] Put the Form POST-method note in Method dropdown option descriptions instead of inline copy; keyboard and pointer behavior are covered by the Resource editor tests. - [x] **Format option descriptions:** Approved exact copy: label `Format`; help below the control “Choose how to send the body.”; `auto` — “Uses text/plain for text values, application/json for other values, and multipart/form-data when files are included.”; `application/json` — “Sends an object or array as JSON.”; `multipart/form-data` — “Sends fields as form data, including files.” `resource-panel.test.ts` verifies the descriptions, below-control help, standalone GET hiding, Form Action GET visibility, and Body editor; the focused suite passes 34/34. - [x] **Headers and Search params add-button tooltips:** Superseded by the user's clarification: the approved info icons beside the Headers and Search params labels explain those sections, so no additional tooltips on the plus buttons are needed. The existing info-icon descriptions remain covered by the focused Resource editor tests. - [x] **Project Settings → Emails helper-text placement:** Move the long inline explanations (including Recipients and Owner plain-text body help) into adjacent info tooltips so the settings stay compact. Do not add or rewrite user-facing copy without explicit approval; preserve approved behavior and cover tooltip placement with focused tests. - [x] **Shadowing warning tooltip copy:** Reverted the unapproved proposed wording and retained the existing tooltip. `variables-section.test.tsx` verifies the parent-scope shadow condition, including an existing shadow and an ancestor rename; a unique name removes the warning. - [x] Add an info tooltip beside Cache max age explaining the response cache lifetime in seconds. A focused Resource editor test verifies it. - [x] Use the approved info icons beside the Headers and Search params labels to explain those sections; no additional plus-button tooltips are needed. Use Minus icons to remove individual rows. Focused Resource editor tests cover the label info tooltips and row removal. - [x] Keep the Body label and remove the Body content type selector. Infer text/plain for string bodies and application/json for objects, arrays, numbers, and booleans; preserve automatic multipart handling for files, explicit request-format validation, and useful editor/error behavior. - [x] Label the body-format dropdown Format and show the approved help “Choose how to send the body.” below it. Use the effective method: hide Body and Format for a standalone GET Resource; a GET-configured Resource selected as a Form Action is effectively POST and keeps both controls visible. Focused editor tests verify the help placement, visibility, and selected body format. - [x] Show Response, Request, and Diagnostics tabs for HTTP Resources, with Response first and selected by default. Capture the actual outgoing method, URL, headers, and body plus response status/status text, headers, final URL when available, and body from the latest Form Preview submission, including retry attempts. `request-inspector.test.tsx` verifies the order and default selection, including the captured numeric response status and actual response body for both HTTP and Email exchanges (focused suite 7/7); route and inspector tests verify captured exchange data, no replay or substitution from current config, and authenticated Preview-only, no-store, ephemeral, bounded, redacted capture. Files show metadata instead of bytes. Email inspection shows the captured transport request and actual HTTP response, with credentials and encoded attachment bytes redacted; the logical delivery outcome remains separate for Form failure handling. Published staging submission and attachment delivery are verified in Batch 7. - [x] Classify Email inspection exchanges by Resource kind, never URL alone; focused sanitizer and inspector tests verify that an ordinary HTTP Resource targeting the Email Service URL keeps normal request-body redaction. - [x] Preserve actual Email Service response headers in the Email inspection Response view; `resource-loader.test.ts` verifies transport capture and `preview-form-inspection.server.test.ts` verifies safe headers reach the inspection view. - [x] Preserve the actual Email Service HTTP response body/status separately from the logical Email delivery result, so provider-level failures remain Form failures even when HTTP returns 200. Read the response body once so inspection and delivery validation share the same parse; keep captured data bounded. Add regressions for malformed success and non-JSON error responses. Single response read, bounded inspection snapshot, provider body and logical outcome regressions; SDK tests pass. - [x] Keep the Variables panel context menu scoped to the panel: right-clicking a variable row targets that row, blank panel space allows Paste, and right-clicking inside the Edit variable dialog does not open the panel menu. The dialog menu offers Delete, Copy, and Refresh only; `variable-menu.test.tsx` covers row, blank-space, and dialog context-menu behavior. - [x] Keep Email Resource attachments enabled by default. The default contact Form’s owner Action keeps attachments enabled, while its visitor confirmation explicitly opts out because it does not include the submitted file. `managed-form-email.test.ts`, `resource-panel.test.ts`, and the default contact Form E2E cover defaults and opt-out. - [x] Support bindings for Email Resource Sender and Custom recipients using the existing binding/Expression Editor pattern; evaluate and validate resolved mailbox values before dispatch. `resource-panel.test.ts` verifies Sender and Custom recipient bindings through the Expression Editor and save/unmount/remount/reopen (30/30). `email-resource.test.ts` verifies resolution and rejects empty, non-string, and invalid values; `managed-form-handler.test.ts` verifies invalid bound values are rejected before Resource fetch or email send (35/35 combined SDK tests). - [x] Memoize Email Resource recipient expression scope inputs (`recipientScope` and aliases) so editing a recipient value does not restart asynchronous expression evaluation on every render; add focused regression coverage. Unrelated Resource edit regression passes; `resource-panel.test.ts` 34/34. - [x] Make all Email binding tests use CodeMirror’s platform-aware Mod shortcut (Meta on macOS, Control elsewhere), including Sender/Custom recipient round-trip and Project-mode bound-recipient clearing, so they pass Ubuntu CI. `resource-panel.test.ts` contains no hard-coded Meta/Control keyboard chords and passes the full focused suite (30/30). - [x] Ensure generated server modules import Email binding helpers only once when both regular Resources and managed Forms use Sender or Custom recipients bindings; generated-site resource-only and combined-module tests in `packages/cli/src/prebuild.test.ts` assert one helper import (2/2), and the CLI typecheck passes. - [x] Ensure generated modules for generic (non-managed) Email Resources using `senderExpression` or `recipientsExpression` import the runtime resolver helpers. `packages/cli/src/prebuild.test.ts` verifies one helper import and typechecks a mixed generated site with page-level and managed-Form Email bindings, plus a page-level-only site; both focused cases pass (2/2). - [x] Enforce the five-recipient total before dispatch for Email Resources with Sender or Custom recipients bindings. Count known/literal recipients even when the Email Resource depends on another Resource; reject recipient expressions whose count depends on a Resource output before any send. Preserve Form-variable bindings. `managed-form-handler.test.ts` covers a resolved six-mailbox binding and verifies rejection before HTTP fetch or email send; existing dependency tests cover Resource-output counts. - [x] Prevent or support Email recipient expressions that depend on another Resource. The editor currently accepts these expressions, but managed-submission preflight rejects them; align editor validation and runtime behavior, with focused regressions. Editor binding scope now excludes Resource outputs; server preflight remains fail-closed; focused tests and Builder typecheck pass. - [x] Disallow Email recipient bindings that depend on another Resource in the Builder editor, while keeping server preflight fail-closed. Resolving dependencies before the recipient-limit check could trigger arbitrary requests; add focused editor and preflight regressions. Editor binding scope now excludes Resource outputs; server preflight remains fail-closed; focused tests and Builder typecheck pass. - [x] Fix Type and Recipients dropdowns in a narrow sidebar so each dropdown panel matches its input trigger width, using Name as the reference; focused tests cover Type, Recipients, and Method. Flattening removes the separate Resource dropdown. - [x] Flatten System resources into the single Type list: remove the separate “System resource” category/dropdown and list Sitemap, Current date, Assets, and Email alongside the other Type choices. When editing an existing System Resource, changing between categories (Sitemap/Current date/Assets ↔ Email) persists the selected type. Preserve existing Assets Resources whose URL expressions use valid non-JSON string-literal syntax; add a reopen regression in `variable-popover-type.test.tsx`. Assets non-JSON URL expression reopen regression passes; Builder popover tests pass. - [x] Fix the Builder typecheck failures in `resource-panel.test.ts` binding tests caused by nullable queried-button assertions, then rerun and pass the Builder typecheck. Builder typecheck passes and `resource-panel.test.ts` passes 30/30. - [x] Add a generated published-route regression proving that an unset/default Form Action body with an uploaded File selects multipart automatically and preserves filename, MIME type, and bytes. `packages/cli/src/prebuild.test.ts` runs the generated endpoint for both defaults and React Router adapters; the focused test passes 2/2. - [x] Add regression coverage that an ordinary Project/Custom Email Resource with `includeAttachments` absent resolves default-on through the Email Service payload. `packages/sdk/src/managed-form-email.test.ts` covers both recipient modes and asserts attachment payloads; the focused suite passes 29/29, and `pnpm oxfmt --check packages/sdk/src/managed-form-email.test.ts` passes. - [x] Resolve current changed-file lint findings (two `no-explicit-any` findings in changed tests and one `react-hooks/exhaustive-deps` finding in `variable-popover.tsx`), then rerun scoped lint. Changed-file `oxlint --deny-warnings` passes on 50 TypeScript files; `oxfmt --check` passes on all 48 matched changed files; Builder typecheck passes; focused settings/variables suites pass 47/47. - [x] Simplify `handleManagedFormSubmission` by removing the redundant one-Visitor-Email validation and relying on the shared `validateManagedFormRecipientLimit` check; add or retain focused preflight regression coverage. Handler and submission suites pass 48 tests, and SDK typecheck passes. ## Additional Form and Builder behavior - [x] **Use the existing upload icon for the File input component:** The shared `UploadIcon` is used in the File Input template and asserted by `core-templates.test.tsx`. - [x] **Verify Success redirect in Builder Preview:** On successful submission, keep navigation inside Preview for internal routes, resolve relative URLs against the previewed page, and navigate the preview frame for external URLs. Add end-to-end coverage for a matched internal route, an unmatched route (which currently silently does nothing), and hash navigation (which currently updates the hash without scrolling). Preserve error feedback and never redirect after a failed action. Matched internal, unmatched internal, and delayed hash target cases pass in Preview E2E; failed actions retain error feedback and do not redirect. - [x] **Finish Email diagnostics loading:** The Email Resource Diagnostics tab no longer remains pending without a request key; focused regression coverage verifies the settled empty state. - [x] **Separate variable editors by type:** The dialog composes focused modules for HTTP, GraphQL, Email, Assets, Parameter, and scalar types; shared controls remain neutral. Focused Builder tests and typecheck pass. ## Issue checklist Eleven issues below are intended to close when this PR merges; #5432 and #4093 are related but remain open. Each closure row must be implemented and verified first; keep remaining work unchecked. | Issue | Required result before merge | Batch | | --- | --- | --- | | #3023 | Configure file inputs in the Builder; deliver single/multiple files to HTTP destinations and Cloud email within documented limits. | 3, 5, 7 | | #5512 | Current-page parameters work in new and legacy Form Resource bindings, including after navigation. | 2a | | #4093 | Related issue for the legacy Webhook Form; this PR implements Resource Actions and response bindings in the new Form but does not change legacy submission beyond the explicitly tracked #5512/#3891 route/query binding fixes, shared lifecycle, or CLI conversion behavior. Keep #4093 open. | 2, 3, 6, 6b | | #5432 | The new Form sends repeated field values as standard multipart entries without losing selections. Legacy Webhook Form behavior remains unchanged except for the separately tracked #5512/#3891 route/query binding fixes; this PR does not close the original issue, and migration is separate. | 2, 3 | | #5499 | One submission can deliver to email and multiple HTTP destinations together; document the ten-destination and five-team-delivery limits. | 2, 5, 6 | | #3530 | A successful submission follows the configured redirect; failure does not redirect. | 6 | | #5428 | A destination receives the trusted visitor IP, user agent, and language through configured Resource headers, with setup documented. | 2, 7 | | #5658 | Custom subject and Sender work as agreed, with provider-level tests for From and Reply-To. | 4, 4a | | #6487 | New Forms reveal off-screen feedback, leave visible feedback alone, respect reduced motion, and do not scroll before redirecting. | 6 | | #4414 | Editable name/reply address, subject, plain-text template, and translated content work with project defaults and per-Resource overrides. Webstudio remains the sender, as agreed. | 4, 4a, 5 | | #4385 | Two forms in the same project can send to different configured recipients without changing each other's delivery. | 4, 5 | | #5018 | Separate new-Form submissions produce separate Gmail conversations; retries retain the same submission reference. | 5a | | #3891 | Every selected query value is available in new and legacy Form Resource bindings through `system.search`, with a single value remaining a string and repeated values becoming an ordered array. | 2a | - [x] After PR #6530 merged (`a49a6769bd`), verified the new Form needs no port: React Router and Remix use the same Form submit path, which creates fresh `FormData` and calls `set` once for the bot field on each request. `generates one fresh bot field for each managed submission` passes (shared browser suite 7/7); Router managed Form tests pass 2/2, Router refresh tests 7/7, and Remix refresh tests 5/5. # Related issues Closes #3023 Closes #5512 Related #4093 Related #5432 Closes #5499 Closes #3530 Closes #5428 Closes #5658 Closes #6487 Closes #4414 Closes #4385 Closes #5018 Closes #3891 Related #6520 — [Multi-step Forms with resumable local drafts](https://github.com/webstudio-is/webstudio/issues/6520) The existing Webhook Form bug #6437 and prior legacy-form fix #6436 are outside this new-Form feature and are not claimed as closed here. The old email planning PR #6486 is superseded by this description. - [x] **Use the canonical server token for Preview email:** The user confirmed the canonical `TRPC_SERVER_API_TOKEN` is supplied from Infisical in Preview and Production. Builder Preview now sends this token to Email Service `/v1/preview-send`; the redundant `EMAIL_SERVICE_PREVIEW_TOKEN` wiring is removed. Focused route tests verify authorization, missing-token behavior, webhook-only behavior, and redaction. - [x] **Defer validation for a new Resource URL:** Untouched blank URLs are not marked invalid until save; edited invalid values validate on blur. Regression tests pass. - [x] **Handle safe public Resource redirects:** Public GET/HEAD redirects are revalidated and allowed without forwarding credentials; unsafe destinations and cross-origin body requests remain blocked. Protected-fetch tests pass. ## Form component identity correction (2026-10-11) - [x] Keep component picker labels and compatibility behavior clear: show the new component as **Form (Beta)**, and keep the existing **Webhook Form** available in its current Data category through the next release. Update the guide for the 10-Action limit and Navigation mode when Actions are empty. Builder component-list and registry template tests pass. - [x] Use one canonical managed component identity, `Form`, for Resource submission and Navigation mode. Keep `WebhookForm` only as the legacy component through the next release, and keep generic `Element` with tag `form` for native browser behavior. Update exports, metadata, templates, adapters, migrations, docs, CLI/MCP discovery, and E2E identifiers; verify saved projects migrate without changing legacy Webhook Forms. Project migration tests pass (25/25), and the CLI prebuild suite verifies saved bundles are migrated before rendering and endpoint generation. ## Framework-neutral Form core (2026-10-11) - [x] Move managed Form contracts, browser submission behavior, and server delivery into SDK Form entries. Keep React hooks/rendering in the React adapter and router refresh timing in the router adapter; preserve compatibility exports. Builder and CLI consume the dedicated server entry. StrictMode, cancellation, redirect fallback, and refresh-failure behavior have regression tests. SDK build and published types, SDK/React/router/CLI/Builder typechecks, focused SDK/React/router/CLI tests, generated route tests, and the browser bundle boundary check pass. Astra Medium reviewed the architecture and final diff; no remaining findings. ## SDK package boundaries (2026-10-11) - [x] Document each SDK package’s architectural role and dependency constraints. Added root READMEs for `sdk`, `sdk-cli`, and `sdk-components-registry`; clarified the existing React SDK, React component, Radix, Router, Remix, and animation package READMEs. Formatting and diff checks pass. ## Form submission lifecycle - [x] **Handle rejected submissions when the controller refuses them:** Consume abort-aware submission promises when a duplicate or disposed Form submission is refused. Regression tests cover both paths; four lifecycle tests, SDK typecheck, and diff checks pass. ## Unified Canvas selection reveal (2026-10-12) - [x] Reduce selection reveal metadata and resolver ceremony while preserving occurrence validation, owner/source boundaries, and focused coverage. Replaced ten one-element rule arrays with one optional rule each, removed the unused fallback field and redundant source union, combined selector validation with path construction, and bounded the source search. The optimization removes 24 production lines and 10 test lines (net), with the shared resolver and derived override layer intact. Focused Builder tests pass 66/66, Radix 20/20, registry 1/1; SDK, Radix, registry, and Builder typechecks, scoped lint, formatting, and diff checks pass. The nested Radix portal check uses a rendered harness rather than the full Canvas. Independent Astra Medium re-review found no correctness findings. Pushed commit `5bc392d3788ef004f9a35c9a12d82dab6087053a`; `builder-pr/whr.staging` SHA verified. - [x] Replace duplicated Radix Navigator selection handlers with declarative component metadata and one Builder-owned, occurrence-aware selection override resolver. Native Select, HeadSlot, and AnimateChildren hooks remain for their separate editor behavior. - [x] Reveal selected `ws:show=false` instances and ancestors through resolved prop overrides, including Form success/error feedback, while preserving normal published rendering. - [x] Resolve fixed `open` and effective item/tab `value` rules for Dialog, Popover, Tooltip, Select, Collapsible, Tabs, Accordion, and Navigation Menu. DialogOverlay retains its existing reveal behavior. Preserve unrelated memory props and Preview behavior. - [x] Verify resolver, lifecycle, nested Dialog portal mounting, Collection occurrence isolation, authored expression updates, and Canvas prop behavior. Local focused Builder suites pass 66/66; Radix 20/20; registry 2/2. SDK, Radix, registry, and Builder typechecks, scoped lint, formatting, and diff checks pass. Independent Astra Medium review findings were fixed and rechecked. Pushed commit `f8bb1078195e17704ed856c4334683584e983485`; `builder-pr/whr.staging` SHA verified. These are local automated checks, not live UI verification. ### Remaining Navigator hook migration - [x] Migrated every remaining Navigator hook consumer: native Option/Select and tagged `ws:element` use effective option values and HTML text fallback; HeadSlot uses a fixed self override; AnimateChildren uses a fixed override only for its exact selection. - [x] Removed Navigator hook implementations, `Hook`/`HookContext`/`InstanceData`/`InstancePath` and `getClosestInstance`, hook subscriptions and store, library registration, package hook entrypoints/build inputs, and the stale catalog hook capability field. - [x] Added behavior coverage for evaluated/bound native values, empty/zero values, HTML whitespace/NBSP fallback, repeated occurrences, nearest owner, stale selection, HeadSlot rendering, exact animation selection, Preview, deselection, and no saved-prop/memory mutation. Existing Radix runtime index behavior remains covered. - [x] Completed Astra Medium self-review and independent Astra High review. Fixed the High review finding: native fallback values must preserve non-ASCII whitespace. - [x] Final repository search found no remaining Navigator hook consumers or registrations. Validation: - `pnpm --filter @webstudio-is/builder exec vitest run app/shared/nano-states/selection-reveal.test.ts app/shared/nano-states/selection-reveal-render.browser.test.tsx app/shared/nano-states/components.test.tsx app/shared/nano-states/props.test.ts app/shared/nano-states/instance-selection.test.ts app/canvas/instance-selection-events.test.ts app/canvas/selected-instance-effects.test.ts` — 7 files, 84 tests passed; the final resolver rerun passed 14 tests after adding one additional authored-expression regression (85 tests across the same files). - `pnpm --filter @webstudio-is/project-build exec vitest run src/runtime/component-catalog.test.ts src/runtime/component-templates.test.ts` — 16 tests passed. - `pnpm --filter @webstudio-is/sdk-components-react-radix test` — 20 tests passed. - `pnpm --filter @webstudio-is/builder exec tsc --noEmit` — passed. - `pnpm --filter @webstudio-is/sdk --filter @webstudio-is/react-sdk --filter @webstudio-is/sdk-components-react --filter @webstudio-is/sdk-components-animation --filter @webstudio-is/sdk-components-registry --filter @webstudio-is/project-build typecheck` — all 6 packages passed. - `pnpm exec oxlint --deny-warnings` on all changed TypeScript files, `pnpm exec oxfmt --check` on all changed files, and `git diff --check` — passed. Native HTML options use the browser's effective value semantics (authored value or text); their runtime has no numeric index default. Radix items continue using their existing runtime index fallback. All three consumers migrated; no hook mechanism remains. |
||
|
|
a4c3b37fc2 |
fix: Unblock reported MCP workflows and improve issue diagnostics (#6438)
## What this fixes
MCP authors can reuse an existing design token in JSX fragments, create
valid border shorthands with CSS variables, and bypass a font that never
settles during responsive screenshots. Common fixed page titles with
spaced separators are also accepted. Issue reports now include the
project ID and safe, bounded IDs from the failed operation, plus clearer
response and browser-startup diagnostics.
## Scope and tradeoffs
- `token("name")` attaches an existing design token; an unknown name
returns a not-found error. Empty token definitions from Builder/UI
remain definitions and follow the normal conflict rules.
- Border shorthands map a single unresolved `var()` to the remaining
width, style, or color field when the other fields are explicit.
- `waitForFonts: false` skips font readiness but still waits for stable
layout frames. The default remains unchanged.
- Fixed titles such as `Pricing - Plans` stay literal. Addition such as
`first + second` remains an expression. Wrap ambiguous subtraction in
parentheses, for example `(first - second)`. Other ambiguous title forms
may still need an exact report.
- Only recognized operation-level IDs are attached to a public GitHub
issue. Authored values, raw arguments, URLs, and credentials are
excluded. IDs from later successful diagnostic reads are not captured.
- This does not fix the underlying response-transform failure; it
improves future reports for that case.
Closes #6424
Closes #6425
Closes #6434
Closes #6439
Ref #6435
Ref #6430
Ref #6417
## Verification
- [x] Reproduced the privacy, page-expression, empty-token, and
content-mode token-reference regressions with failing tests, then
confirmed the fixes pass. A regression test also covers parenthesized
subtraction.
- [x] Full protocol, template, vision, project-build, and CLI test
suites pass; focused Builder issue-report and fragment tests pass. The
protocol, CLI, and project-build suites were rerun after the CI fix.
- [x] Relevant package typechecks, lint, package-boundary check, fixture
pipeline, generated API check, and generated docs check pass. Fixture
output has no diff.
- [x] Reviewed the authoritative MCP documentation and regenerated the
API metadata and MCP docs.
- [x] CSS parser regressions cover border, each side shorthand, and
unresolved variables in width, style, and color positions; css-data
tests, typecheck, and lint pass.
- [x] Fixed the local evaluation runner to pass the task and Webstudio
MCP tools to Luna/low. Each of the six fixtures passed at least once;
focused MCP/evaluator tests, CLI and project-build typechecks, lint, and
formatting pass.
- [x] The complete Luna/low suite passed all six fixtures in two
consecutive sequential runs. Baselines remain incompatible with this
model/effort override and were not updated.
## Follow-up
- If another fixed-title form still fails, use the new project-level
report context and the exact title shape to narrow the parser case.
|
||
|
|
39c290bfa7 |
fix: resolve MCP-reported runtime and component issues (#6362)
## Summary - harden MCP runtime behavior around asset uploads and resources, HTTP responses, component and Content Block application, data reads, and Chromium screenshots - reject shared Slots inside Content Block templates and avoid unsafe unused-asset findings when Content Blocks can hide MDX dependencies - expand named grid-area values according to CSS shorthand fallback rules - add a consent-safe YouTube preconnect control and only warm origins that are actually used - add focused and end-to-end regression coverage for the reported paths ## Verification - project-build: 2,419 tests passed - css-data: 4,623 tests passed, 1 skipped - sdk-components-react: 134 tests passed, 1 skipped - high-impact CLI fixture: 7 tests passed - Builder asset REST service: 22 tests passed Closes #6336 Closes #6339 Closes #6353 Closes #6354 Closes #6355 Closes #6356 Closes #6357 Closes #6358 Closes #6359 |
||
|
|
e69a3d2f8e |
feat: Create structured MDX entries from collection folders (#6317)
## Paragraph editing follow-up — |
||
|
|
05706b5425 |
feat: Make Content Block templates the MDX authoring interface (#6307)
Closes #6306 ## Outcome Content Block templates are now the public component model for MDX authoring. - Standard Markdown resolves through matching Content Block templates. - PascalCase JSX resolves to a named template first, then to a registered component. - Adding or changing a template immediately affects matching MDX, including content authored before that template existed. - Every supported standard MDX element is present in new Content Blocks by default. - Template `name` is a stable PascalCase JSX identifier. `label` remains optional display text only. - Lowercase JSX represents native HTML and SVG without exposing `ws.element` in authored syntax. - Unknown, ambiguous, and unsafe JSX stays in source, produces a source-ranged Builder diagnostic, and is omitted from published output. - Frontmatter images preserve exact `$ref` values and use the Asset description for `alt` text. - Keyboard and mouse insertion from the slash menu remove the trigger slash and preserve the selected template. ## Public MDX contract - Markdown is canonical for standard prose. - Lowercase JSX creates native elements: `<section><p>Text</p></section>`. - PascalCase JSX uses templates and registered components: `<PromotionCard />`, `<Heading tag="h1">Title</Heading>`, and `<YouTube src="https://youtu.be/KI5JHpzBK1s" />`. - Component-name collisions use stable qualified identifiers such as `Checkbox` and `RadixCheckbox`. - A self-closing template keeps its designed default subtree. An explicitly paired empty template clears it. - Static quoted values and bare booleans are supported. - Imports, exports, expressions, spreads, functions, executable JavaScript, unsafe attributes, `<$.*>`, and authored `<ws.element>` are rejected. - `ws` remains only for compiler primitives with no React component. ## Implementation - [x] Add stable, unique template names and keep them separate from labels. - [x] Parse and validate Markdown and JSX through their ASTs with precise source ranges. - [x] Share component identity between template JSX, Content MDX, Builder materialization, reload, copy, and publication. - [x] Resolve templates live after add, edit, rename, reorder, removal, undo/redo, collaboration updates, and reload. - [x] Preserve authored props, children, Asset references, template defaults, and the exact slash-menu template choice. - [x] Make one typed MDX semantic inventory drive default templates and slash-menu eligibility. - [x] Move component-dependent template assembly into the component registry. - [x] Replace production `$`, `animation`, and `radix` JSX namespaces with direct component imports. - [x] Keep only explicit compiler primitives in `ws` template JSX. - [x] Migrate repository templates, fixtures, stories, examples, MCP guidance, CLI guidance, and generated outputs. - [x] Add Builder unit and Playwright coverage for authoring, diagnostics, insertion, synchronization, reload, and publication. - [x] Review and update the authoritative Content Block and generated MCP documentation. ## Compatibility - Persisted project data continues to use namespaced component IDs and `ws:element` for generic HTML elements. No project-data migration is required. - Legacy `<ws.element ws:tag="…">`, `<ws.element ws:name="…">`, `<$.*>`, `<animation.*>`, and `<radix.*>` input remains readable during the compatibility window. - Current serializers and repository-owned sources emit only canonical Markdown, lowercase intrinsic JSX, or PascalCase identifiers. - Untouched user MDX is not rewritten automatically. Legacy syntax is canonicalized when its owning source is edited. - Deferred follow-up: remove legacy namespace readers and proxy support only after the canonical writer is deployed and existing projects are backfilled. This is intentionally outside this PR. ## Verification - [x] CI package tests, typechecks, lint, package boundaries, generated assets, migrations, and size checks pass. - [x] All six Builder E2E shards pass. - [x] Fixture checks pass on Ubuntu and Windows. - [x] Targeted local Content Engine, Template, SDK, registry, Project Build, migration, Builder, component, CLI, and Playwright tests pass. - [x] Runtime contracts, CLI docs, and MCP docs were regenerated from their owning sources. - [x] Local fixture generation completed without an unexpected fixture diff. - [x] Documentation impact reviewed and included in this PR. - [x] Review and approve the intentional List visual snapshot change. - [x] Run the complete agent evaluation suite after explicit approval. - [x] Run the network fixture link/sync pipeline against the matching PR deployment after explicit permission. ## Known visual change The generated List story now targets its actual default `ul` element instead of `ol`, so the default list styling appears in the visual snapshot. The visual change was reviewed and the approved visual check passes. |
||
|
|
8718feb465 |
test: simplify browser testing infrastructure (#6271)
## Summary Unifies frontend testing around three intentional boundaries: - Vitest Node projects for pure and server tests - Vitest Browser Mode with Playwright/Chromium for DOM and component tests - Playwright Test for Builder end-to-end workflows The Builder no longer uses jsdom. Ordinary E2E workflows use an automatically managed isolated `page` fixture; explicit contexts remain only for simultaneous identities and suite-scoped setup. ## Technical choices - Split Builder tests into one Node project and independent browser projects so each Chromium/Vite process has a bounded workload. - Added shared Browser Mode configuration for the affected SDK React packages. - Preserve the Builder's production top-window bridge ownership; Browser Mode uses explicit test-only installation seams. - Centralized Playwright context options and kept action/navigation timeouts bounded. - Validate Playwright filters before Docker, migrations, browser installation, or Builder builds start. - Preserve the first failed E2E attempt's report, traces, screenshots, and videos when CI retries a shard. - Split static checks, typechecks, CLI tests, core package tests, browser package tests, and Builder projects into parallel CI jobs with five-minute hard ceilings. - Distribute Builder E2E tests across ten isolated shards with two workers; parallel suites create unique per-worker setup data. - Reuse color contrast DOM/canvas samples so the exhaustive theme sweep takes about four seconds instead of timing out at 30 seconds. - The removed cost is jsdom behavior drift, oversized serial check jobs, and unnecessary E2E startup for empty selections. Real browser startup and E2E environment setup remain. ## Todo - [x] Split Builder pure/server tests from browser/component tests. - [x] Migrate Builder DOM tests from jsdom to Vitest Browser Mode. - [x] Migrate affected SDK React DOM tests to the shared Browser Mode setup. - [x] Remove jsdom annotations, dependencies, and compatibility setup. - [x] Replace ordinary manual E2E contexts with the isolated `page` fixture. - [x] Keep explicit contexts only for concurrent identities and `beforeAll` setup. - [x] Preserve setup authentication isolation from workflow pages. - [x] Add bounded Playwright action and navigation timeouts. - [x] Preserve first-attempt E2E failure evidence across CI retry. - [x] Reject empty E2E selections before expensive environment setup. - [x] Parallelize repository checks into bounded CI shards. - [x] Isolate parallel E2E setup data across workers. - [x] Remove the color contrast sweep bottleneck. - [x] Update E2E contributor documentation. - [x] Review product documentation impact; no user-facing product docs are affected. - [x] Run the full repository tests and typechecks. - [x] Run formatting, lint, package-boundary, generated-file, and fixture checks. - [x] Complete final correctness and simplification review. ## Verification - `pnpm --filter @webstudio-is/builder test` - `pnpm -r typecheck` - `pnpm lint` - `pnpm check:package-boundaries` - `pnpm check:generated-api` - `pnpm check:generated-docs` - `pnpm fixtures` - Design-system: 222 tests pass locally. - Builder: all seven Vitest projects pass locally. - The formerly slow E2E shard passes locally in 1m55s. - Full database-backed Builder E2E execution passes in CI. - All CI jobs pass; the slowest completed in 4m58s. - No fixture inputs/outputs or generated files changed. Ref #6270 |
||
|
|
9e504037fb |
fix: improve MCP reliability across project workflows (#6222)
## Summary Fixes the reproducible MCP, preview, redirect, audit, mutation, and asset-upload failures from the latest issue batch. Historical and non-reproducible reports were investigated without speculative fixes. Visual verification is now opt-in unless the user explicitly requests it. ## Issue checklist - [x] #6207 — report tested recovery when an explicitly selected browser fails to start. - [x] #6208 — accept generic and registered buttons as valid label targets without requiring a separate form label. - [x] #6209 — reject unknown style coordinates such as `breakpointId` and document the supported `breakpoint` field. - [x] #6210 — traced Node 18 access to CLI 0.293.0 predating the launcher guard; closed as already fixed by #6201. - [x] #6211 — verified the supported launcher blocks the reported Node 18 crypto path; closed as already fixed by #6201. - [x] #6212 — give sequential responsive captures the sum of their per-capture timeout budgets. - [x] #6213 — keep preview install, build, and server subprocesses on the parent CLI Node runtime. - [x] #6214 — add safe confirmation and diagnostics for ambiguous mutation responses; this does not claim to remove upstream latency. - [x] #6215 — confirmed as the same historical Node 18 release path as #6210; closed as already fixed by #6201. - [x] #6216 — verified malformed input returns structured errors on current main; closed as not reproducible and improved future failure diagnostics. - [x] #6217 — add transaction-id confirmation for large fragment mutations after ambiguous 5xx responses; this does not make the operation asynchronous. - [x] #6218 — verified the existing DevTools reconnect fix from #6205; closed as already fixed. - [x] #6219 — preserve the incoming pathname and query when a redirect target supplies only a hash. - [x] #6220 — return ordered `uploaded`, `failed`, and `ambiguous` results so partial asset successes are not lost or retried unsafely. - [x] #6221 — verified parser recovery without a Node crash; closed as not reproducible and improved future JSX failure diagnostics. - [x] #6223 — verified browser-session reuse, reconnect, and cleanup; closed as already fixed by #6205 after the reported CLI version. - [x] #6224 — expose both text-asset sources and encode the exactly-one requirement in the standard MCP schema. - [x] #6225 — could not reproduce the project-specific upstream timeout; closed with safe confirmation coverage and improved structured diagnostics, without claiming a latency fix. - [x] #6226 — could not reproduce the project-specific upstream timeout; closed with safe confirmation coverage for insert-fragment, without adding a speculative fragment-specific fix. - [x] #6229 — reproduced the reported two-process sequence with the current local CLI and a real browser; both captures passed, so the 0.293.0 report was closed without a speculative fix. - [x] Require explicit user consent before preview, screenshots, diffs, OCR, or rendered visual audits unless visual verification was requested. ## Technical notes - Ambiguous mutation responses are confirmed only with the same idempotent transaction id. If confirmation is also ambiguous, the session reports `AMBIGUOUS_COMMIT`, refreshes state, and never replays a fresh mutation. - CLI error mapping retains the original remote error as a non-enumerable cause, so JSON gateway status reaches the ambiguity classifier without exposing raw remote context in serialized diagnostics. - Forced asset uploads are not automatically retried after an unknown/network/5xx outcome. Batch results retain the original input index, including duplicate filenames. - Stable issue codes and constraints are retained for diagnostics. Arbitrary validation paths, messages, argument values, project content, and raw contextual data are excluded. - The MCP handshake remains the source of tool input schemas; goal-specific guides avoid duplicating those contracts and reduce model-context cost. - `update-asset-content` now exposes `path` and `content` with a machine-readable `oneOf` constraint, matching the runtime rule that exactly one source is required. - Redirect targets with their own pathname or query still replace those request components. - Generated runtime contracts, CLI documentation, product documentation, and deployment fixtures were regenerated from their sources. ## Verification - [x] Three independent review/test agents exercised visual consent and preview behavior; mutation, asset, and privacy behavior; and accessibility, styles, and redirects. - [x] `pnpm checks` passed after the final transport follow-up: all workspace tests and typechecks, lint with warnings denied, package boundaries, generated API contracts, generated docs and documentation checks, and the complete fixture link/sync/build pipeline. - [x] Changed-package suites passed, including Project Build (2,125 tests), HTTP client (42 tests), CLI, Protocol, and Builder. - [x] Complete `pnpm evaluations` run on the preceding implementation commit: all five real-world fixtures and every behavioral check passed. Four baseline comparisons passed; one font-assets sample exceeded only its model-output token allowance, and its isolated rerun passed. Evaluations were not rerun after the final transport and schema follow-ups because fresh approval is required. - [x] Design evaluation MCP response bytes improved from the 39.8 KB baseline to 37.6 KB after removing duplicated guide contracts. - [x] Documentation impact reviewed; generated MCP docs and multi-file upload guidance are updated. ## Related issues Closes #6207 Closes #6208 Closes #6209 Ref #6210 Ref #6211 Closes #6212 Closes #6213 Ref #6214 Ref #6215 Ref #6216 Ref #6217 Ref #6218 Closes #6219 Closes #6220 Ref #6221 Ref #6223 Closes #6224 Ref #6225 Ref #6226 Ref #6229 |
||
|
|
5ec3547d36 |
fix: Support JSON content files in Builder (#6168)
## Summary - make the complete asset filename editable in Asset settings - convert existing editable text assets when their extension changes while preserving the asset ID - accept every JSON root type and normalize JSON-compatible syntax into formatted strict JSON - expose the same optional extension change through MCP/API `update-asset-content` - initialize new JSON files and empty text-to-JSON conversions with a valid empty object - distinguish browser URL imports from JSON file uploads so JSON objects with a root `url` field remain file content - update the Assets and Content Engine documentation ## Root cause Asset settings rendered the extension as a fixed suffix outside the name input. The metadata rename mutation could only change the display basename, while content revisions always inherited the current storage extension. As a result, changing `.md` to `.json` was impossible in the UI and unavailable through the shared MCP/API revision command. Asset settings now edits the complete filename. Ordinary renames keep using the metadata mutation. A text-extension change loads the current content, validates it for the target format, and writes a new immutable revision through the existing conflict-safe content endpoint. The existing Acorn-backed `parseJsonExpression` utility accepts JSON-compatible syntax without evaluating code and serializes it as strict JSON. Objects, arrays, strings, numbers, booleans, and `null` are all accepted. ## Checklist - [x] Create JSON files by entering a `.json` filename - [x] Edit the complete filename in Asset settings - [x] Convert editable text-file extensions while preserving the asset ID - [x] Expose extension conversion through `update-asset-content` - [x] Create JSON files and convert empty text files with a valid empty object - [x] Normalize safe JSON-compatible syntax to strict JSON - [x] Accept objects, arrays, and scalar JSON roots - [x] Reject executable or incomplete content - [x] Preserve browser-uploaded JSON object shapes - [x] Review and update authoritative Builder documentation - [x] Add focused UI, revision, HTTP-client, CLI/MCP, protocol, and upload regression tests - [x] Run affected typechecks, lint, documentation checks, and the Builder production build - [ ] Manually verify create, edit, rename `.md` to `.json`, reload persistence, and Content Engine query in a browser - [ ] Run the complete local agent evaluation suite if approved ## Verification - 217 focused tests passed across Builder, asset revisions, HTTP client, CLI/MCP, and protocol - Builder, asset-uploader, HTTP-client, protocol, CLI, and project-build typechecks passed - Builder production build passed - MCP and Content Engine documentation checks passed - `oxlint` on changed TypeScript files passed - `git diff --check` passed - Regression tests were demonstrated failing before the implementation and passing afterward A disposable E2E database and PostgREST stack bootstrapped successfully in the earlier real-path test, but UI automation remained blocked because no controllable browser was available. Fixture inputs and generated fixture outputs are not affected. Agent evaluations were not run because approval is required and was not requested. Closes #6167 |
||
|
|
dbfb0a9849 |
fix: Preserve variable font styles (#6153)
## Summary Preserve font style metadata for variable fonts so upright and italic files from the same family produce distinct `@font-face` rules. Variable fonts with an `ital` or `slnt` axis are also registered for the matching CSS styles. ## Technical choices - Detect `normal`, `italic`, and `oblique` from the font binary, preferring OS/2 `fsSelection`, then `post.italicAngle`, then the existing subfamily parser. - Add `style` to variable-font metadata while defaulting legacy stored records to `normal`. - Keep update metadata partial and strict, but accept and persist `style` and `variationAxes` together so a `get-asset` result can be written back. - Group generated font faces by family, style, and browser-selectable variable descriptors so separate upright and italic files cannot overwrite each other. - Regenerate the runtime operation contracts from the updated schema. ## Checklist - [x] Add regressions using real upright and italic Inter variable-font binaries. - [x] Cover style detection, metadata compatibility, updates, and generated font-face selection. - [x] Verify the final stylesheet contains distinct upright and italic `@font-face` rules. - [x] Verify the public asset update contract accepts a complete variable-font metadata object. - [x] Review documentation impact and update the custom-font guide. - [x] Regenerate and verify runtime API contracts. ## Verification - `@webstudio-is/fonts`: 14 tests and typecheck passed. - `@webstudio-is/asset-uploader`: 198 tests and typecheck passed. - `@webstudio-is/sdk`: 514 tests and typecheck passed. - `@webstudio-is/project-build`: 2,094 tests and typecheck passed. - CLI: 980 tests and typecheck passed. - Builder typecheck passed. - `pnpm lint`, `pnpm check:package-boundaries`, `pnpm check:generated-api`, and `pnpm check:generated-docs` passed. - Regression tests were demonstrated failing against the previous behavior and passing with the fix. - Fixture builds regenerated successfully with no diff. Full remote fixture sync was attempted, but the deployed API rejected the source CLI contract as incompatible. - Agent evaluations were not run because they require separate explicit approval. Closes #6150 |
||
|
|
87968f40ff |
fix: Improve MCP authoring and preview reliability (#6105)
## Summary
Fix MCP reliability failures reported in ten issues against CLI 0.288.0
and 0.289.0:
- keep every advertised array input schema client-valid, including
compact and tuple schemas
- report a text asset update as successful when its persisted revision
and bytes confirm that the server committed despite a failed response
- preserve standard package import resolution in generated React Router
previews
- upload variable WOFF2 fonts when callers provide the documented static
font metadata shape
- explain unmatched JSX as a stateless fragment syntax error
- distinguish the `animation` component namespace from value helpers and
reject unsupported CSS at-rules with an actionable validation error
- forward optional session tools such as `download-asset` through
top-level CLI shortcuts
- isolate published generated-preview dependencies from parent
workspaces
- return structured screenshot failures when React Router production
builds fail
Sensitive visual regression checks also run automatically for
organization-authored fork PRs. External fork PRs remain gated by
`safe-to-deploy`.
## Root causes and implementation
The MCP handshake compactor retained an array's type but discarded its
`items` schema. Unconstrained and tuple arrays could also be advertised
without `items`. Schema normalization now adds `items: {}` whenever an
array omits it, while focused discovery retains complete constraints.
Catalog coverage rejects every missing array item, including tuple
schemas, and the handshake remains below its 200 KB budget.
Asset content updates previously treated any failed response as a failed
mutation, even when the server had already swapped the revision.
Recovery now reads the current revision, compares the persisted bytes,
and confirms the revision remains stable across the read. It returns the
updated asset only when all evidence agrees. Otherwise it returns
`ASSET_UPDATE_COMMIT_UNCERTAIN` with guidance not to retry blindly.
Definitive 4xx responses retain their original errors.
The generated React Router Vite config replaced the client and SSR
resolver condition lists without explicitly retaining the standard
`import` condition. Both lists now preserve `import`. The regression
test generates a dynamic route, stages the built React Router SDK as a
published dependency without the local `webstudio` source condition, and
runs the generated build.
Variable fonts are detected with `variationAxes`. Merging the documented
static `style` and `weight` upload metadata into that detected variable
shape made validation fail after the binary was read. Font override
validation and shape-aware merging now live in `@webstudio-is/fonts`.
The SDK delegates to that domain function, which validates the complete
override before applying compatible fields, retains the detected
static-or-variable shape, and keeps the detected format.
Fragment parsing is stateless. Unmatched opening and closing tags now
explain that every opening element must use the same closing tag or be
self-closing, and that refreshing the session cannot repair malformed
JSX.
The `animation` binding is a component namespace, not a callable helper.
Tool schema text, evaluation errors, and the MCP manual now separate
component namespaces from value helpers. The MCP JSX `css` binding
parses the final interpolated CSS with CSS-tree and recursively rejects
selectors and unsupported at-rules such as `@keyframes`, while
preserving supported declarations and case-insensitive `@media` rules.
Keeping this validation at the external binding avoids changing internal
component templates.
CLI shortcut discovery previously omitted optional session tools even
though the runtime registered their handlers. Shortcut discovery now
includes the same optional tool categories as the CLI MCP host, while
real top-level CLI commands keep precedence.
Published generated previews could reuse a stale `node_modules` symlink
into a parent source workspace. Only development manifests containing
the local placeholder version may now reuse the CLI dependency tree.
Published manifests replace symlinks with a local install and disable
npm workspace discovery.
React Router could report a bundler failure while its build command
exited successfully outside CI, allowing screenshot startup to terminate
without a structured result. Managed production builds now set `CI=1`,
so the existing MCP error boundary receives the nonzero build failure
and prints structured JSON.
Visual regression authorization now distinguishes repository members
from external contributors. Organization members' fork PRs run on open,
synchronize, and reopen events; external fork code runs only from a
`safe-to-deploy` label event, and a later commit does not reuse that
approval.
## Implementation checklist
- [x] Preserve valid array item schemas in compact MCP discovery
- [x] Normalize tuple input schemas with an `items` fallback
- [x] Keep the MCP handshake below its 200 KB size budget
- [x] Cover the real `update-props` schema and oversized array
compaction
- [x] Reconcile failed asset-update responses against stable persisted
content
- [x] Reject recovery when a concurrent asset revision is detected
- [x] Preserve `import` resolution in generated React Router client and
SSR builds
- [x] Build a generated dynamic route against a staged published SDK
package
- [x] Regenerate and verify affected React Router fixtures
- [x] Preserve detected variable-font axes with documented upload
metadata
- [x] Add actionable unmatched-tag and unsupported-CSS validation
- [x] Correct animation namespace guidance in tool discovery and errors
- [x] Forward optional session tools through top-level CLI shortcuts
- [x] Replace parent-workspace dependency links for published previews
- [x] Disable npm workspace discovery for isolated preview installs
- [x] Make React Router build failures return nonzero in managed
previews
- [x] Run visual regression automatically for organization-authored PRs
- [x] Verify each regression test fails without its corresponding fix
- [x] Review documentation impact and update the authoritative MCP
manual plus generated outputs
- [x] Confirm no database migration or compatibility migration is
required
- [ ] Run `pnpm evaluations` after explicit approval; not run because
repository policy requires approval before agent evaluations
## Verification
- `packages/cli/src/prebuild.test.ts`: 57 tests passed
- `packages/cli/src/commands/schema.test.ts` and `mcp.test.ts`: 51 tests
passed
- `packages/project-build/src/runtime/components.test.ts` and
`mcp.test.ts`: 194 tests passed
- `packages/asset-uploader`: 196 tests passed
- focused font schema, SDK asset schema, CSS parser, and template CSS
tests passed
- `packages/http-client/src/index.test.ts`: 38 tests passed
- relevant CLI, project-build, asset-uploader, fonts, SDK, CSS data,
template, HTTP client, and React Router SDK typechecks passed
- MCP documentation tests, typecheck, generation, and Markdown source
tests passed
- React Router SDK package build passed
- full fixture link, sync, and generation pipeline passed
- affected Docker and Vercel fixture typechecks and locally bootstrapped
production builds passed
- complete CLI suite: 61 test files and 966 tests passed
- CLI typecheck passed
- preserved nested-workspace dynamic preview installed local `0.289.0`
dependencies and completed its production build
- visual-regression workflow YAML parsing and authorization truth table
passed
- repository lint passed with warnings denied
- formatting and `git diff --check` passed
## Tradeoffs and limitations
Deferred array schemas advertise unconstrained `items` in the compact
handshake to remain client-valid without duplicating detailed
constraints. Agents can retrieve the complete item schema through
focused `meta.get-more-tools` discovery.
Asset reconciliation performs three read requests only after an
uncertain 5xx, malformed response, or transport failure. This keeps the
normal successful path unchanged and avoids claiming success if the
revision changes during recovery.
For variable fonts, detected axes define the metadata shape. Static
`style` and `weight` hints are ignored because they cannot be
represented alongside `variationAxes`; compatible fields such as
`family` still override detection.
Native CSS keyframe authoring remains unsupported in fragment `css`
templates. The tool now rejects it before rendering and directs callers
to editable Webstudio Animation components instead of advertising a
nonexistent callable helper.
Published previews perform one isolated npm install when no valid local
dependency tree is cached. Local source development keeps the fast
workspace symlink path for placeholder-version manifests.
Closes #6104
Closes #6099
Closes #6098
Closes #6106
Closes #6107
Closes #6108
Closes #6114
Closes #6115
Closes #6116
Closes #6117
|
||
|
|
d5b3f3ab80 |
fix: Improve MCP authoring reliability (#6056)
## Summary This consolidates the reported MCP reliability fixes into one delivery. - validate all inserted fragment expressions with the shared runtime rules and reject text/expression children when component content models require elements - return an actionable error for unsupported existing-resource references in JSX - preserve siblings rendered before expression children - accept unresolved CSS-variable shorthand expansion and eliminate dynamic-alt/`htmlFor` audit false positives - resolve exposed underscore MCP names during focused discovery - omit unavailable stopped-preview fields and restart stale iterative previews for external clients - bound single and responsive screenshot capture, return `SCREENSHOT_TIMEOUT`, and discard failed reusable browser sessions - dispose previews when shared MCP runs fail or stdio clients disconnect, including complete Windows process-tree cleanup - resolve generated route templates from the CLI-managed preview workspace during responsive verification - retry a browser that exits during rendered-audit startup and report sanitized exit diagnostics with a concrete browser-path recovery action - materialize complete current session data before preview generation so route order and project styles cannot collapse from missing lazy namespaces - always include CLI and Node.js version fields in generated issue reports, with explicit legacy-client fallbacks - document the updated preview and screenshot contracts and regenerate the CLI manual The current branch also retains the protections already present on `main` for dry-run isolation, CSS rewrite truncation metadata, stderr-only progress, shortcut argument forwarding, strict MCP schemas, and refreshed conflict diagnostics. ## Technical choices - MCP command orchestration stays in `mcp.ts`; preview port/retry behavior lives with `mcp-preview`, and persisted checkpoint storage lives in `mcp-checkpoint`. Each module owns its matching tests. - Expression validation lives on the shared fragment input schema, reuses `listFragmentExpressions`, and emits the same structured validation issues as prop bindings, so JSX-derived and structured fragments follow one contract across fragment, collection, and runtime UI insertion. - Element-child requirements come from component registry content models during fragment insertion; no component names are hardcoded in the JSX parser. `CollapsibleTrigger` metadata now matches its enforced `asChild` implementation. - Stopped preview status returns only `running: false`; unavailable `url`, `pid`, and `mode` fields are optional and omitted from JSON. - Explicit `preview.start` restarts a stale compatible iterative preview; managed screenshots keep their optimized refresh path. - Screenshot operations share one timeout utility. A single capture or complete responsive batch is bounded after preview readiness; lower-level CDP deadlines use the same stable error code. - Failed managed captures close and forget their browser session. A closed browser session cannot restart itself while cancellation is in progress, which prevents orphaned Chromium processes. - One ownership helper disposes one-shot and multi-project MCP hosts on success or failure. Shared `mcp run` hosts dispose in one `finally` boundary, long-running MCP hosts dispose on connection close, and Windows uses `taskkill /T /F` instead of killing only the npm wrapper. - Framework template readers receive the absolute template directory captured from the build root, so later async work cannot redirect them to the linked project's cwd. The same option drives cleanup for React Router, Remix, and Vike. - Browser startup gets one bounded retry before a rendered capture batch fails. Persistent failures report only the exit code, signal, or spawn error code plus the supported `WEBSTUDIO_BROWSER_PATH` recovery action; local paths and raw browser output are not exposed. - CLI and MCP preview share one session materializer that ensures the canonical Builder namespace set before writing `.webstudio/data.json`. Serialization rejects incomplete snapshots instead of converting missing style/instance namespaces into valid-looking empty arrays, and preview freshness records the post-materialization project version. - The CLI injects its package version and `process.versions.node`; issue formatting now keeps both fields visible even for legacy reports that omit runtime metadata. ## Checklist - [x] Add regression coverage for every changed behavior - [x] Demonstrate new regression tests fail without the corresponding fixes and pass with them - [x] Run affected package test suites and typechecks - [x] Run lint, package-boundary, and generated API checks - [x] Review fixture impact — no fixture inputs or outputs are affected - [x] Review documentation impact and update the authoritative MCP CLI manual source/generated output - [x] Run the complete local agent evaluation suite ## Verification - `@webstudio-is/project-build`: 2,090 tests passed - MCP asset query result modes are preserved across create, read, and update serialization - complete local agent evaluation suite: all 5 fixtures passed, 1,308,276 aggregate tokens - `@webstudio-is/react-sdk`: 140 tests passed - `@webstudio-is/template`: 43 tests passed - `webstudio` CLI: 956 tests passed - issue reporter: 5 targeted tests passed - MCP host disposal regression was demonstrated failing without the shared ownership boundary and passing with it - relevant package typechecks passed - `pnpm lint` passed - `pnpm check:package-boundaries` passed - `pnpm check:generated-api` passed - generated-doc tests, typecheck, sync, and regeneration passed; the aggregate clean-tree command reports the intentionally changed generated CLI manual - stalled single capture, stalled four-viewport capture, startup retry across a four-viewport batch, sanitized browser-exit diagnostics, complete preview materialization, post-refresh version tracking, closed-session restart, Windows process-tree cleanup, and preview-workspace regressions were demonstrated failing without their fixes and passing with them ## Known limitations - External `baseUrl` screenshots remain caller-owned and intentionally do not regenerate, restart, or stop that external server. Closes #6034 Closes #6035 Closes #6038 Closes #6042 Closes #6043 Closes #6044 Closes #6045 Closes #6046 Closes #6047 Closes #6048 Closes #6049 Closes #6050 Closes #6051 Closes #6052 Closes #6059 Closes #6060 |
||
|
|
92084aeea3 |
fix: Remove MCP workflow friction (#5974)
## Summary - Make MCP discoverable from the CLI README and expose direct MCP tool calls as restart-free CLI subcommands. - Preserve required discriminator fields in compact MCP SDK schemas and surface the `set-text-content` contract. - Return instance props by default from `inspect-instance` and improve the `list-instances` input hint. - Add a Builder context-menu **Move** submenu as an accessible alternative to drag-and-drop sibling reordering. Companion generated documentation: https://github.com/webstudio-is/webstudio-community/pull/274 ## Technical choices and compatibility - Compact SDK schemas retain required fields only inside root composition branches, avoiding broad schema expansion. - `inspect-instance` now defaults to `props` only; larger styles, bindings, sources, children, and ancestors remain opt-in. - Existing move commands and shortcuts back the new menu, so command behavior stays centralized. - No migrations, fixture inputs, generated fixture bundles, or generated API contracts are affected. ## Todo - [x] Add direct CLI MCP discovery and restart guidance. - [x] Preserve discriminated-union requirements for SDK clients. - [x] Improve instance inspection and input diagnostics. - [x] Add accessible menu-based instance movement. - [x] Add regression coverage and verify fail/pass behavior. - [x] Run targeted tests, typechecks, lint, package-boundary, and generated-doc checks. - [ ] Deferred: run `pnpm evaluations` after separate explicit approval, as required by repository policy. ## Verification - `@webstudio-is/project-build`: 172 targeted tests passed. - `@webstudio-is/protocol`: 8 targeted tests passed; typecheck passed. - `@webstudio-is/builder`: 8 targeted tests passed; typecheck passed. - CLI and project-build typechecks passed. - Root lint, package-boundary check, generated-doc check, and diff checks passed. - New and modified regression tests were demonstrated failing before their fixes and passing afterward. ## Known limitations Synthetic drag-and-drop behavior is unchanged. The new menu and existing keyboard shortcuts provide the automatable and accessible reordering path. Agent evaluations were not run because explicit approval was not provided. |
||
|
|
f335340a54 |
fix: Correct content database sizing and Markdown references (#5972)
## Summary - Report only serialized content-database bytes that are actually included in the published bundle. - Replace the ambiguous `markdown-body` query mode with the strict `markdown-body-ref` mode. - Require every selected Markdown body to have a document-graph node; compilation now fails instead of embedding source bytes as a fallback. - Apply the same Markdown eligibility rule during compilation and runtime query execution, excluding non-Markdown files from reference-only databases and results. - Highlight database sizes in red only when documents were omitted because of the size limit. - Refresh Assets diagnostics after an Assets resource edit is confirmed by the server. - Update CLI/MCP guidance, examples, schemas, generated contracts, and fixtures to use the reference-only mode. ## Root cause Dynamic Markdown-body queries conservatively treated unrelated files, including remote images, as possible hydration candidates. The old mode could also fall back to embedding a Markdown body when graph resolution was unavailable, despite its intended storage-backed behavior. Compilation and runtime selection did not previously share the same content-mode eligibility check. Diagnostics displayed the unbounded pre-limit estimate instead of the serialized bundled size, did not carry the omission reason into query-preview diagnostics, and an early cached preview could leave the merged measurement stale until reload. ## Impact Content database diagnostics and publish warnings now reflect bundled database bytes rather than remote source-file sizes. Saved query filters trigger a post-sync diagnostics refresh. Markdown detail queries must use `content.mode: "markdown-body-ref"`; the old `markdown-body` value is rejected, missing graph support is an explicit compilation error, and non-Markdown assets cannot be selected or counted by a reference-only query. Ref #5966 ## Verification - Content engine: 39 files / 261 tests passed. - Asset uploader: 22 files / 183 tests passed. - SDK: 29 files / 505 tests passed. - Protocol: 9 files / 86 tests passed. - Project build: 82 files / 2,055 tests passed. - CLI: full suite previously passed (57 files / 933 tests); final prebuild verification passed 48 tests. - Focused Builder diagnostics tests: 4 files / 10 tests passed. - Typechecks passed for Builder, content engine, asset uploader, SDK, protocol, project build, and CLI. - Generated API contracts and docs are current. - Fixture build passed and is reproducible after the final review changes. - Formatting, lint, package-boundary checks, and diff checks passed. - New strict-reference, runtime-selection, compilation-selection, and size-highlight regression tests were demonstrated failing without their implementations and passing with them. - Agent evaluations were not run because this follow-up did not receive separate evaluation approval. |
||
|
|
56a22fde8a |
feat: Add storage-backed document graph resolution (#5966)
## Summary Introduce a document graph architecture for Assets resources so structured documents can reference one another while keeping full document payloads on CDN-backed storage. The content database will retain the searchable index and dependency graph. At runtime, the server will select a root document, determine its complete dependency set before performing I/O, fetch each unique document concurrently, and assemble the final result server-side. ## Implementation todos ### Document graph foundation - [x] Add normalized internal reference contracts for whole documents, JSON paths, Markdown body, and Markdown frontmatter. - [x] Add an immutable graph abstraction with deterministic ordering, dependency traversal, revision validation, and cycle detection. - [x] Add a storage-independent resolver with dependency closure, bounded parallel loading, shared-content deduplication, representation selection, assembly hooks, cancellation, and structured errors. - [x] Add focused unit coverage for the isolated document-graph abstraction. - [x] Define and validate the external remote-document reference syntax. - [x] Add a pure graph compiler that resolves canonical document URLs into IDs and revisions and validates missing, duplicate, and cyclic dependencies. ### Build-time graph - [x] Add a storage-independent source-catalog compiler for bounded parallel reference discovery and deterministic graph compilation. - [x] Add a versioned deterministic document graph artifact with canonical serialization and integrity verification. - [x] Discover and normalize statically declared references while Assets resources are processed. - [x] Resolve document identities and revisions and report missing, stale, or cyclic dependencies with document context. - [x] Persist document nodes and dependency edges alongside searchable resource metadata. - [x] Include persisted graph data in artifact integrity and size validation. ### Document representations - [x] Add a standalone JSON document adapter for exact reference markers, compiler occurrences, JSON path selection, and immutable assembly. - [x] Add a standalone Markdown document adapter for bounded decoding, frontmatter references, immutable assembly, and source, body, and frontmatter selection. - [x] Parse fetched JSON and Markdown documents through format-specific adapters. - [x] Select complete documents, JSON fragments, Markdown body, and Markdown frontmatter through the shared reference model. - [x] Assemble nested and shared references into the final server-side value. ### Runtime resolution - [x] Enforce reachable-document, per-document byte, and aggregate-byte limits before parsing and assembly. - [x] Add a revision-keyed bounded source-cache wrapper with replayable bytes, stale-entry fallback, and in-flight deduplication. - [x] Add a storage-neutral loaded-source contract that rejects revision mismatches before parsing. - [x] Resolve graph-backed property paths before filtering, sorting, pagination, and projection; skip graph loading for unrelated queries. - [x] Add an injected HTTP/CDN document loader with revision metadata validation. - [x] Apply runtime payload limits, bounded concurrency, caching, and structured failure mapping. - [x] Ensure shared documents are fetched and parsed once per resolution. ### Product integration - [x] Integrate equivalent behavior in local preview, SSR, SSG, and published runtime paths. - [x] Preserve existing embedded Assets resource behavior without requiring migration. - [x] Add observability for graph loading, document fetching, caching, and resolution failures. - [x] Document the public reference and resolution behavior. ### Review hardening - [x] Enforce graph, artifact, cache, and published-storage identity consistency. - [x] Compile only graph sources reachable from selected build entries. - [x] Resolve graph-backed fields before query evaluation and avoid unnecessary graph loading. - [x] Consolidate duplicated graph schemas, utilities, and document content-type detection. - [x] Correct runtime documentation and use structural evaluation validation. ### Verification - [x] Keep a deterministic high-impact agent evaluation for Markdown-to-JSON references across the dynamic blog overview and article routes. - [x] Add a separate discoverability evaluation that must learn the reference workflow from MCP guidance without prompt-supplied syntax, queries, bindings, or call order. - [ ] Re-run the complete local agent evaluation suite after the discoverability fixture change and compare every fixture with its accepted baseline. - [x] Add integration coverage for CDN-backed hydration, parallel fetching, caching, and shared dependencies. - [x] Add integration coverage for missing references, cycles, revision changes, limits, and cancellation. - [x] Verify equivalent behavior across preview, SSR, SSG, and published deployments. - [x] Verify that existing embedded Assets resources retain their current behavior. ## Intended outcome - Storage-backed documents do not require their complete payloads to be embedded in the content database. - The server knows the complete statically discoverable dependency set before fetching document content. - Shared dependencies are fetched once per resolution and independent resources can be fetched in parallel. - JSON fragments and Markdown body/frontmatter representations use the same document-resolution system. - Reference and payload revisions cannot be mixed silently. - Existing Assets resources continue to work without migration. ## Verification results - `@webstudio-is/content-engine`: 37 files / 244 tests passed; typecheck passed. - `@webstudio-is/asset-uploader`: 22 files / 182 tests passed; typecheck passed. - CLI: 57 files / 925 tests passed, including generated SSR and SSG build/prerender coverage; typecheck passed. - Repository-wide `pnpm oxfmt --check` passed across 2,757 files. - Package-boundary validation passed. - Previous `pnpm evaluations` run: all 4 original high-impact fixtures passed. The complete 5-fixture rerun is pending explicit approval after adding the discoverability fixture. |
||
|
|
efbc3098c4 |
feat: add queryable Assets resources (#5946)
Closes #5919 Ref #5920 ## Outcome Extend the existing Assets system resource with optional queries for Markdown, JSON, and standard file metadata. Unconfigured Assets resources retain the existing fetch-all behavior and activate none of the content-query infrastructure. Files are the authoring model. A standalone content engine converts an immutable snapshot of only the files required by configured queries into one bounded, deterministic runtime database. Builder preview, SSR, and SSG use the same query semantics without requiring a database service for a published site. ## Final architecture ```text Markdown / JSON files │ ▼ ContentSource adapter (FS or Webstudio Assets) │ immutable snapshot ▼ ContentCompilationPlan (union of configured query requirements) │ ▼ ContentCompiler (parse, normalize, discover fields, enforce budget) │ ▼ ContentArtifact (bounded, deterministic, immutable JSON) │ ▼ ContentDatabase (filter, OR groups, sort, paginate, project, read content) │ ├── Builder preview ├── SSR runtime └── SSG build only ``` The authoring/storage layer owns mutable files. The content engine consumes immutable snapshots and produces immutable, disposable artifacts. Query authoring is a separate OpenAPI-driven path: ```text Assets REST OpenAPI operation + standard request JSON Schema + dynamic field paths │ ▼ Generic OpenAPI adapter │ resolved schemas, controls, paths, and transport mapping ▼ Generic query-builder core and React renderer │ arbitrary provider-defined value ▼ Assets REST request adapter │ ▼ { query: value } ``` The selected standard OpenAPI operation is the only source of query UI semantics. Request parameters and JSON Schema describe the editable data; the generic renderer derives controls and owns layout. ### Activation and compilation - The compiler does not index every asset. It combines all reachable configured Assets-resource queries into one `ContentCompilationPlan`. - Static constraints such as folder, path, and file type select candidate files. Dynamic values such as a route-bound slug cannot reduce the build-time candidate set. - The plan contains the union of fields used by filtering, sorting, routing, projection, excerpts, and requested full content. - Documents shared by multiple resources are stored once in one deployment-wide runtime database. - Markdown/frontmatter, JSON properties, excerpts, and full bodies are prepared only when required. - No configured content query means no parsing, artifact, or shipped runtime dependency. Fresh generated apps need no content-runtime cleanup; incremental generation removes stale integration if a previously configured query is deleted. ### Runtime database limit - `CONTENT_DATABASE_MAX_BYTES` limits the complete serialized runtime database, not source files, images, or unrelated assets. - The default is `500 * 1024` bytes and is presented as 500 KiB. - Counted data includes indexed metadata, frontmatter/JSON properties, excerpts, the field catalog, serialization overhead, and full Markdown bodies only when requested. - Prepublish validation builds and serializes the exact database that will be attached to the immutable build, so it knows the unbounded size, bounded size, included documents, omitted documents, and affected resources. - Overflow never stores partial documents. Candidates are ordered by `createdAt` descending, canonical path ascending, and stable document ID ascending. Missing dates follow dated documents. A document that does not fit is skipped while smaller later documents are still considered. - Exceeding the limit warns but does not block publishing. - Builder preview can report exact bounded versus unbounded results when dynamic expressions have concrete preview values. Prepublish warnings remain conservative for future route values. ### Snapshot consistency The source contract guarantees a consistent compilation, while adapters provide the mechanism: - Webstudio Assets captures asset metadata and immutable content references. - Filesystem sources capture file identity and verify files did not change before accepting the artifact. - A changed source retries once, then fails clearly instead of producing a mixed-revision database. - This requires no universal snapshot service, source-tree copy, or new versioned-asset infrastructure. ### Package ownership - `@webstudio-is/content-engine`: runtime artifact schema, query execution, field catalog, compact runtime statistics. - `@webstudio-is/content-engine/compiler`: source snapshots, compilation plans, Markdown/JSON parsing, deterministic compilation, byte accounting, and compiler diagnostics. This entry exists to keep Remark/YAML/future MDX dependencies out of runtime bundles. - `@webstudio-is/query-builder`: resolves standard OpenAPI operation schemas into an internal provider-neutral editor model, validation, mutations, and source serialization. It owns no Assets, provider-authored UI contract, or fixed REST-query semantics. - `@webstudio-is/query-builder/react`: container-neutral schema renderer whose layout is renderer policy. It remains separate from the content engine. - SDK: Webstudio Assets/Resource domain models and mapping Webstudio expressions into generic content requirements. - Protocol: external Assets REST/OpenAPI transport contracts. - Asset uploader/repository: asset and folder mutations, generic metadata, immutable byte references, and storage access. Its hosted adapter supplies Webstudio Assets snapshots and parsed-metadata cache persistence to the content engine; parsing, artifact creation, and query semantics remain engine-owned. - Builder and generated-app integrations decide when to compile, cache, embed, load, or discard a database. ### Runtime lifecycle - Builder caches immutable `ContentDatabase` instances by source revision plus compilation-plan hash and coalesces concurrent compilation. Matching operations reuse the same instance; field discovery uses a dedicated minimal all-properties plan that excludes excerpts and bodies. - SSR attaches the validated bounded database to the build and includes the small runtime only when required. - SSG uses the database only while generating pages and omits it and the runtime from final output unless a future runtime-query feature requires them. - Local development compiles from the filesystem and uses identical query semantics. - Published pages query their local artifact; they do not call Builder REST endpoints. - Published artifacts need no external index store, invalidation, garbage collection, SaaS content proxy, new bucket, or new environment variable. ## Implemented foundation These existing parts remain useful and should be moved behind the final ownership boundaries rather than reimplemented. - [x] Upgrade the existing Assets system resource in place and preserve unconfigured fetch-all behavior. - [x] Implement typed filtering, nested boolean groups, deterministic sorting, pagination, projections, dynamic values, and content hydration. - [x] Add Markdown frontmatter, excerpt, JSON-property, and field-catalog extraction. - [x] Add reusable query-control implementations and a separately imported React UI as a foundation for the final OpenAPI-driven contract. - [x] Add Assets query authoring and dynamic field discovery as existing behavior to migrate into the Assets OpenAPI operation. - [x] Add synchronized visual and editable expression authoring. - [x] Unify Assets REST operations under `/rest/assets/*`. - [x] Define authenticated collection, item, content, upload, folder, query, field-catalog, and OpenAPI operations. - [x] Support Builder cookie authentication and public API token authentication. - [x] Add Builder preview, MCP/CLI operations, SSR execution, SSG path generation, and conditional activation. - [x] Add revision-keyed parsed metadata persistence for the hosted Builder. - [x] Add focused query, API, authorization, Builder, CLI, SSR, SSG, migration, and fixture coverage. ## Remaining implementation plan ### 1. Establish the content engine - [x] Rename the generic `@webstudio-is/asset-resource` implementation to `@webstudio-is/content-engine`. - [x] Move existing behavior instead of creating a parallel implementation. - [x] Keep the primary entry runtime-only and add only the compiler entry needed for bundle separation. - [x] Remove the old package after all consumers migrate. - [x] Ensure the engine has no Builder, SDK, asset-uploader, PostgreSQL, R2, or deployment-target dependency. ### 2. Define source snapshots and compilation plans - [x] Introduce `ContentSource.openSnapshot()` and a revision-bearing `ContentSourceSnapshot`. - [x] Include canonical path, stable document ID, content type, immutable content reference, size, and optional `createdAt`. - [x] Implement Webstudio Assets and filesystem snapshot guarantees using their adapter-specific mechanisms. - [x] Introduce `ContentCompilationPlan` for the union of reachable configured resource requirements. - [x] Separate statically selectable candidates from dynamic runtime predicates. - [x] Add mutation-during-compilation tests for replacement, deletion, folder movement, and filesystem changes. ### 3. Separate storage from content processing - [x] Keep upload, replacement, folders, byte storage, revisions, and generic metadata in the Assets repository/uploader. - [x] Move parsing, excerpts, field discovery, query execution, and artifact creation into the content engine. - [x] Remove content-index coordination from asset mutation business logic. - [x] Preserve original source `createdAt` across content replacement and expose it as optional queryable metadata. - [x] Keep hosted parsed-document persistence behind the content compiler's optional cache interface. ### 4. Build deterministic compilation - [x] Move the existing Markdown, frontmatter, JSON, canonicalization, hashing, document-building, and tests into the compiler. - [x] Keep frontmatter behind a dedicated module and continue using the existing Remark/Micromark pipeline. - [x] Parse only candidate files and only the tiers required by the compilation plan. - [x] Deduplicate documents and fields required by multiple resources. - [x] Define a versioned, deterministic `ContentArtifact` with integrity information. - [x] Keep verbose parse and omission diagnostics outside the runtime artifact. - [x] Reject incompatible artifact versions cleanly. ### 5. Enforce the deployment-wide database budget - [x] Replace the old asset-index limit with `CONTENT_DATABASE_MAX_BYTES`, defaulting to 500 KiB. - [x] Have applications read configuration and pass the byte limit into the compiler. - [x] Measure the exact serialized database, including selected bodies and overhead. - [x] Implement deterministic skip-and-continue packing of complete documents. - [x] Return compiler diagnostics with unbounded/bounded bytes, included/omitted counts, omitted identities, largest contributors, and affected resources. - [x] Expose only compact artifact statistics at runtime through `ContentDatabase.getStats()`. - [x] Add exact boundary, deterministic ordering, deduplication, and oversized-document tests. ### 6. Finalize the runtime database - [x] Define `ContentDatabase` over an immutable artifact. - [x] Preserve filtering, nested AND/OR groups, sorting, offset/limit, projection, field references, and content retrieval. - [x] Expose `getFieldCatalog()` and compact `getStats()`. - [x] Keep Markdown/YAML/compiler dependencies out of the runtime entry. - [x] Add execution guards and maximum-size runtime benchmarks. ### 7. Make query authoring OpenAPI-driven The selected standard OpenAPI operation is the complete query-authoring contract. The query builder resolves parameters and request-body JSON Schema, derives its internal editor model, and applies renderer-owned layout policy. It must not embed or fetch a second capabilities/UI language through `x-webstudio-query` or another side channel. - [x] Remove `x-webstudio-query` and all vendor query-definition metadata from the Assets OpenAPI operation. - [x] Resolve local OpenAPI references and derive the editable request value, defaults, validation, labels, choices, arrays, objects, and unions solely from standard operation parameters and request-body JSON Schema. - [x] Represent dynamic Assets field paths with standard JSON Schema vocabulary in the `queryAssets` request schema. - [x] Keep the normalized editor representation internal to query-builder; do not expose another provider-authored capabilities contract. - [x] Remove provider-controlled layout metadata such as `group`; make compact scalar packing and all other layout renderer policy. - [x] Keep Webstudio expression binding as a consumer/editor concern layered over schema values, not part of the OpenAPI contract. - [x] Preserve the existing Assets filter, sort, output selection, full-content/range selection, limit/offset, editable source, and configured-field behavior. - [x] Keep the Assets resource adapter responsible for serializing the edited query into its request body. - [x] Remove the obsolete Assets query-capabilities endpoint, URL, and UI-side capability parsing. - [x] Add a non-Assets OpenAPI proof with differently named parameters and no vendor metadata. - [x] Verify the generic packages contain no Assets imports, fixed Assets property contract, or provider-authored layout model. - [x] Update focused core, React, SDK, protocol/OpenAPI, Builder, and source round-trip tests. ### 8. Manage ContentDatabase lifecycle in Builder - [x] Cache databases by source revision plus normalized compilation-plan hash. - [x] Coalesce concurrent compilation requests. - [x] Reuse cached database instances across field discovery, autocomplete, query preview, and response preview when their normalized compilation plans match. - [x] Replace it when source revision or query requirements change. - [x] Preserve the optional revision-keyed hosted parsed-document cache without making it a source of truth. - [x] Show exact preview warnings when concrete values reveal missing bounded results. ### 9. Materialize and verify immutable builds - [x] Compile and serialize the exact deployment-wide database during prepublish validation. - [x] Attach that validated bounded database to the immutable build without rebuilding it from later asset state. - [x] Show non-blocking prepublish warnings with database bytes, omissions, and potentially affected resources. - [x] Avoid claiming exact future route matches when dynamic values are unknown. - [x] Include the database/runtime only when a reachable configured query requires them. - [x] Ensure detail resources requesting Markdown content include all statically eligible bodies needed by dynamic routes. ### 10. Integrate all targets - [x] SSR: load the build's bounded database and conditionally include the small runtime. - [x] SSG: query the same database during generation, then omit database/runtime output when no runtime query is needed. - [x] Local/CLI: compile from the filesystem source with the same semantics. - [x] Preserve overview pagination, alternative route fields, draft-as-user-data behavior, and full article rendering. - [x] Verify ordinary Assets and fresh query-free sites perform no parser, artifact, content-runtime dependency install, or cleanup work. ### 11. Correct API and package ownership - [x] Move external Assets REST/OpenAPI contracts to `@webstudio-is/protocol`. - [x] Keep Webstudio domain models and Assets system-resource configuration in the SDK. - [x] Keep both cookie and token authentication behavior on the unified Assets REST surface. - [x] Remove pass-through route wrappers/reexports that add no transport behavior. - [x] Keep published runtime queries local to the artifact rather than routing them through Builder REST. ### 12. Remove superseded deployment infrastructure - [x] Remove external deployment-index upload, fetch, invalidation, cleanup, and garbage-collection code. - [x] Remove the SaaS asset-origin adapter requirement; close webstudio-is/webstudio-saas#652 as superseded because it has no independent purpose. - [x] Remove index-bucket compatibility configuration and avoid adding Infisical variables. - [x] Remove obsolete external-index paths, package dependencies, routes, scripts, and tests. - [x] Consolidate unreleased intermediate migrations where safe and retain only schema required by the final hosted cache/API design. ### 13. Verification and release readiness - [x] Move and preserve existing tests while changing ownership; add new tests only for changed contracts and behavior. - [x] Run content-engine compiler/runtime tests, query-builder tests, SDK/protocol/API tests, Builder tests, CLI tests, and affected typechecks. - [x] Run formatting, generated-file idempotence, fixtures, clean Supabase-compatible migrations, E2E shards, and complete agent evaluations. - [x] Verify SSR and SSG conditional inclusion and query-free generated package manifests. - [x] Run the five-article MCP blog workflow: authoring, field discovery, overview, dynamic detail routes, full Markdown rendering, publishing, and published-site verification. - [x] Run a maximum-budget database test and a mutation-during-compilation test. - [x] Perform a final architecture, security, API compatibility, migration, bundle, correctness, and DRY review. - [x] Reconcile every checkbox and replace the verification section with results from the final architecture before release. ## Future MDX work MDX is intentionally separate from this release. - The compiler contract should allow a future build-only MDX parser. - MDX compilation must not enter the published runtime. - Embedded JSX must use an explicit compiled representation rather than runtime source evaluation. - Webstudio components, tokens, styles, and component allowlists remain Webstudio integration concerns. - The future MDX work should reuse `ContentArtifact` and `ContentDatabase` where practical. ## Superseded design The following earlier design choices are no longer the target architecture: - GROQ or GraphQL as the Assets query language. - One external or long-lived index object per resource/query. - A deployment index fetched by the Worker from SaaS/CDN infrastructure. - Lazy production reads of source Markdown bodies. - A separate index bucket, asset origin, invalidation service, or garbage collector. - A 4 MiB published index limit. - Rebuilding an old build's database from the current mutable asset set. - Bundling query infrastructure into projects without a configured content query. ## Verification baseline The embedded runtime-database architecture previously passed the checks below. The OpenAPI-driven query-authoring change additionally passed: - Query-builder core: 4 files / 19 tests. - Query-builder React: 1 file / 7 tests. - SDK Assets source/config: 1 file / 7 tests. - Protocol Assets OpenAPI: 1 file / 9 tests, including independent OpenAPI validation. - Builder Assets OpenAPI description: 1 file / 5 tests. - Query-builder, React, SDK, protocol, and Builder typechecks. - Package-boundary validation and formatting of every affected file. Local verification baseline: - Content engine: 15 files / 104 tests. - Asset uploader: 24 files / 175 tests. - Builder: 204 files / 2,423 tests. - CLI: 54 files / 866 tests. - Complete workspace typecheck, formatting, lint, package boundaries, generated fixtures, and fixture builds. - The complete migration history applied successfully to a fresh Supabase-compatible database, including every migration after the consolidated Assets migration. - Builder production build and full local E2E suite passed. - Complete agent evaluations passed: authenticated page (170,859 ms), design input (207,101 ms), and font assets (67,050 ms). - Final review additionally verified correct REST edit/view/build permits, cookie and public-token read paths, immutable snapshot retries, exact database budgeting, query-free dependency omission, and SSR/SSG conditional runtime inclusion. Final staging verification passed against the embedded runtime-database architecture: MCP uploaded and updated five Markdown articles, discovered dynamic frontmatter fields, previewed a five-item sorted overview and one full-body slug query, updated both Assets resources, found zero binding issues, rendered `/blog` and `/blog/dynamic-routes` at HTTP 200, published build version 266, and verified both published routes at HTTP 200 with the expected article metadata and Markdown body. The workflow also found and fixed offset-form asset timestamps failing canonical datetime validation and made MCP `preview.start({ port: 0 })` correctly select an available local port. ## Release constraints - No new SaaS content service, bucket, or environment variable. - No published-site database dependency. - No parser/compiler dependency in query-free sites or runtime-only bundles. - The validated content database is immutable build output. - Publishing warnings do not block deployment. - The selected standard OpenAPI operation and its request JSON Schema are the only source of query UI semantics. |
||
|
|
7bc9942774 |
perf: Improve MCP evaluation reliability and efficiency (#5959)
## Summary - capture bounded MCP failure codes, validation issue paths, per-tool latency, mutation timing, uncached input, catalog size, and per-call/per-tool response bytes in every evaluation result - require every raw fixture outcome to have zero failed calls and zero retries, even when the workflow later recovers - update baselines only when all raw outcomes and all regression comparisons pass - refuse to replace an accepted aggregate token baseline with a noisier, higher-token sample - regression-gate aggregate response bytes alongside tokens, calls, focused reads, mutations, errors, and retries - compact repeated SDK schemas while preserving required arguments, destructive safety controls, and ordinary optional fields for common goal-guided tools - omit low-frequency optional argument names until focused discovery, reducing the initial tool handshake without removing callable tools - stop echoing the caller's goal from `meta.guide` responses - make evaluations copy their canonical objective verbatim into `meta.guide`, preventing vague paraphrases from selecting a generic workflow - normalize grouped `styles` and `declarations` maps into canonical flat `update-styles` operations before validation - normalize relative `create-page` paths through the platform URL parser - bundle the four bounded authentication discovery reads into one `inspect-auth-context` call - bound large MCP style-key mutation results while preserving a representative sample and exact total count - combine fragment insertion with immediate persisted binding verification to remove a sequential model turn ## Root causes and impact Recovered MCP failures could previously be reported as successful raw outcomes, and baseline files could be written before the complete comparison result was known. Raw outcomes now fail on any MCP error or retry, and accepted baselines are written only after every fixture and comparison passes. Rejected samples exposed two concrete error sources. A vague authentication objective selected a generic workflow and produced four validation failures, 20 calls, and 1.07M tokens; evaluations now pass the canonical objective verbatim. A relative `create-page.path` caused an `INVALID_INPUT` failure and retry; the adapter now canonicalizes it before validation. The deterministic catalog in the accepted baseline measures **50,864 bytes**, down from **53,317 bytes** (−4.60%). Input schemas fell from **31,190 to 28,737 bytes** (−7.86%). The two composite workflows add two conditionally exposed tools and 360 catalog bytes (about +0.71%) when their backing operations are available. Broad experiments that removed focused descriptions or all goal-specific examples made guide responses smaller but increased token use or caused retries. Those variants were rejected. The current implementation compacts only recognized goal guides, while retaining the detailed `update-styles` schema and the `upload-assets` batch example proven necessary by the evaluations. ## Workflow-churn iteration Authenticated discovery uses one model-visible `inspect-auth-context` call. Fragment authoring can now use `insert-fragment-verified`, which commits the insertion and immediately runs persisted binding verification for up to 200 bindings. This removes the following sequential verification turn without changing mutation or verification counts. Current deterministic workflow shape: - authenticated workflow calls: **14 → 10** (−28.57%) - design workflow calls: **17 → 16** (−5.88%) - aggregate workflow calls: **39 → 34** (−12.82%) - failures: **0** - retries: **0** - authenticated MCP response bytes: **52,524 → 46,900** (−10.71%) - authenticated guide response: **26,782 → 24,358 bytes** (−9.05%) One complete run measured authenticated tokens at **348,225** (−11.44%) and first verification at **38.3s** (−30.14%); design first verification moved to **28.1s** (−27.27%). The final exact-code run also passed with **10/16/8** calls and zero failures/retries, measuring authenticated tokens at **377,802** (−3.92%) and design first verification at **34.5s** (−10.67%). The composite preserves post-commit safety: if verification infrastructure fails after insertion, the result remains `committed:true`, returns `POST_COMMIT_VERIFICATION_FAILED`, and explicitly instructs the agent not to repeat the insertion. A guarded baseline-update run was rejected because stochastic aggregate tokens did not beat the accepted result, so no result artifacts were rewritten. ## Style response compaction Large style mutations previously returned every generated style key. In the design fixture, that single successful response was **19,121 bytes** and remained in context for all later turns. MCP responses now retain the first 20 keys plus `styleKeyCount` and `styleKeysTruncated`; the typed runtime operation remains unchanged. Focused evaluation results: - `update-styles` response: **19,121 → 2,815 bytes** (−85.28%) - design-workflow MCP responses: **70,941 → 54,633 bytes** (−22.99%) - calls: **17 → 17** - failures/retries: **0 / 0** - tokens: **498,764 → 505,136** (+1.28%, treated as noise and not accepted as a new baseline) The subsequent complete `pnpm evaluations` run passed all three fixtures with exact **11/17/8** call sequences and zero failures/retries. The accepted lower-token baseline remains unchanged. ## Goal-guide context compaction Recognized goal guides no longer repeat every selected tool's description and examples. Current Codex clients defer MCP tools and load each named tool's exact contract before invocation, so the guide keeps the goal-specific workflow while returning compact tool references. Free-form guide searches remain fully descriptive. Two complex inputs remain intentionally hydrated: `update-styles` keeps its detailed schema, and `upload-assets` keeps one batch example. Removing the upload example caused five failed attempts in a complete run; restoring only that example returned the font workflow to its exact 8-call, zero-error path. Final complete-suite results: - calls: **10 / 16 / 8** (**34 aggregate**) - failures/retries: **0 / 0** - goal-guide payload: **58,082 → 37,718 bytes** (−35.06%) - authenticated MCP responses: **46,900 → 38,268 bytes** (−18.41%) - design MCP responses: **51,854 → 44,008 bytes** (−15.13%) - aggregate MCP responses versus the accepted baseline: **147,335 → 101,922 bytes** (−30.82%) - aggregate tokens: **1,146,951**, only **+0.33%** above the stricter accepted 1,143,217-token baseline The complete suite and every per-fixture comparison passed. The guarded updater did not replace the accepted artifacts because the aggregate token sample did not beat the existing baseline. ## Responsive verification batching The MCP now exposes `screenshot.responsive` when reusable page capture is available. It accepts one generated route plus 1–8 viewports, refreshes the session preview once, reuses one browser target, and returns bounded evidence for every viewport. Auth and design evaluation workflows use one responsive capture instead of a separate preview start and two screenshot turns. Automatic screenshots now allocate an available preview port and reuse an already-running managed preview port. This prevents an unrelated service on the historical 5173 default from producing an MCP failure. Explicit ports, local previews, and external URLs remain unchanged. The evaluator records the bounded viewport array, counts the batch as verification and visual evidence, and compares its latency, response size, calls, errors, retries, and token consumption on every run. The new regression tests were demonstrated fail-then-pass. Guarded accepted results: - aggregate tokens: **1,143,217 → 990,190** (−13.39%) - aggregate calls: **39 → 30** (−23.08%) - aggregate MCP response bytes: **147,335 → 98,470** (−33.17%) - failures/retries: **0 / 0** - authenticated calls: **14 → 8** (−42.86%) - design calls: **17 → 14** (−17.65%) ## Design and font workflow batching Design discovery now uses one conditionally exposed `inspect-design-context` call to return bounded pages, breakpoints, design tokens, assets, and variables instead of five model-visible reads. Font persistence now uses one `verify-font-assets` call to refresh the asset namespace and return every changed font asset instead of a refresh plus separate verification reads. The evaluation tasks and validators require these composite paths. Both tools retain the underlying operation limits, namespace metadata, persisted-result checks, and zero-error/retry gates. Review also confirmed that SDK schemas must keep `additionalProperties:false` and that non-default MCP safety hints must remain explicit; those protocol constraints were preserved rather than traded for catalog-token savings. Guarded accepted improvements versus the previous responsive-batching baseline: - aggregate tokens: **990,190 → 914,760** (−7.62%) - aggregate calls: **30 → 24** (−20.00%) - aggregate focused reads: **8 → 2** (−75.00%) - aggregate MCP response bytes: **98,470 → 95,614** (−2.90%) - design calls: **14 → 10** (−28.57%) - font calls: **8 → 6** (−25.00%) - failures/retries: **0 / 0** The strict SDK catalog is **51,362 bytes** with **28,964 input-schema bytes**. The two new conditional workflow tools add 355 catalog bytes (+0.70%) and 130 schema bytes (+0.45%) while removing six model-visible workflow turns. ## Terminal responsive verification Auth and design now finish with one conditionally exposed `verify-page-responsive` call. It captures every requested viewport in one browser session and immediately runs the static page audit, removing one sequential model turn per workflow while retaining both kinds of evaluator evidence. The tool inherits the audit operation's permit and read namespaces; strict SDK schemas and MCP safety annotations remain unchanged. Latest complete comparison against the accepted baseline: - aggregate tokens: **914,760 → 869,274** (−4.97%) - aggregate calls: **24 → 22** (−8.33%) - aggregate MCP response bytes: **95,614 → 95,053** (−0.59%) - authenticated calls: **8 → 7** - design calls: **10 → 9** - font calls: **6 → 6** - failures/retries: **0 / 0** - authenticated focused run: **289,078 tokens** (−10.06%) and first verification **55,552 ms** (−17.90%) - design focused run: p95 MCP latency **9,642 ms** (−10.32%); token and first-verification variance increased in that individual sample, so only the complete comparison is used for the aggregate conclusion The complete comparison passed all fixture and aggregate regression gates. Two subsequent guarded `--update-baselines` attempts could not start any fixture because the external Codex runner had reached its usage limit; both exited before the first MCP call and wrote no accepted result files. Therefore the tracked accepted baseline below remains **914,760 tokens / 24 calls**, while **869,274 tokens / 22 calls** is the latest successful complete comparison. Regression coverage was demonstrated fail then pass. After this iteration, project-build MCP tests passed **105/105**, high-impact evaluation tests passed **41/41**, and project/CLI typechecks, lint, package boundaries, generated docs, and diff checks all passed. ## Accepted evaluation baseline | Fixture | Failed | Retries | Calls | Mutations | Tokens | Uncached input | Response bytes | First mutation | First verification | | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | authenticated-page-v1 | 0 | 0 | 8 | 4 | 321,396 | 44,353 | 37,536 | 39,076 ms | 67,660 ms | | design-input-v1 | 0 | 0 | 10 | 6 | 330,580 | 54,441 | 40,036 | 30,419 ms | 36,551 ms | | font-assets-v1 | 0 | 0 | 6 | 3 | 262,784 | 20,704 | 18,042 | 37,609 ms | 47,073 ms | Accepted aggregate: **914,760 tokens**, **24 calls**, **2 focused reads**, and **95,614 MCP response bytes**, with zero failures and zero retries. Compared with the previous accepted baseline, tokens fell **7.62%**, calls fell **20.00%**, focused reads fell **75.00%**, and response bytes fell **2.90%**. ## Verification - [x] Complete `pnpm evaluations -- --update-baselines` - [x] Independent complete `pnpm evaluations` after the composite workflow change - [x] Worse-token and error-containing samples rejected without replacing accepted baselines - [x] Added/changed regression tests demonstrated fail then pass - [x] Project-build MCP tests: 105 passed - [x] Complete high-impact evaluation tests: 41 passed - [x] Project-build and CLI typechecks - [x] `pnpm lint` - [x] `pnpm check:package-boundaries` - [x] `pnpm check:generated-docs` - [x] `git diff --check` - [x] Earlier workspace tests, including 2,356 builder tests - [x] Earlier generated API checks - [ ] Fixture sync/build: sync could not reach `main.development.webstudio.is` and timed out; no local test, evaluation, or code check failed ## Implementation checklist - [x] Error and retry rate enforced at zero for raw and compared outcomes - [x] Relative page paths normalized to prevent a known validation failure and retry - [x] Baseline replacement blocked unless every outcome and comparison passes - [x] Aggregate token baselines cannot ratchet upward - [x] Workflow churn bounded and regression-gated - [x] Authentication discovery collapsed from four model-visible reads to one - [x] Fragment insertion and persisted binding verification combined safely - [x] Large style-key mutation results bounded at the MCP boundary - [x] Token consumption captured, compared, and regression-gated - [x] Response bytes captured per call and tool, summarized, and aggregate regression-gated - [x] Per-tool latency, mutation timing, catalog size, and validation paths captured - [x] Repeated catalog/schema and focused-guide context reduced - [x] Canonical goal selection stabilized for evaluations - [x] Grouped style maps normalized before validation - [x] Responsive verification batches desktop and mobile evidence in one call - [x] Automatic screenshot previews avoid occupied default ports - [x] Responsive viewport evidence is captured and regression-gated - [x] Accepted evaluation artifacts preserve the best measured token baseline |
||
|
|
e80ca3b54c |
fix: Improve MCP authoring and screenshot reliability (#5957)
Closes #5954 Closes #5951 Closes #5950 Closes #5953 ## Summary - route external screenshot `url` and `baseUrl` targets through the self-closing browser path - keep reusable Chromium sessions limited to Webstudio-managed session previews - validate typed style values at the public runtime boundary while retaining CSS strings for design tokens - build MCP and shell validation messages from compact structured issues - advertise typed style objects with a concrete keyword example - persist `update-asset` metadata patches to the related file record and verify the saved value - store multi-sentence page titles and metadata as fixed text without misclassifying sentence punctuation as member access - capture privacy-safe aggregate token usage and MCP workflow metrics in every high-impact evaluation result - compare each evaluation with a compatible checked-in baseline and fail on correctness, token, tool-use, error/retry, broad-read, or MCP-latency regressions ## Root causes ### External screenshot lifecycle The fast iterative preview change made `session` the default preview source. External captures were then treated as managed session-preview captures and opened a reusable Chromium session. One-shot MCP processes do not call `preview.stop`, so the browser kept the process alive and could be orphaned when the caller killed the hung CLI. ### Validation errors and schemas `update-styles` declared `value` as unconstrained input, and design-token objects were checked only for a string `type`. Invalid values therefore passed the advertised/input schema and failed later in the full `StyleValue` parser, exposing the raw Zod union message. Error serialization already carried flattened issues but did not use them for `error.message`. ### Asset metadata persistence `update-asset` correctly produced an `assets/[id]/meta` patch, but asset persistence compared only Asset-table fields. Metadata lives on the related File row, so the patch was treated as unchanged and the successful mutation never wrote the new value. ### Page text expression disambiguation Page text normalization treated a period followed by whitespace and a word as JavaScript member access. Normal prose such as `Marques. Toutes nos marques` was therefore validated as an expression and rejected instead of being stored as a string literal. ### Evaluation observability The evaluation result only stored duration and two call counts, so token cost, failures, retries, mutation flow, verification coverage, and MCP latency could regress without a durable comparison. Results now use a bounded schema-v2 aggregate and never persist prompts, transcripts, stdout/stderr, credentials, arguments, or response payloads. ## Impact - external screenshots complete without retaining browser processes - repeated managed-preview screenshots retain browser reuse - invalid style inputs fail at the public boundary with paths such as `updates.0.value` - `error.message` is concise and actionable instead of a repeated raw union dump - generated schemas no longer advertise primitive or array values for `update-styles` - supported design-token CSS strings remain accepted - `update-asset` font metadata remains changed after commit, refresh, and reload - `create-page` and `update-page` accept ordinary multi-sentence titles and SEO descriptions while preserving computed expressions - every high-impact run reports input, cached-input, cache-write, output, reasoning-output, total-token, and cache-rate aggregates - MCP results report calls, success/failure, retries, focused/broad reads, dry runs, planned/committed mutations, verifications, total/p50/p95 latency, and first-mutation/verification timing - compatible results are compared with accepted baselines using a 15% relative regression threshold; correctness failures and zero-baseline errors, retries, or broad reads remain zero-tolerance - stochastic wall-clock and milestone timings remain visible as deltas without making a single noisy run fail ## Todo - [x] Reproduce external URL browser-session routing - [x] Use self-closing capture for external screenshots - [x] Preserve browser reuse for managed session previews - [x] Reproduce primitive style-value schema acceptance - [x] Validate typed style values at the public boundary - [x] Preserve CSS-string design-token inputs - [x] Format stdio and shell validation messages from structured issues - [x] Regenerate and verify runtime operation contracts - [x] Reproduce dropped asset metadata at the persistence boundary - [x] Persist and verify related File metadata updates - [x] Reproduce multi-sentence page text failures on create and update - [x] Distinguish sentence punctuation from computed member access - [x] Add privacy-safe token and MCP workflow metrics - [x] Add per-fixture baseline comparisons and regression gates - [x] Refresh accepted schema-v2 baselines for all fixtures - [x] Review the complete diff - [x] Run repository checks and the complete agent evaluation suite ## Verification - `pnpm checks` — passed, including workspace tests, typechecks, lint, package-boundary checks, generated API/docs checks, fixture sync, and fixture builds - builder tests — 189 files, 2,356 tests passed - CLI tests — 53 files, 852 tests passed - project-build tests — 81 files, 2,023 tests passed - asset-uploader tests — 11 files, 98 tests passed - targeted evaluation metrics/regression/trace/runner tests — 5 files, 17 tests passed - all four issue regression tests and the new evaluation harness tests were demonstrated failing without their implementations and passing with them - `pnpm evaluations -- --update-baselines` — 3/3 fixtures passed and schema-v2 baselines were updated - a subsequent real `font-assets-v1` result compared successfully after the regression policy was finalized |
||
|
|
c3c0e32204 |
fix: make MCP font assets editable and deterministic (#5944)
Closes #5943 Closes #5942 ## User impact MCP-managed font assets now keep their identity, metadata, and generated CSS behavior predictable across upload, editing, synchronization, and rendering. ## Changes - Preserve explicit font metadata overrides while validating supported fields and rejecting unsupported metadata instead of silently dropping it. - Expose font metadata through `update-asset`, with partial updates merged into the existing metadata and validated before the mutation is committed. - Prefer the typographic family name when reading font files, so Fontsource and Google font files resolve to the same logical family where the file provides that information. - Deduplicate generated font sources by URL and format, choose a deterministic source when legacy assets collide, and derive the CSS format hint from the filename when historical metadata is stale. - Add MCP and CLI examples explaining font metadata upload and correction. - Add regression coverage for metadata persistence, invalid overrides, family-name extraction, duplicate source handling, stale format metadata, and generated runtime contracts. ## Verification - `pnpm checks` - 46 workspace packages tested, including 2,355 Builder tests, 809 CLI tests, 2,019 project-build tests, and 97 asset-uploader tests. - All workspace typechecks, Oxlint, package-boundary checks, generated API/docs checks, and fixture link/sync/build checks passed. |
||
|
|
d440e02990 |
feat: Add safer MCP authoring and draft-page verification (#5905)
- **Restore points for agent work**: Create named restore points before major edits, review changes with a dry run, and safely revert project content if needed. - **Shared Slot authoring**: Agents can turn existing sections into shared Slots and insert linked copies across pages, including valid nested Slots. - **DTCG and Figma token imports**: Import design tokens and CSS variables through CLI, MCP, or direct paste, with aliases, modes, composite values, and mappings supported. - **Consistent token conflict handling**: Every paste workflow now offers the same preserve, rename, or merge choices instead of silently overwriting existing design tokens. - **Draft-page previews**: Agents can generate and inspect previews of draft pages without publishing them or exposing drafts in production builds. - **Safer MCP mutations**: Destructive operations require explicit confirmation, invalid raw patches and invented record IDs are rejected, and restore operations commit atomically. - **More dependable agent contracts**: New tools have complete runtime schemas, clearer validation errors, and consistent behavior across CLI, MCP, Builder, and server APIs. |
||
|
|
98d5c607ee |
feat: Improve MCP safety, context efficiency, and agent workflows (#5895)
## Summary This combines the three reported MCP improvements into one release-ready change: - Safer mutation planning and confirmation for destructive operations. - Much smaller MCP handshake and default discovery responses. - Better agent workflows for checkpoints, multi-project work, and page duplication. ## Safer destructive operations - Exposes `dryRun` in MCP schemas for every local-capable mutation. - Returns planned transactions through `meta.session.transaction`. - Requires a two-step confirmation flow for destructive semantic operations. - Returns a short-lived token bound to the project, build version, input, and exact generated patch plan. - Rejects expired tokens, altered inputs, changed versions, and changed plans. - Sets `destructiveHint: true` only for operations that actually require confirmation. - Explicitly protects destructive operations whose names do not begin with delete or replace, including replacing all redirects and clearing all styles from a style source. ## Smaller, focused MCP context - Defers oversized tool input schemas from the initial MCP handshake. - Keeps complete focused schemas available through `meta.get_more_tools` and CLI schema inspection. - Makes template registry lists compact while preserving complete payloads through focused get calls. - Makes component summaries compact by default and supports paginated component details. - Replaces placeholder operation descriptions with useful command descriptions. - Constrains previously untyped JSON schema fields so clients can validate inputs earlier. - Keeps runtime output schemas compatible with strict MCP clients. ## Better agent workflows - Allows read-only discovery and inspection while a workflow checkpoint is awaiting acknowledgement. - Continues blocking mutations until the checkpoint is acknowledged. - Checks persisted checkpoints before every workflow call rather than only the first call. - Adds `--project` support so saved projects can be selected without relinking the current directory. - Isolates project sessions and checkpoints by selected project. - Adds atomic `duplicate-page` substitutions for copied fixed text and copied variable values. - Prevents substitutions from modifying source content or global root state. - Rejects ambiguous or missing copied variables without leaving a partial duplicate. ## Realistic agent verification Minimal-context tests used a weak agent against the local packaged CLI and stdio MCP: - Planned a page deletion, stopped for explicit user confirmation, committed the unchanged plan, and verified the page was absent. - Tried to reuse a confirmation token with altered input; Webstudio returned `DESTRUCTIVE_CONFIRMATION_INVALID` and preserved both pages. - Verified compact handshake schemas and focused retrieval of the complete oversized schema. - Verified compact template and component discovery responses. - Selected a saved project explicitly without relying on the directory link. - Verified checkpointed workflows permit reads but block mutations. - Duplicated a London page into a Paris page with atomic text and variable substitutions. - Verified source content remained London, copied content became Paris, and temporary pages were removed. ## Validation - Project-build focused suites: 188 tests passed. - CLI focused suites: 183 tests passed. - Protocol focused suites: 21 tests passed. - CLI, project-build, and protocol typechecks passed. - Repository lint passed with zero warnings. - Package-boundary validation passed. - Generated runtime operation contracts were regenerated from source. - Real local CLI/MCP tests passed against a running Builder server. Closes #5887 Closes #5888 Closes #5889 |
||
|
|
12ad74584a |
feat: Improve MCP authoring, auditing, and verification (#5886)
## Summary This improves how coding agents discover, author, verify, and audit Webstudio projects through the CLI and MCP. The work focuses on making agents more reliable with less context, while keeping edits semantic and Builder-editable. ## What changes for users - Agents can verify dynamic bindings after authoring and receive actionable diagnostics when expressions, resources, scopes, or component props do not line up. - Agents can create complete resource-backed runtime UI through a semantic operation instead of coordinating fragile low-level edits. - MCP discovery and command output are compact by default, with pagination and focused verbose detail when an agent needs more context. - Asset, style, token, font, page, and component inspection provides stronger usage context so agents can preserve the existing design system. - Rendered audits provide better evidence for responsive behavior, geometry, contrast, generated-build size, and large-project runs. - Cross-project MCP workflows can run from a manifest with explicit mutation approval, isolated failures, resumable progress, and persistent checkpoints. - Project-specific agent onboarding is clearer and does not add telemetry. - Packaged CLI releases gain stronger smoke verification for install, MCP startup, authoring, preview, screenshots, and audits. ## Reliability and architecture - Runtime operation output schemas are generated from the runtime registry and remain client-compatible. - Project-build exports are consolidated around architectural boundaries instead of feature-by-feature convenience entry points. - HTTP, preview, screenshot, and project-session paths are hardened for longer autonomous workflows. - Craft compatibility work is read-only and provenance-aware; it does not silently opt ordinary projects into Craft behavior. - A minimal-context evaluation harness exercises key workflows with a weaker model and stores bounded, privacy-safe evidence. ## Validation - Rebased on the latest `origin/main`. - `pnpm lint` - Repository-wide `oxfmt --check` - CLI typecheck and full test suite: 773 tests passed. - Project-build typecheck and full test suite: 1,866 tests passed. - Protocol typecheck and full test suite: 83 tests passed. - HTTP client tests: 23 passed. - CSS data tests: 3,620 passed, 1 skipped. - Focused Builder tests: 67 passed. - Package-boundary, generated API, and generated documentation checks passed. - Minimal-context agent evaluations passed for authenticated resource-backed UI and design-system-aware responsive authoring. |