6761 Commits
Author SHA1 Message Date
Oleg Isonen be74d8d698 fix: Validate response headers and expose MCP rule edits (#6444)
Header rules previously accepted `null` values, which silently turned a
rule into a header-removal instruction even though the settings UI only
offers adding a string value or deleting a rule. Require a nonempty
string throughout the SDK, Builder, CLI generator, and API contract.

Expose configured rules to MCP through `list-response-headers`,
`set-response-header`, and `delete-response-header`. Setting a rule adds
or replaces the header for the same route and case-insensitive name; `/`
and `/*` remain distinct. The Builder UI and MCP operations now share
the same rule-key, edit, and Zod validation logic. The generated JSON
Schema advertises the header-name pattern and nonempty value, while
server-side Zod validation enforces route syntax, forbidden headers,
duplicates, and total size. The generic `update-project-settings`
operation still supports replacing the whole list.

Validation: SDK, project-build runtime and MCP, CLI command and prebuild
tests; SDK, Builder, project-build, CLI, and protocol typechecks; lint,
package boundaries, generated API and docs checks. Local fixture sync
against the development API is blocked by differing bundle versions; PR
CI tests fixtures against its deployed Builder host.
2026-09-25 23:37:07 +02:00
Oleg Isonen 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.
2026-09-25 21:01:43 +02:00
Oleg Isonen 2c9fd16689 feat: configure custom response headers by route (#6429)
## Description

Project Settings starts with an empty response-header rule list. Users
can add valid HTTP response headers for all paths (`/*`) or an
Authentication-style route such as `/private/*`, and remove any
configured rule. The Headers and Authentication views share the
accessible rule-list form. The header form suggests standard
response-header names and common values for the selected header, while
accepting custom names and values. `Set-Cookie` is not suggested because
a rule can store only one value per header and would replace existing
cookies.

`X-Powered-By`, `X-Content-Type-Options`, and
`Strict-Transport-Security` are dispatcher-owned and cannot be
configured. Users may configure `Content-Security-Policy`,
`X-Frame-Options`, and `Referrer-Policy`; when absent, the dispatcher
supplies their defaults. The SDK validates header names, values, routes,
duplicates, and size limits. The CLI emits only configured rules.

Customization requires Pro for custom-domain publishing, while unchanged
defaults and staging remain available on Free plans. The server checks
the saved production snapshot against the project owner's plan.

Companion Cloud worker PR:
https://github.com/webstudio-is/webstudio-saas/pull/664. Deploy its
dispatcher support before exposing these settings in the Builder. Other
hosts need an adapter that applies the generated rules; Webstudio Cloud
dispatcher defaults do not apply automatically elsewhere. Static exports
do not apply headers automatically.

## Validation

- SDK, CLI, Builder browser, publish-warning, and production-build tests
pass.
- Builder, SDK, and CLI typechecks and lint pass.
- The companion Cloud worker tests, typecheck, and lint pass.

Replaces https://github.com/webstudio-is/webstudio/pull/6428 to use
`headers.staging`.
Closes https://github.com/webstudio-is/webstudio/issues/4748
2026-09-25 16:46:35 +02:00
Oleg Isonen 30b4f994dc fix: restore publish countdown (#6443)
## Summary
- Keep the publish countdown visible while the publish action is in
progress.
- Show the pending spinner after the 60-second countdown expires.

The refactor in `804cd39bc` applied the design system's pending button
state immediately. That state hides the button text, including the
countdown label. Progress still disables the action throughout
publishing, while the pending visual state now waits until the countdown
ends.

## Verification
- `git diff --check`
2026-09-25 14:51:27 +02:00
Oleg Isonen cd64a6627e fix: Load unbound resource previews in the variable dialog (#6432)
New resources can fail to show data in the **New variable** dialog
because a page-plan refresh cancels their preview request before it
finishes.

This change keeps explicitly requested previews alive until the dialog
releases them. Changing inputs, changing resource type, or closing the
dialog cancels obsolete work and prevents old data from showing. Unused
page resources remain lazy; a preview still makes an on-demand request
when **Load data** is clicked. The dialog stays usable while unrelated
page resources are loading.

Follow-up to #6030.

### Checklist
- [x] Reproduce the cancellation with a regression test that fails
before the fix.
- [x] Keep dialog previews independent of page resource discovery, using
the existing loader, cache, and request versions.
- [x] Clear old previews on input changes and release requests on dialog
close.
- [x] Cover new Current date, Sitemap, HTTP, and existing unbound
resources, plus repeated loads and stale responses.
- [x] Keep tests for obsolete page-request cancellation and async
resource dependencies passing.
- [x] Test the unsaved dialog with an unrelated page request pending and
a page-plan recalculation.
- [x] Clear the old preview when switching resource types or starting an
asynchronous body edit.
- [x] Review documentation impact: no update needed; this restores the
documented preview behavior.
- [ ] Confirm GitHub CI passes.

### Verification
- The dialog loading, type-switch, and body-edit regression tests failed
before their fixes and passed afterward.
- 82 focused tests and the settings-panel suite (588 tests) passed.
- Builder typecheck and repository lint passed.
- With preview retention temporarily disabled, all nine new loader
regression cases failed as expected.
2026-09-23 13:03:13 +02:00
Oleg Isonen 9845517029 fix: hydrate published Markdown assets (#6422)
Migrated from #6421 to the short .staging branch name.
2026-09-23 01:01:08 +02:00
Oleg Isonen 3d0a0657aa feat: Support async resource dependencies (#6030)
## Summary

Make `Resource<Document>` the lazy document-resolution abstraction
shared by Content Engine graphs, generated sites, and Builder previews.
Expressions use one async computation interface: referenced resource
documents resolve first, independent requests run concurrently, and
unused resources stay unloaded.

Ref #6029
Ref #6031

## Implementation checklist

- [x] Define one resource contract for synchronous or asynchronous
document resolution.
- [x] Resolve explicit roots lazily with memoization, bounded
concurrency, cancellation, and structured errors.
- [x] Preserve cycle, duplicate, and missing-resource diagnostics.
- [x] Extract resource dependencies with the existing expression AST.
- [x] Discover consumed resource roots from the current page tree and
page metadata.
- [x] Generate a lazy request graph for published page data, including
independent requests.
- [x] Resolve dependency documents before evaluating dependent request
expressions.
- [x] Integrate dependency-first loading with Builder preview caching
and invalidation.
- [x] Expose safe Resource bindings in the editor and reject
self/indirect cycles.
- [x] Adapt REST, GraphQL, Assets, date, and sitemap requests through
the same Resource contract.
- [x] Cover laziness, chains, shared dependencies, concurrency,
invalidation, cancellation, failures, and cycles with fail/pass tests.
- [x] Review documentation impact and document dependent Resources in
the Data variables guide.
- [x] Track collection-item and component-parameter-dependent resources
as follow-up #6031.
- [x] Regenerate and review affected fixtures.
- [x] Resolve action-resource request dependencies when an action is
invoked.
- [x] Reproduce and fix cached form submissions and multi-root
dependency-cycle hangs.
- [x] Document uncached form actions and regenerate affected fixtures.
- [x] Preserve single and multi-selected content until a cut
successfully writes to the clipboard.
- [x] Preserve boolean expression values in the search-exclusion
checkbox.
- [x] Cancel cut deletion if project data changes while preparing or
writing the clipboard; cover content/property edits and project switches
for single and multi-selection, and document the retry behavior.

- [x] Validate pending cuts after the final await, immediately before
deletion, and reproduce the queued-update race for single and
multi-selection.

## Technical direction

The persisted, revisioned `DocumentGraph` remains the Content Engine
artifact. `Resource<Document>` is the execution abstraction for
obtaining a document; its resolver evaluates only the dependency closure
reachable from requested roots. Static and remote documents therefore
differ by Resource implementation, not by expression value type.

Generated pages always emit a `ResourceRequestGraph`. Each node owns its
output name, dependencies, and request factory. Roots come from resource
variables actually referenced by the current page's metadata, props,
actions, or expression children. Unused and off-page resources remain
unresolved. The loader still accepts legacy request maps for
compatibility.

Builder preview uses the same page-root discovery as published
generation. Its existing cache remains the lifecycle boundary: cached
dependency documents unlock dependent requests, while invalidation
removes downstream requests until their inputs resolve again. The
separate canvas frame recomputes its local plan from the synchronized
cache so rendered expressions receive those documents.

Action requests and their dependencies resolve when a form is submitted.
Each submission sends a new action request, bypassing Webstudio's
resource cache. Dependency requests still use their configured cache
lifetime.

## Review simplifications

- Consolidated page resource-root discovery in the SDK and reused it in
Builder.
- Scoped root and action discovery to the current page tree.
- Removed the redundant immediate-resource helper; `Resource.resolve`
already accepts `Document | Promise<Document>`.
- Moved output names onto graph nodes and removed the duplicate
graph-level map.
- Removed unused planner output and corrected transitive cycle discovery
to include only Resource data sources.
- Regenerated fixtures so independent resources also use lazy graph
execution.
- Reused one abort-aware concurrency limiter for document sessions and
Resource resolution.
- Collapsed duplicate generator action scans and removed redundant
generated-resource bookkeeping.
- Unified legacy request maps and request graphs through one resolution
path.
- Shared the 20-request concurrency limit between SDK, Builder preview,
and the server batch boundary.
- Preserved dependency failure paths and original causes, including the
first cancellation reason.
- Removed invalid-request logging that could expose Resource headers or
request bodies.
- Made editor cycle detection alias-safe by comparing underlying
Resource IDs.
- Memoized page settings values to prevent repeated async validation
renders.

## Verification

Latest local verification for `bbca6395e3`:

- Both queued-update regression cases failed before the fix and passed
afterward. Validation now runs synchronously immediately before
deletion, after the final await, in both cut paths.
- Passed: 255 copy/paste and command tests, Builder typecheck, lint,
package boundaries, formatting, and diff checks.
- Reviewed documentation: the existing copy/paste safety description
remains accurate. Fixture inputs and outputs are unaffected; no fixture
regeneration was needed. Agent evaluations were not run because separate
approval is required.
- CI for the new commit is pending.

Previous local verification for `ffbc75b152`:

- Six regression cases failed before the fix and passed afterward:
content edits, property edits, and project switches during a pending
cut, with both single and multiple selections.
- Passed: 253 copy/paste and command tests, Builder typecheck, lint,
package boundaries, source formatting, and diff checks.
- An unchanged content-block test file initially failed to import in the
browser. Its isolated rerun and the full 253-test rerun passed without
code changes.
- The guard conservatively treats any project-data change as a reason to
keep the clipboard copy without deleting content. It shows a retry
message. Copy/paste documentation now describes this behavior.
- Fixture inputs and outputs are unaffected; fixtures were not
regenerated. Agent evaluations were not run because separate approval is
required. CI for the new commit is pending.

Previous local verification for `3eb580697c`:

- Reproduced both regressions with failing tests before fixing them:
premature deletion during cut and false search exclusion appearing
checked.
- Passed: 203 copy/paste and page-settings tests, Builder typecheck,
lint, package boundaries, formatting, and diff checks.
- Cut coverage includes keyboard and programmatic entry points, single
and multiple selections, delayed successful writes, rejected writes, and
disabled copying.
- Reviewed documentation impact: these fixes restore existing behavior;
no documentation update is needed. Only Builder UI and clipboard files
changed; fixture inputs and outputs are unaffected, so fixtures were not
regenerated for this commit.
- CI status for this older commit was recorded as pending at handoff.
Agent evaluations were not run; they require separate approval.

Previous local verification for `858b348ad`, rebased onto `cf606ca4c`:

- Regression tests failed before each fix and passed afterward: repeated
submissions in both generated route templates, plus three
multi-root/branching cycle cases.
- Passed: 590 SDK tests, 1,129 CLI tests, 72 focused Builder tests,
SDK/CLI/Builder typechecks, lint, package boundaries, and fixture
link/sync/build.
- Initial concurrent runs hit timeouts. The complete CLI suite passed
with one worker; the focused Builder suites passed without file
parallelism.
- Documentation and generated fixture changes reviewed. Agent
evaluations were not run; they require separate approval.
- CI for `858b348ad` passed: Main (including all six E2E shards), Visual
Regression, Lint PR, and Check submodules.

Earlier verification (before this update):

TDD fail/pass evidence includes resolver creation, AST dependency
discovery, published graph reachability, dependency-gated request
generation, Builder dependency-first planning, Resource editor cycle
filtering, page-tree root scoping, off-page action isolation, transitive
Resource-only cycle discovery, abort/timeout precedence, server batch
limits, and the rebased SSG fixture consuming its lazy Resource.

- `pnpm checks` passed after rebasing onto `ce84ddcf12`; it covered
workspace tests, typechecks, lint, package boundaries, generated
API/docs validation, and fixture link/sync/build.
- Earlier focused checks passed: Builder typecheck, 12
page-settings/pages unit tests, lint, formatting, and `git diff
--check`.
- Local generated-resource E2E passed for HTTP, GraphQL, and
current-date resources (3 tests).
- The page-actions and Marketplace page-settings persistence E2Es both
pass locally.
- CI for `6b8b08a70` passed: Main workflow (including all six Builder
E2E shards), Visual Regression, Lint PR, and Check submodules.
- Agent evaluations were not run because they require separate explicit
approval.

## Current behavior and limitations

- A failed dependency blocks its dependents and reports the resource
path and cause.
- One published resolution call is the memoization boundary; Builder
maps dependency readiness onto its versioned request-cache lifecycle.
- Scoped collection-item and component-parameter resources remain in
#6031.
- Builder skips cyclic dependency paths without blocking independent
resources.
2026-09-22 21:04:42 +02:00
Oleg Isonen d506525915 fix: allow re-uploading fonts with invalid metadata (#6423)
## Summary

After #6419, a project with broken font metadata can load again, but
uploading the same font still returns the broken asset. Deleting it
first does not help: the upload reuses the deleted file.

This lets a normal re-upload read the font again and create a valid
asset.

## Changes

- Reuse the existing font-metadata validation before restoring or
creating a deduplicated asset.
- Clear only the broken file's content hash so a fresh upload can
proceed. Keep its bytes, metadata, and existing references unchanged.
- Check that the metadata still matches before clearing the hash. If
another request repaired it, reuse that valid file.
- Keep normal deduplication for later uploads of the new valid font.

No schema changes or migrations. Existing references are not
automatically replaced, and the original font bytes must still be valid.

## Checklist

- [x] Reproduce the bug before fixing it.
- [x] Cover invalid JSON and invalid font metadata, with both active and
deleted assets, using real WOFF2 bytes.
- [x] Cover a concurrent metadata repair, including a failing run with
the update guard removed.
- [x] Pass all 306 asset-uploader tests, package typecheck, and
lint/format checks for both changed files.
- [x] Verify fixture impact: no fixture inputs, outputs, or bundle
contracts changed; all 17 fixture checks pass. Regeneration is not
needed.
- [x] Review documentation impact: the custom-font and asset guides
already describe normal uploading; no documentation changes needed.

Agent evaluations and browser E2E were not run; both require separate
approval.

Ref #6419
2026-09-22 17:39:54 +02:00
Oleg Isonen 804cd39bc2 fix: Stop slow diagnostics from blocking publishing and clarify publish status (#6416)
Closes #6406

## What changes

Publishing no longer waits for advisory content-database and MDX
diagnostics. Those checks run alongside the publish request, so a slow
diagnostic does not prevent publishing from starting.

Permissions, pending content saves, and blocking project checks still
run before publishing. The server continues to enforce publish
authorization and build integrity.

### Publish dialog

- Add a green **Validate** button beside the blue **Publish** button,
with equal widths.
- Show waiting states and disable the actions while their work is
running.
- Let users validate without publishing. Successful validation shows a
persistent “Validation passed. Ready to publish.” toast.
- Clear the button's **Validated** state when the popover closes.
Reopening still reflects an active publish job.
- Move **Add new domain**, **Export**, and **Publish settings** into the
header's ellipsis menu.
- Show yellow progress icons for publishing domains, green checkmarks
for completed publishes, red icons for failures, and neutral icons for
unpublished domains. Built-in and custom domains share the status
mapping.

### CLI and MCP

Add `check-publish-readiness` to check the saved project without
starting a publish. It checks permissions, rejects empty or unusable
domain selections, reuses the existing build-integrity checks, and
returns content diagnostics. Broken resource references no longer
produce a false `valid: true` result.

### Content checks

Run independent collection reads with bounded concurrency instead of
processing every collection sequentially. This reduces serial waiting,
but does not eliminate the underlying storage reads or content
processing.

## Checklist

- [x] Keep authorization and blocking project checks in place.
- [x] Run advisory content diagnostics alongside publishing.
- [x] Add standalone validation, waiting states, and popover-local
validation results.
- [x] Consolidate secondary actions in the publish menu.
- [x] Distinguish pending, completed, failed, and unpublished domain
states.
- [x] Add CLI/MCP readiness checks using existing integrity and
verified-domain logic.
- [x] Bound concurrent collection reads.
- [x] Add regression tests, including invalid domains, missing
resources, status transitions, and popover state.
- [x] Demonstrate the latest regression tests failing without the fixes
and passing with them.
- [x] Review documentation impact: update CLI operation guidance and
metadata, and document domain status colors in the publishing guide.
- [x] Run focused Builder, CLI, and asset-uploader tests, Builder
typechecking, lint, formatting, and the generated-API check.

## Verification and limits

The latest UI check passed all 49 publish tests. The preceding readiness
fix passed 98 focused Builder tests and 130 CLI tests; collection
coverage passed 112 tests. Typechecking, lint, formatting, and
generated-API checks passed. The latest fixes do not affect fixture
inputs or generated site output.

Storybook screenshots and agent evaluations were not run because
approval was not given. A live production publish was not performed
during the latest verification.

Publish can finish before advisory diagnostics do. Returned warnings
appear afterward and cannot stop a publish that has already started.
Standalone validation still waits for diagnostics, and actual publishing
still requires content synchronization, server checks, and
build/deployment work. This does not promise that every publishing
timeout is eliminated.
2026-09-22 17:34:14 +02:00
Oleg Isonen cf606ca4c3 fix: tolerate invalid asset metadata on load (#6419)
Malformed persisted asset metadata can prevent an entire project from
opening. This PR recovers safely from invalid stored metadata and
prevents the same invalid font records from being written again.

## Summary
- Safely parse persisted asset metadata; malformed assets load as
generic files instead of breaking the project.
- Log structured diagnostics for invalid persisted font metadata without
exposing the metadata contents.
- Detect font uploads by extension even when sent as generic files, and
preserve parsed font metadata.
- Reject invalid font metadata overrides and file/font schema
combinations before persistence.
- Add regression coverage for the reported empty metadata, upload
parsing, validation, write rejection, and read fallback.

Existing malformed rows are not rewritten. They remain identifiable as
generic files and can be re-uploaded after the fix is deployed.

Fixes #6418

## Verification
- `pnpm --filter @webstudio-is/asset-uploader test` (301 passed)
- `pnpm --filter @webstudio-is/sdk test` (573 passed)
- Relevant typechecks, generated API check, formatting, and `git diff
--check` passed.
2026-09-19 23:50:06 +02:00
Oleg Isonen ade7a30560 perf: prepare content collections concurrently (#6412)
Ref #6396

Publishing could spend the entire serverless request budget preparing
content collections one folder at a time. Projects with several
collections could therefore time out before the publish diagnostics or
bundle response was ready.

This change:

- validates independent collection folders concurrently;
- discovers reserved collection files concurrently;
- removes collection configuration and template files from the content
index concurrently;
- reuses the existing bounded content-read limit, so storage and parsing
work remains capped;
- keeps validation rules and published project semantics unchanged.

Todo:

- [x] Reproduce the affected production project returning HTTP 504 after
about 15.45 seconds.
- [x] Confirm the diagnostics calculation is fast locally and isolate
the delay to publication asset preparation.
- [x] Add regression tests for concurrent validation, reserved-file
discovery, and index preparation.
- [x] Demonstrate the new tests fail against the previous serial
implementation.
- [x] Run the full asset-uploader test suite, typecheck, formatter,
linter, and diff checks.
- [x] Review documentation impact: no user-facing documentation changes
are needed for this internal performance fix.

Verification:

- pnpm --filter @webstudio-is/asset-uploader test: 296 tests passed
- pnpm --filter @webstudio-is/asset-uploader typecheck
- pnpm exec oxfmt --check on changed files
- pnpm exec oxlint --deny-warnings on changed files
- git diff --check

Known limitation:

The affected production deployment must be retested after this PR is
deployed. The local reproduction confirms the old behavior and the
focused regression tests confirm the reduced serial work, but the remote
project cannot execute new server code from this branch.
2026-09-18 15:26:56 +02:00
Oleg Isonen 2a389b878c fix: load embedded MDX documents in previews (#6415)
## What changed

Published MDX Content Blocks could build with document bindings but fail
at runtime when the content index did not include the MDX source text.
Dynamic article routes then returned an asset-query 500 or crashed
during SSR.

This change:

- hydrates missing local MDX source text during CLI prebuild;
- resolves document-graph references from embedded content before making
an HTTP request;
- adds a regression test for embedded document loading.

## Verification

- published runtime tests passed (17 tests)
- prebuild tests passed (69 tests)
- content-engine and CLI typechecks passed
- exact imported marketplace project reproduced locally and verified:
SSG generated 10 pages, `/posts/designing-with-intent` returned HTTP 200
with article content, and no `ReferenceError` or asset-query 500
occurred
2026-09-18 15:02:36 +02:00
Oleg Isonen cd1881bde6 fix: upload asset tickets serially (#6414)
## Summary

- Reserve and complete each asset upload before requesting the next
upload ticket.
- Prevent concurrent identical uploads from triggering the server's
in-progress deduplication error.
- Keep the upload result mapping keyed by the folder-scoped upload key.

## Verification

- `pnpm --filter @webstudio-is/builder exec vitest run
app/builder/shared/assets/upload-assets.test.ts
app/builder/shared/assets/directory-drop.test.ts
app/builder/shared/assets/asset-upload.test.tsx`
- 41 tests passed.
2026-09-18 14:38:13 +02:00
Oleg Isonen 0e30263787 fix: Fix multi-file asset drops and folder-scoped deduplication (#6413)
## Summary
- Preserve all files when dropping multiple local files into Assets.
- Scope client-side duplicate detection by folder.
- Allow the same underlying file to have asset entries in different
folders.

## Validation
- Focused asset upload and directory-drop tests passed locally.
- `git diff --check` passed.
2026-09-18 13:33:00 +02:00
Oleg Isonen d6c1a3c763 fix: Show one Alert styling target in Markdown Embed (#6411)
Markdown Embed currently adds one general Alert descendant plus five
variant-specific descendants. This shows six Alert entries in the
Navigator even though they represent one element type.

This keeps one Alert descendant. Note, tip, important, warning, and
caution still render normally and retain their state-specific default
backgrounds.

Ref #6278

## Implementation

- Remove the five variant-specific Alert descendants from the Markdown
Embed template.
- Keep the shared Alert descendant and its `data-state` style rules.
- Add a regression test for the single Alert styling target.

## Checklist

- [x] Reproduce the six Alert descendants in a failing test.
- [x] Keep one Alert descendant.
- [x] Preserve default styles for all five variants.
- [x] Run the SDK components test suite.
- [x] Run SDK components typecheck and declaration generation.
- [x] Run repository lint.
- [x] Review documentation impact: no documentation change is needed
because Markdown syntax and rendered behavior are unchanged.
- [x] Review fixture impact: no fixture inputs or generated fixture
bundles are affected.
2026-09-18 11:14:59 +02:00
Oleg Isonen e7181fffdc feat: Add GitHub-style Alert component (#6279)
## Summary

- Add one official `Alert` component with a content-editable `variant`
property for note, tip, important, warning, and caution.
- Register Alert in the component catalog with a generated title
descendant and Storybook story.
- Render GitHub-style alerts in Markdown Embed using the same DOM,
class, role, and variant contract.
- Materialize connected Content Block MDX alerts as actual Alert
instances and serialize content or variant edits back to `> [!TYPE]`.
- Document the component and both Markdown workflows.

## Implementation

Alert labels and variants live in Content Engine and are shared across
the component, Markdown renderer, and MDX adapters.

1. The Alert React component owns the wrapper, generated title, stable
classes, `role="note"`, and `data-variant`.
2. Markdown Embed transforms candidate micromark HTML before the
existing sanitizer and emits the same markup contract. It remains
sanitized HTML rather than a Builder instance tree.
3. Content Engine retains alert metadata on parsed Markdown blockquotes.
Project Build maps those nodes to Alert instances, reconciles their
authored children, and restores canonical GitHub syntax during MDX
serialization.
4. The component registry exports one Alert insertion template with a
styleable title descendant.

Unsupported, lowercase, misplaced, and malformed markers remain ordinary
blockquotes. There are no schema, migration, persisted-data, or
sanitizer-policy changes.

## Checklist

- [x] Add Alert as an official component with one Variant property.
- [x] Support all five GitHub alert types.
- [x] Share alert metadata and marker recognition.
- [x] Preserve nested Markdown and ordinary blockquote behavior.
- [x] Keep URL and HTML sanitization in the existing security boundary.
- [x] Add shared, title, and type-specific Markdown Embed descendants.
- [x] Materialize Content Block alerts as Alert instances.
- [x] Preserve alert content and variant changes through MDX
serialization.
- [x] Regenerate component props and Storybook artifacts from source.
- [x] Review documentation impact and update Alert, Markdown Embed,
Content Block, and navigation docs.
- [x] Verify fixture impact; no fixture inputs or generated fixture
outputs are changed.

## Verification

- [x] New tests demonstrated failure before implementation and pass
afterward.
- [x] `pnpm --filter @webstudio-is/content-engine test` — 417 passed.
- [x] `pnpm --filter @webstudio-is/project-build test` — 2,282 passed.
- [x] `pnpm --filter @webstudio-is/sdk-components-react test` — 144
passed, 1 skipped.
- [x] SDK component registry tests.
- [x] Relevant package typechecks, declaration builds, and production
builds.
- [x] Root lint, formatting, package-boundary, generated-doc, and diff
checks.
- [x] GitHub Actions main, E2E, lint, submodule, deployment, and
approved visual-regression checks.

## Known limitations

Alert identifiers must be uppercase and appear at the start of the
blockquote's first paragraph, matching the documented syntax. Markdown
Embed emits the component's markup contract but cannot create Builder
instances inside sanitized HTML.

Closes #6278
2026-09-18 00:42:50 +02:00
Oleg Isonen 18d171212f feat: Resolve asset paths in HTML embeds (#6409)
## Description

- Resolve root-relative script, stylesheet, image, and other asset
attributes in HTML Embed markup.
- Use the same asset-path map in Builder Canvas and generated published
sites.
- Keep SaaS asset proxy URLs deployment-relative instead of hard-coding
the `p-….apps.webstudio.is` project origin.
- Preserve canonical `/cgi/asset/` versus `/cgi/image/` routing and
`?format=raw` behavior in Canvas.

## Regression coverage

Added coverage for SaaS prebuild URLs, the real Builder
asset-map-to-HTML-Embed integration, every supported asset attribute,
HTML character references, and responsive-image `srcset` edge cases.

## Verification

- [x] CLI prebuild suite: 69 passed
- [x] HTML Embed suites: 63 passed
- [x] Full Builder browser suite: 2,646 passed
- [x] Asset URL map tests: 2 passed
- [x] CLI, Builder, React SDK, and project-build typechecks
- [x] Lint and package-boundary checks
- [x] Previous PR CI: 29 successful, 1 skipped

## Code Review

- [x] Self-reviewed
- [x] Added regression and integration tests
2026-09-17 20:38:40 +02:00
Oleg Isonen 60c549c46f fix: preserve local styles and improve issue diagnostics (#6408)
Fixes #6386

## Summary

- Keep local `ws:style` declarations after `ws:tokens` so instance
styles override token defaults.
- Capture privacy-safe HTTP timing/status, exceptional session namespace
states, preview freshness/version agreement, and validation paths in
issue reports.
- Derive reportable validation roots from the public operation catalog
instead of maintaining a duplicate allowlist.
- Keep captured failures current by clearing them after a successful
tool call.

## Verification

- Protocol issue-report tests: 3 passed
- Project build MCP: 128 tests passed
- CLI project session: 27 tests passed
- Builder issue reporter: 5 tests passed
- Relevant package typechecks passed
- Generated API metadata is clean
2026-09-17 20:13:27 +02:00
Oleg Isonen dd4bbabf79 fix: preserve text animation content during cleanup (#6383)
Updates the animation component to include
webstudio-is/sdk-components-animation#35.

On canvas, HTML Embed can replace its DOM before Text Animation cleanup
runs. Cleanup now avoids detached sibling references and preserves the
replacement content.

This PR currently points to the reviewed branch commit from animation PR
#35 and should merge after that PR. If #35 is squash-merged, the
submodule pointer must be refreshed to its resulting main commit.

Tests:
- Focused browser tests pass in Chromium and Firefox: 14/14 assertions.
2026-09-17 14:59:02 +02:00
Oleg Isonen fc15ebf81a perf: avoid repeated MDX parsing in publish diagnostics (#6407)
## Description

The cold `build.contentDatabasePublishDiagnostics` path was parsing the
same reachable MDX documents while the publication plan converged, then
parsing them a third time to produce omission diagnostics. Content-heavy
projects can approach or exceed the 15-second serverless limit on that
first request.

This change:

- creates a request-scoped MDX dependency resolver that reuses parse
results by asset revision while the publication plan converges;
- carries the final template omissions out of the existing publication
pass, so the diagnostics endpoint does not walk the artifact again;
- keeps the existing sequential asset validation/preparation/stability
checks unchanged;
- does not use module-level caching or depend on a warm serverless
instance;
- leaves the timeout error handling from #6406 unchanged.

## Performance evidence

An isolated cold-path benchmark used the same MDX payload (2,000
sections) and compared the previous three independent
dependency-resolution walks with the new request-scoped convergence
path:

| path | time |
| --- | ---: |
| before | 1,178 ms |
| after | 404 ms |

That is a 66% reduction in the measured MDX resolution phase. The tests
also assert the underlying work reduction directly: an unchanged
revision is parsed once across convergence passes, while a changed
revision is parsed again. For a stable 214-document candidate set, this
removes up to 428 of the previous 642 parse operations.

## Verification

- `pnpm --filter @webstudio-is/project-build test` — 2,420 tests passed
- `pnpm --filter @webstudio-is/builder exec vitest run --project
core-server` — 336 tests passed, including server-side e2e runner tests
- `pnpm --filter @webstudio-is/project-build typecheck`
- `pnpm --filter @webstudio-is/builder typecheck`
- `pnpm lint`
- `pnpm check:package-boundaries`
- `pnpm check:generated-api`

## Code Review

- [x] made a self-review
- [x] added comments for the request-scoped cache and revision safety
- [x] added tests for parse reuse, revision invalidation, and the
optimized endpoint integration path

## Before merging

- [ ] tested on a deployment preview with a production-sized content
project

## Additional content-read optimization

Successive compilation plans now share already-fetched, revision-keyed
content bytes within one callback-scoped preparation session. The byte
store:

- is created inside one asset-stability attempt and discarded when that
attempt finishes;
- is not stored on the repository, in a module, or outside the request;
- remains bounded by the existing 2 MiB hydration limit;
- refetches changed revisions and starts clean on stability retries.

Tests verify that two overlapping plans read unchanged content once, a
revision change forces another read, a new session reads again, and a
stability retry creates a new session. A fully overlapping two-pass
workload therefore performs up to 50% fewer object-storage content
reads.

Additional verification:

- `pnpm --filter @webstudio-is/asset-uploader test` — 292 tests passed
- `pnpm --filter @webstudio-is/asset-uploader typecheck`

## Remaining optimization assessment

The percentages below estimate whole-request improvement and are
workload-dependent; they are not additive.

| optimization | estimated improvement | complexity | decision |
| --- | ---: | ---: | --- |
| Request-local content-byte reuse | 10–35% | 3/10 | Implemented |
| Stop when the initial dependency plan is already a fixed point | 5–15%
for static/known MDX; ~0% for dynamic discovery | 2/10 | Implemented |
| Reuse a canonical source snapshot/materialized entries across
different plans | 15–30% | 8/10 | Deferred: large semantic and
invalidation surface |
| Reuse the bundle's initial asset-data snapshot | 1–5% | 5/10 | Not
worthwhile |
| Add a diagnostics-specific bundle loader | 2–8% | 6/10 | Not
worthwhile for this PR |
| Parallelize or skip unrelated user/project metadata | 0–3% | 2/10 |
Not meaningful |
| Combine collection validation and reserved-asset inspection | 0–5%
normally | 4/10 | Only meaningful for projects with many collections |

The fixed-point fast path is covered by a test asserting one artifact
preparation and one dependency-resolution call when the original plan is
already stable.
2026-09-17 10:47:48 +02:00
Oleg Isonen ea17eaa3c4 docs: explain the complete Content Engine workflow (#6403)
## What changed

- Add the complete Content Engine video tutorial to the main guide.
- Explain the launch benefits before the step-by-step setup: file-based
content, visual MDX editing, designed templates, collection entry forms,
and MCP automation.
- Document collection entry creation and editing in Content mode.
- Explain content collection folders in the Assets guide.
- Document that shared Slots cannot be used inside Content Block
Templates.
- Add missing links between Content Engine and Content Block
documentation.

## Release documentation audit

Reviewed the shipped MDX-backed Content Block, MDX template, structured
collection entry, collection authoring, query diagnostic, publication
warning, and asset-path changes. The detailed feature docs already cover
their implementation-specific behavior. This PR fills the remaining
discovery and workflow gaps across the main Content Engine, Assets,
Modes, and Content Block pages.

## Todo

- [x] Add the launch video to the Content Engine guide.
- [x] Update the main launch value proposition and supported scale.
- [x] Document collection workflows in Assets and Content mode.
- [x] Document the shared-Slot limitation for Content Block Templates.
- [x] Review documentation impact and navigation.
- [x] Verify generated documentation remains clean.
- [x] Verify changed relative links resolve.

## Verification

- Content Engine documentation tests: 2 passed
- Generated Content Engine and MCP documentation checks: passed
- Documentation TypeScript check: passed
- Relative link check: passed
- Git diff whitespace check: passed

The repository formatter excludes these GitBook Markdown files, so the
pre-commit formatting task had no eligible targets. No fixture inputs or
generated fixture outputs are affected.
2026-09-17 10:24:49 +02:00
Oleg Isonen 0d448313ab fix: harden MCP mutations and validation (#6402)
## Summary

Fixes the reproducible MCP regressions reported today:

- validate font upload metadata locally instead of surfacing an HTTP 500
- replace only the addressed child when `insertIndex` is supplied
- support cross-parent duplication
- bind data-variable assignments in actions
- reject string-valued `style` bindings
- include descriptions for every advertised MCP tool
- preserve resource data-source exposure on full updates
- render mixed expression/element children in the Builder canvas
- preserve explicit outer Collection references in nested scopes
- reject unsupported `update-page-settings` fields instead of dropping
them
- reject unavailable asset-query variables and log server error details

Closes #6390
Closes #6391
Closes #6392
Closes #6393
Closes #6394
Closes #6395
Closes #6398
Closes #6399
Closes #6401
Closes #6404
Closes #6405

## Verification

- 484 project-build tests
- 62 CLI tests
- 35 Builder browser tests
- 25 SDK tests
- project-build, Builder, CLI, and SDK type checks
- root lint and generated runtime contracts
2026-09-17 10:16:11 +02:00
Oleg Isonen aa1587c100 fix: Clarify publish timeout errors (#6406)
## Summary

- detect pre-publish HTTP timeouts from the response metadata already
attached to `TRPCClientError`
- show a clear publish-specific message instead of the JSON parser error
- cover both HTTP 504 and Vercel timeout responses

## Why

When `build.contentDatabasePublishDiagnostics` exceeds the serverless
function duration, the proxy returns HTML or plain text. tRPC's JSON
parser fails, but the resulting client error still contains the original
HTTP response. The publish flow now uses that response to report:

> Pre-publish checks timed out. Publishing was not started. Please try
again.

This PR intentionally does not change the shared tRPC transport or claim
to solve the underlying diagnostics performance problem.

## Verification

- `pnpm --filter @webstudio-is/builder exec vitest run --project
core-browser-builder-features
app/builder/features/publish/publish-error.test.ts`
- `pnpm --filter @webstudio-is/builder typecheck`
- `pnpm exec oxlint --deny-warnings <changed files>`
- `pnpm exec oxfmt --check <changed files>`
2026-09-17 09:30:08 +02:00
Oleg Isonen f23007457d fix(content-engine): normalize asset path filters (#6389)
## Description

Fixes Assets path filters so authors can use visible folder and file
names while canonical stored paths remain URL-encoded.

- normalizes path filter operands segment by segment without double
encoding
- preserves already encoded filters and literal string matching behavior
- covers equality, inequality, membership, contains, starts-with, and
ends-with operators with spaces and URL-significant characters
- verifies local/Preview database execution and published runtime return
the same result
- documents visible path input in the generated Content Engine reference

Closes #6388

## Steps for reproduction

1. Create an Assets folder named `Tutorial posts`.
2. Add an MDX file inside it.
3. Create an Assets system resource with `path starts with "Tutorial
posts/"`.
4. Preview the query and expect the MDX file to match.
5. Change the value to `Tutorial%20posts/` and expect the same result
for backward compatibility.

## Regression proof

Before the implementation, the focused reproduction failed because the
matcher returned no items: expected `["alpha"]`, received `[]`. With the
fix, the expanded path-operator regression suite passes.

## Verification

- [x] Content Engine test suite: 47 files, 577 tests passed
- [x] Content Engine typecheck
- [x] CLI typecheck
- [x] Repository lint with warnings denied
- [x] Formatting check for every changed source/test file
- [x] Content Engine documentation test
- [x] Content Engine and MCP documentation generation is idempotent
- [x] Webstudio Features fixture generation is idempotent and typechecks
- [x] No agent evaluations were run

## Documentation impact

Updated the authoritative CLI Content Engine reference source and
regenerated the Builder Content Engine and MCP documentation. The new
guidance tells users to enter visible path names and notes that existing
encoded filters remain supported.

## Code Review

- [ ] hi @kof, I need you to do
  - conceptual review (architecture, feature-correctness)
  - detailed review (read every line)
  - test it on preview

## Before requesting a review

- [x] made a self-review
- [x] added inline comments where behavior is not obvious

## Before merging

- [ ] tested on preview environment (preview dev login: 0000)
- [ ] updated test cases document (not needed; automated regression and
authoritative user docs were updated)
- [x] added tests
- [x] no new environment variables
2026-09-15 19:01:27 +02:00
Oleg Isonen 3531089965 fix: fall back to pnpm for generated previews (#6382)
## Description

Fixes #6381.

Generated preview previously defaulted to npm whenever launcher metadata
was unavailable, even when npm was absent and pnpm was installed.
Resolve npm from PATH first and fall back to pnpm, using the same
resolver for dependency installation and preview commands.

The React Router Vite plugin is already declared by the generated
template, so no dependency duplication or compatibility wrapper is
needed.

## Verification

- 47 focused preview/package-manager tests pass
- CLI typecheck passes
- repository lint passes
- formatting and generated CLI docs are current

## Code Review

- [x] made a self-review
- [x] added regression tests
- [x] no new environment variables
2026-09-14 16:57:17 +02:00
Oleg Isonen b7a11e4bc2 feat: Delay canvas scripts until resources render (#6380)
HTML Embed scripts in canvas and preview now wait until the complete
resource queue settles. Their markup remains rendered while loading;
only executable scripts are delayed, so scripts can read the committed
resource-backed DOM without requiring a custom event.

## Implementation

- Add an internal `$resourcesState` atom with `pending` and `settled`
states, driven by the complete resource queue.
- Synchronize the atom from the Builder to the canvas iframe.
- At the Builder component-render boundary, pass only HTML Embed the
private prop `$ws$executeScripts={isSafeMode !== true && resourcesState
=== "settled"}`.
- In the SDK HTML Embed component, default `$ws$executeScripts` to
`true`, keep markup inert while it is false, and execute scripts after
React commits the transition to true.
- Treat failed requests as settled so scripts cannot wait forever.
- Keep the private prop out of generated component arguments and
metadata through the existing `$`-prefixed prop convention.
- Keep published rendering unchanged because published sites do not pass
the private Builder prop.
- Keep the existing standard `DOMContentLoaded` / `load` compatibility
and sequential script queue.
- Remove the canvas wrapper and component-registry override.

## Todo

- [x] Track collective resource readiness in the Builder.
- [x] Synchronize readiness into the canvas.
- [x] Delay HTML Embed scripts in both canvas and preview.
- [x] Combine safe mode and resource readiness into one private
execution prop.
- [x] Preserve rendered HTML while scripts are delayed.
- [x] Preserve pages with no resources and failed-resource behavior.
- [x] Keep the private prop out of user-facing component metadata.
- [x] Add resource-state, sync, Builder-prop, and browser regression
tests.
- [x] Review documentation impact; no user-facing documentation changes
are needed.
- [x] Review fixture impact; no fixture inputs or generated outputs are
affected.

## Verification

- SDK HTML Embed browser tests — 44 passed
- Builder private-prop tests — 15 passed
- Resource tests — 28 passed
- Sync tests — 14 passed
- React SDK, SDK components, and Builder typechecks — passed
- Focused Oxlint and formatting — passed

Closes #6379
2026-09-14 15:53:26 +02:00
Oleg Isonen 9963c95ef8 fix: Sync OAuth profile fields on login (#6376)
Closes #6375

## Summary

- Refresh username, avatar, and provider fields when an existing user
logs in through OAuth.
- Avoid unnecessary database writes when profile fields are unchanged.
- Preserve existing development-login behavior.
- Add regression coverage for changed GitHub profile data.

## Testing

- `pnpm --filter @webstudio-is/builder exec vitest run --project
core-server app/shared/db/user.server.test.ts`
- `pnpm --filter @webstudio-is/builder typecheck`
2026-09-14 12:29:10 +02:00
Oleg Isonen 2aec79774e fix: focus spacing input when popover opens (#6374)
## Description

Restore automatic focus to the CSS value input when the spacing popover
opens.

The property label became focusable after its migration to
`ResettableLabel`, making it the first tabbable element in the popover
and causing Radix to focus the label. Marking the intended value input
with `autoFocus` makes React focus it as the popover content mounts;
Radix then detects that focus is already inside its focus scope and
preserves it.

## Steps for reproduction

1. Select an element.
2. Open the Style panel spacing section.
3. Click any margin or padding value.
4. Verify focus is placed in the CSS value input.

## Verification

- [x] Builder typecheck
- [x] Prettier check
- [x] Oxlint
- [x] `git diff --check`

## Code Review

- [ ] conceptual review
- [ ] detailed review
- [ ] test on preview
2026-09-14 12:26:50 +02:00
Oleg Isonen bd414eb53e fix: Fix text animation crash with leading HTML embed text (#6378)
Updates the animation component to include
webstudio-is/sdk-components-animation#34.

An HTML Embed that starts with raw text no longer crashes when placed
inside Text Animation. The component now restores adjacent text nodes in
a safe order during cleanup.

Tests:
- Focused browser test passes in Chromium and Firefox: 12/12 assertions.
2026-09-14 11:17:01 +02:00
Oleg Isonen 649ee3669d fix: Batch asset queries during project import (#6368)
## Problem

Project import sent every asset filename in one database request. Large
projects produced an oversized URL and failed before assets could
upload.

Closes #6367

## Changes

- [x] Reuse the existing encoded filename chunking rule for imports
- [x] Batch imported file lookup and restore requests
- [x] Add regression coverage for long filename lists
- [x] Review documentation impact: no user-facing documentation changes
are needed

## Verification

- [x] Builder import tests: 21 passed
- [x] Asset uploader tests: 50 passed
- [x] Builder and asset-uploader typechecks
- [x] Lint, formatting, and diff checks
- [x] Real CLI import passed the original metadata lookup and began
uploading 380 assets
- [ ] Agent evaluations were not run; they require separate approval

The real import later reached the target project's separate 350-asset
limit. That limit is outside this fix.
2026-09-11 23:33:09 +02:00
Oleg Isonen 5d34b77fac feat: Improve collection entry authoring (#6361)
Collection authors and editors get clearer field help, direct entry
navigation, and actionable validation instead of warnings that point at
the wrong place.

Closes #6360

## Implementation

- [x] Keep resettable help open after a click while the label remains
hovered
- [x] Stop repeating the property label inside resettable help
- [x] Store JSON Schema field descriptions and show them in entry forms
- [x] Add an optional dynamic collection entry page for explicit canvas
navigation
- [x] Rename entry file actions to **Edit file**
- [x] Add **Open on canvas** to entry menus and Entry settings
- [x] Put the entry filename in the Entry settings title
- [x] Make collection validation banners explain the problem and open
the first affected entry
- [x] Remove false output-only `UNOBSERVED_FIELD` warnings while keeping
filter and sort diagnostics
- [x] Encourage useful field descriptions in general MCP collection
guidance
- [x] Update Content Engine documentation
- [x] Confirm no database migration is required
- [x] Regenerate the affected asset-query runtime fixture
- [ ] Visually review the affected Storybook states in light and dark
themes
- [ ] Run the complete local agent evaluation suite
- [x] Run broader package and Builder checks
- [x] Complete final diff review

## Technical notes

- `entryPageId` lives in `collection.json` under `x-webstudio`; no
database schema changes are needed.
- Entry creation still leaves the current canvas unchanged. Canvas
navigation only happens after the user selects **Open on canvas**.
- An entry page must contain exactly one URL parameter. The entry
filename supplies that parameter, so the page can identify the selected
entry.
- Output projection fields are schemaless and may be produced after
reference resolution, so catalog absence is no longer reported there.
Filter and sort fields still report catalog diagnostics.
- The structured-query runtime change affects the generated
`webstudio-features` fixture, which is regenerated in this PR.

## Verification

- 561 Content Engine tests
- 2,413 Project Build tests
- 253 Design System tests
- 1,794 Builder tests
- 67 focused Builder regression tests after the final navigation change
- 11 ResettableLabel browser tests
- 128 focused MCP tests
- Builder, Content Engine, Design System, and Project Build typechecks
- Repository lint and package-boundary checks
- Generated documentation checks
- Full workspace fixture build with `pnpm -r fixtures:build`
- Formatting and diff checks
- New regression tests were demonstrated failing before their fixes and
passing afterward

## Documentation impact

- [x] Updated the Content Engine guide for field descriptions, dynamic
entry pages, actionable validation, **Edit file**, and **Open on
canvas**
- [x] Updated MCP guidance so collection fields include editor-facing
descriptions
2026-09-11 19:25:04 +02:00
Oleg Isonen d6375096ff fix: improve asset upload retries and diagnostics (#6364)
## Summary
- retry only transient asset upload failures after a short backoff
- return ASSET_UPLOAD_RETRY_EXHAUSTED with retryability and HTTP status
for persistent failures
- preserve successful batch uploads and support retrying the failed JPEG
alone

## Verification
- pnpm --filter @webstudio-is/http-client test -- src/index.test.ts
- pnpm --filter @webstudio-is/http-client typecheck
- pnpm --filter webstudio test -- src/error-codes.test.ts

Fixes #6363
2026-09-10 23:01:51 +02:00
Oleg Isonen 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
2026-09-10 21:32:14 +02:00
Oleg Isonen eb61139e5a feat: Scale publishing allowance with workspace seats (#6352)
Builder already counts production builds for the project owner,
including publishes created through MCP and CLI. This change scales the
allowance used by Builder with the owner's paid seat capacity, so five
seats at 100 publishes each give 500 publishes per day. Free stays at
10.

Closes #6351

## Implementation

The existing `userPublishCount` endpoint still reads the owner-wide
`user_publish_count` view. For a project, it now returns the project
owner's base allowance multiplied by the owner, included member seats,
and purchased extra seats. Builder uses that returned allowance in its
existing publish UI.

The count remains owner-wide. MCP and CLI publishes contribute to it
because they create production build records, but their publish
endpoints do not enforce this Builder-side limit. No database, server
publish-path, dependency, fixture, or generated-file changes are
included.

## Checklist

- [x] Calculate the paid allowance from the owner, included seats, and
purchased extra seats.
- [x] Keep Free at 10 publishes per day.
- [x] Reuse the existing owner-wide publish count.
- [x] Use the calculated allowance in Builder.
- [x] Review documentation impact; no Builder documentation changes are
needed for this small allowance update.
- [ ] Update the pricing page to explain the publishing allowance and
how seats increase it.
- [x] Review fixture impact; fixture inputs and outputs are unchanged.
- [x] Review the final diff for unnecessary changes.

## Verification

- The project router tests pass, including the five-seat 500-publish
allowance.
- The new assertion fails against the previous implementation and passes
with this change.
- Builder browser tests passed.
- Project and Builder typechecks passed.
- Formatting, lint, and package-boundary checks passed.
- All GitHub Actions workflows passed.
- Agent evaluations do not cover this flow and were not run.
2026-09-10 18:39:54 +02:00
Oleg Isonen 2c51bc9928 fix: Restore Cloudflare publishing with Wrangler 4 (#6350)
Publishing fails because Wrangler 3 cannot parse the JSON import
attributes used by `html-tags@4`. Upgrade the CLI and Cloudflare
template to Wrangler 4.130.0 and regenerate the fixture and lockfile
used to package the publish artifact.

Wrangler 4 also rejects Worker deployment when it finds the Pages
configuration. Add a SaaS-only `wrangler.jsonc` that takes precedence
over the Pages template's TOML configuration. Publish commands still
supply the Worker name and compatibility date. Fix the staging smoke
check's compatibility-date scope and shell expansion.

- [x] Upgrade Wrangler and regenerate the publish fixture and lockfile.
- [x] Reproduce the original parser failure and verify it passes with
Wrangler 4.
- [x] Build the packaged artifact and pass the staging smoke check and
both SaaS publish dry runs, including dispatch-namespace arguments.
- [x] Pass 383 existing template tests, CLI and fixture typechecks,
repository lint, and all fixture generation from checked-in data.
- [x] Review documentation impact: no user-facing documentation changes
are required; the existing Node 22.12+ requirement remains sufficient.
- [ ] Complete staging CI and manually test publishing.

[Publish artifact
build](https://github.com/webstudio-is/webstudio/actions/runs/34400149252)
is running; [staging
deployment](https://github.com/webstudio-is/webstudio/actions/runs/34400149301)
is queued. Remix 2 still declares a Wrangler 3 peer dependency; the
packaged production build and both publish dry runs passed with v4.
Agent evaluations were not run.
2026-09-09 22:28:38 +02:00
Oleg Isonen 13952a7d34 fix: Preserve Global root tokens when importing templates (#6348)
Inserting the CMS Sitemap marketplace page could detach existing Global
root tokens and break styles across existing pages. Preserve the root's
existing style-source selections in their original order, then append
mapped incoming sources without duplicates. Incoming local declarations
continue to merge into the existing root local source.

The MDX save fix is now included in main through
`commitAssetContentUpdate`. This PR retains an E2E assertion that saved
MDX revisions are not persisted again through `build.patch`.

Closes #6347

Verification and delivery:

- [x] Add four regression cases covering empty, local-only, token-only,
and mixed incoming root selections, including repeated imports, token
remapping, and local declaration merging. All four failed before the fix
and pass after it.
- [x] Rebase onto main and rerun style-copy and page-copy tests: 55
passed. Formatting and diff checks passed.
- [x] Verify the cloned project locally: reproduce the broken Home
canvas before the fix and restore it with Undo. After the fix, insert
Sitemap and verify Home retains its logo sizing, typography, spacing,
rounded corners, and both Global root tokens after reload.
- [x] Verify the MDX E2E assertion fails with duplicate persistence and
passes with the fix. Before rebase, all 13 shard 4 tests passed locally
with four workers, along with relevant typecheck and lint checks.
- [x] Resolve the rebase conflict using main's shared MDX implementation
and remove the duplicate helper from this branch.
- [x] Review documentation impact against marketplace, CSS-variable, and
Content Block documentation: no documentation changes required.
- [x] Review fixture impact: no fixture inputs, serialization formats,
or generated contracts changed.

No migrations are required. This prevents future imports from dropping
root selections; it does not reconstruct selections already lost in
previously saved projects.
2026-09-09 21:43:06 +02:00
Oleg Isonen e69a3d2f8e feat: Create structured MDX entries from collection folders (#6317)
## Paragraph editing follow-up — 42a16db02c (September 9)

Nested formatted paragraphs now retain their text, links, styling
identity, and existing labels during editing and saving. No database
migration.

- [x] Review all four changed text-editor files.
- [x] Preserve text before and after nested links inside emphasis,
strong text, and spans.
- [x] Preserve existing instance labels on save and keep inherited
formatting from overwriting a link's styling identifier.
- [x] Demonstrate regression failures without each fix, then pass all 41
focused text-editor and MDX synchronization tests.
- [x] Pass Builder typecheck, lint, formatting, and diff checks.
- [x] Verify the reported paragraph in Design and Content modes: edits
reach MDX and survive reload; original text restored. Verify matching
link color and underline inside and outside editing.
- [x] Review documentation and fixture impact: this restores existing
documented behavior; no fixture inputs, generated output, or
documentation changes are needed.
- [x] Commit and push the reviewed fixes.
- [ ] Confirm CI on this commit before merging.

Full E2E and agent evaluations were not rerun for this focused
follow-up. Earlier CI and suite results below apply to their recorded
commits, not this new head.

---

## Final verification — 50093ed2b7 (September 9)

Article edits now retain inline formatting and save newly inserted
custom components reliably. Saving a file no longer sends a second
project-sync write that could restore an older revision. No database
migration.

- [x] Fix iframe-blur saves, inherited template defaults, unused
resolved assets, and stale template tag metadata.
- [x] Remove duplicate server writes for already saved file revisions;
share the result with the canvas using the existing external-content
sync channel.
- [x] Demonstrate new regression tests fail without their fixes and pass
with them. Keep E2E file-write, content-order, and reload assertions;
remove the obsolete duplicate metadata-write wait.
- [x] Pass all 2,955 Builder tests, all 2,409 Project Build tests, and
all 8 focused MDX E2E checks.
- [x] Pass Builder and Project Build typechecks, lint, formatting, and
diff checks.
- [x] Run the complete fixture pipeline with no generated changes. The
later Builder-only save-acknowledgment changes affect no fixture inputs
or outputs.
- [x] Verify live article fields, hero replacement/save/reload/restore,
Accordion text edits, and YouTube URL edits. Verify UI edits reach MDX
and MDX edits persist after reload. Restore original article content.
- [x] Review documentation impact: these fixes restore the documented
Content Block editing behavior; no further documentation change is
needed.
- [x] Apply `visual-change-approved` after the user's explicit approval
of the 24 reviewed intended visual changes.
- [x] Commit and push the reviewed changes; worktree is clean.
- [x] Confirm CI and mergeability on this final commit: all 19 Main
workflow jobs passed, including all six E2E shards; PR lint and
submodule checks passed. Visual approval run 34383497017 passed. Its
predecessor reported the 24 intended differences; all 24 current images
were byte-identical to the reviewed report, with zero capture errors.
GitHub reports no merge conflict.

Agent evaluations were explicitly skipped for these final runtime fixes,
as requested. Earlier evaluation results below apply to their recorded
commits, not this final commit. This summary supersedes historical
pending-check notes below. The PR has not been merged.

---

## Final merge check — 9bc31ca679 (September 9)

- [x] Merge main without rewriting branch history. Preserve main's
image-picker fix and description-list validation.
- [x] Reset inherited HTML parent context at template boundaries. All
four new regressions fail without the fix and pass with it.
- [x] Fix shared navigation style composition and alignment; verify the
browser regression fails before and passes after. Review focused
screenshots.
- [x] Pass 2,408 Project Build tests, 251 design-system tests, and 3
image-picker tests; Builder, Project Build, and design-system
typechecks; lint, formatting, and diff checks.
- [x] Run the complete fixture pipeline with no generated changes.
- [x] Rerun all six agent evaluation scenarios on the merged code with
GPT-5.5. All functional checks pass; incompatible/missing token-baseline
comparison remains explicitly disabled.
- [x] Review documentation impact: this merge preserves documented
behavior; the image-editing guidance was already updated in f90e39cfcc.
- [x] Commit and push; worktree is clean and GitHub reports no merge
conflict.
- [x] Confirm Main CI on 9bc31ca679: all 19 jobs passed, including all
six E2E shards.
- [x] Review final visual artifact 10110845203 from run 34367911826: 24
added/changed stories, zero capture errors. Reviewed collection dialogs
and icons, light/dark login gradients, dashboard and project-settings
navigation, panel spacing, button variants, inset lists, and resettable
labels. Fourteen captures are byte-identical to the previously inspected
CI images. Updated navigation and collection-settings captures were
inspected; the formerly blank story renders normally. Pixel measurements
confirm the columns story retains its intended column positions (second
column x=261, third around x=388).
- [x] Apply visual approval after renewed explicit user approval.
Final-head CI is tracked above.

The live hero replacement and article/custom-component checks below are
now complete. The PR has not been merged.

---

## Merge-readiness follow-up — September 9

- [x] Remove contradictory image guidance and add an executable
writable-source/read-only-alt binding recipe. Regenerate CLI/MCP docs.
- [x] Verify the recipe regression fails with the old guidance and
passes with the new guidance. All 10 focused documentation tests, 2
content-reference tests, CLI/docs typechecks, lint, formatting, and
generated-documentation checks passed.
- [x] Rerun all six agent evaluation scenarios with GPT-5.5: all
functional checks passed. The incompatible/missing token-baseline gate
was explicitly disabled; existing baselines are unchanged.
- [x] Commit the documentation follow-up as f90e39cfcc.
- [x] Review existing CI screenshots and identify unintended shared-list
alignment changes. Implement a local shared-style composition/alignment
fix with a regression test demonstrated failing before and passing
after; all 251 design-system tests and its typecheck passed.
- [x] Visually verify and push the shared list fix in 9bc31ca679. The
focused run compared 28 stories; navigation is left aligned, column
layouts remain intact, and the previously blank collection-settings
story rendered normally. No product fix was inferred from that one blank
capture.
- [x] Review and approve the intended visual differences. See the
current final-head CI status above.
- [x] Verify these changes affect no fixture inputs or generated fixture
output. No database migration.

The previously pending live hero-swap and article/custom-component
checks are now complete. See the current verification summary above.

---

## Article editing follow-up — d307c8ba3d (September 9)

The target blog's hero source was incorrectly marked read-only. It now
uses the existing writable image picker. Reading time and the author URL
also use direct writable values. Time properties now have native
date/date-time controls. No database migration.

- [x] Fix the target's hero source, reading-time structure, and author
URL bindings while preserving the current content and link destination.
- [x] Verify Content-mode category, title, excerpt, date, reading-time,
shared image Description, author name/URL, MDX heading, and list-item
edits reach their source files. Check reloads and restore test values.
- [x] Add date-only, local timestamp, UTC timestamp, empty-value, and
legacy-value regression coverage; demonstrate failure without the date
control and passing results with it.
- [x] Fix the macOS Bash runner's empty-argument failure. Demonstrate
default invocation and argument-forwarding regressions fail without
their behavior and pass afterward.
- [x] Update authoritative Content Block/Time docs and generated CLI/MCP
guidance: inventory every article field, use direct writable bindings
and appropriate controls, check saved source and reload, and never infer
full editability from one heading.
- [x] Pass 291 date/text/Time/design-system tests (one existing skip), 2
runner tests, and 9 documentation tests; relevant typechecks, lint,
package boundaries, formatting, and diff checks.
- [x] Run the complete fixture pipeline: no generated fixture changes.
The later runner/docs-only edits do not affect fixture inputs or
outputs.
- [x] Run the full Builder E2E suite: 82 passed. This used an explicit
reporter; the default invocation correction was then covered by the
runner tests.
- [x] Run all six agent evaluation scenarios on gpt-5.5 after the final
image guidance correction. All functional checks passed. The older-model
token-baseline gate was disabled explicitly; accepted baselines were not
changed.
- [x] Review, commit, and push code and generated documentation.
- [x] With explicit approval, replace the live hero image through
Content mode, verify the MDX reference and reload, then restore and
verify the original image.
- [x] Complete the article-field checks listed above. Insert Accordion
and YouTube from templates; verify UI edits reach MDX and MDX edits
persist after reload. Restore the original article content.
- [x] Inspect CI for 7834d90a61: formatting, lint, typechecks, generated
docs, package/Builder tests, build checks, and five E2E shards passed.
The long MDX lifecycle exhausted its total 120-second budget at its
final assertion; the trace showed completed edits and reloads, not a
stalled save.
- [x] Give only that lifecycle a 180-second total budget; leave
individual save waits and assertions unchanged. Both local repeats
passed (plus setup). Push d307c8ba3d; this test-timeout-only follow-up
affects no fixture inputs or generated output.
- [x] Verify Main CI for d307c8ba3d: all 19 jobs passed, including all
six E2E shards. Visual approval remains separate.
- [x] Finish visual review and approval after correcting navigation
alignment and verifying the collection-settings story renders. The user
approved the reviewed 24 intended differences.
- [x] Recheck target publication preparation: seven documents included,
zero omitted, zero missing-template omissions. No deployment.
- Deferred: manual Storybook/screenshots; not authorized for this pass.

These results supersede the earlier pending image-product decisions and
live-check notes. Evaluation status for the latest fixes is recorded
above.

---

## Blog authoring fixes — 6359f979a3 (September 9)

This review fixes failed UI-to-MDX saves, missing source Undo/Redo,
editor recovery and focus, and publication preparation for the migrated
blog. No database migration.

- [x] Stop treating template-record ordering as a style edit; still
reject actual style changes that cannot be saved in MDX.
- [x] Share save-preparation errors with property controls across the
canvas frame.
- [x] Keep malformed MDX as an unsaved draft, with repair and discard
paths; focus the source editor when opened from the Navigator.
- [x] Enable supported Content-mode Move actions using the existing
central permission rules.
- [x] Keep source Undo/Redo separate from transient rendering history.
Serialize rapid commands, wait for pending canvas edits, reject
stale-source conflicts, and retain the last 20 edits per project
session.
- [x] Fix publication candidate discovery for projected documents and
unrelated author references; keep rejecting unsupported references used
as the actual MDX source.
- [x] Demonstrate regression tests failing without their fixes and
passing afterward.
- [x] Run the complete Builder (2,938), Content Engine (559), and
Project Build (2,395) suites: 5,892 tests passed.
- [x] Pass relevant package typechecks, root lint, formatting, package
boundaries, and diff checks.
- [x] Run the full fixture pipeline; no generated fixture changes.
- [x] Review documentation impact and document draft recovery and source
Undo/Redo.
- [x] Verify live heading edits, author-file edits, malformed-draft
recovery, and immediate delete/Undo/Redo; restore test content and
confirm persisted source.
- [x] Verify target publication preparation: seven documents included,
zero omitted, no missing-template omissions. This did not deploy the
site.
- [x] Review, commit, and push.

### Remaining verification and decisions

- [x] Correct the target's read-only Image source binding. The existing
Content-mode picker replaces the article reference; shared Description
edits use Asset settings. No new image-editing API or data model is
needed. See the latest verification limits above.
- [x] Repeat the full E2E and agent evaluation suites after these
changes: 82 E2E tests and all six evaluation scenarios passed.
- [x] Repeat the live Accordion/YouTube insertion and editing flow after
the save fixes, including UI-to-MDX saves and MDX edits surviving
reload.
- Historical note: this round did not run CI or Storybook checks; later
results are recorded above.

Unknown JSX already has source-range lint diagnostics; it remains an
editable missing-template placeholder, not an unconditionally blocked
save. The target author's binding was also repaired through the UI so
Content mode can edit its referenced author file. Shared resolved URLs
remain read-only text; the image picker changes the article reference,
and Asset settings edits the shared Description.

This section supersedes earlier claims of complete verification for the
latest code; historical results below remain records of earlier runs.

---

## Sync and import verification — a2a567551c (September 9)

Ordinary `link` → `sync` → `import` now exports the current saved
Builder project with its current assets. It no longer mixes an older
published build with current files. Import uploads assets before
assigning their source folders, then restores those assignments when
importing the bundle.

- [x] Fix foldered asset imports without a database migration.
- [x] Preserve file formats, folder assignments, build metadata, and
stable repeat-sync output.
- [x] Keep explicit `--buildId` export behavior unchanged; default sync
does not require publishing.
- [x] Review implementation and demonstrate regression tests failing
before the fixes and passing afterward.
- [x] Update protocol test servers for the current Builder data
endpoint.
- [x] Review documentation impact and update CLI documentation and
README.
- [x] Run all 1,121 CLI tests and 43 HTTP-client tests, both package
typechecks, root lint, package boundaries, formatting, and diff checks.
- [x] Run the full fixture pipeline and include expected current-build
fixture changes.
- [x] Verify the real source through the local CLI: repeated sync gives
identical output; import into the supplied target succeeds. Blog listing
and article rendering were also checked during the import investigation.
- [x] Commit and push the fix.
- Deferred at user request: agent evaluations for this round. Later CI
results are recorded above.

Sync still downloads the project and missing asset files and builds the
content index locally. This is not a transfer-free operation. Default
sync exports editable-build metadata rather than published deployment
metadata; use the explicit build option when selecting a particular
build.

---

## Final review and verification — ab3a6b7c55 (September 9)

Existing entries now use the same field controls as New entry. The
author-name permission fix, field-level validation, publishing warnings,
and final cache fixes are included. No database migration.

### Completed

- [x] Review all 38 local files, including entry autosave, revision
conflicts, permissions, shared fields, collection discovery, and
publishing diagnostics.
- [x] Allow existing-folder setup without rewriting entries or rejecting
unconfigured frontmatter.
- [x] Add Entry settings with autosave, repairable invalid fields,
immutable filename slugs, and preserved MDX body/unknown properties.
- [x] Warn before publication when missing or ambiguous custom templates
omit content, including discovered dynamic sources.
- [x] Allow authorized frontmatter writes to referenced Markdown author
files and preserve error messages across the canvas-frame boundary.
- [x] Reject impossible integer limits, slugless generated-filename
patterns, and slug maximum length zero. Existing raw configurations
remain readable for repairs.
- [x] Share collection discovery across Assets, file editors, and
property controls. Builder first synchronizes project asset metadata
into the browser. The browser then groups that metadata once per update
and shares configuration/template reads, instead of repeating them for
each control. This does not remove initial synchronization or
entry-frontmatter reads. Preserve project/access isolation, refresh
retries, stale-response protection, and duplicate-filename invalidation.
- [x] Demonstrate new regressions fail before fixes and pass afterward.
- [x] Correct the folder-drag E2E's obsolete root-folder assumption and
verify remembered-folder persistence.
- [x] Review and update authoritative Content Engine and Content Block
documentation.
- [x] Merge current main and regenerate the conflicting CLI
documentation from its source.
- [x] Fix generated previews with shared development dependencies:
invoke React Router binaries through Node, isolate each preview's Vite
cache, and forward script options correctly through npm and pnpm.
- [x] Fix a false MDX conflict when a new edit arrives immediately after
the previous save is acknowledged. Advance the local revision before
asynchronous parsing and ignore stale refresh results.
- [x] Run the full fixture link/sync/build pipeline. Generated changes
contain only the expected React Router scripts and Vite cache location.
No database migration.
- [x] Review the final diff and scan added lines for credential
patterns.

### Verification results

- Builder: **2,934 tests passed**. CLI: **1,120 tests passed**.
- Content Engine: **559 passed**; Asset Uploader: **291 passed**;
Project Build: **2,394 passed**; Design system: **250 passed**.
- The clean full workspace run passed all 35 non-CLI packages. Its two
CLI failures exposed pnpm argument forwarding; the complete CLI rerun
passed after that fix. Earlier load-related test timeouts did not recur
in the clean run.
- All workspace typechecks passed. Builder and CLI typechecks and
repository lint were repeated after their final changes. Package
boundaries, generated API checks, and the complete
generated-documentation gate passed. Documentation was regenerated from
source; the post-commit clean-file check passed.
- Full Builder E2E: **82 passed** from a fresh build, including
generated previews, collection creation/reload, MDX editing, stale
revisions, and permission checks. The isolated disposable test database
was cleaned up afterward; development data was untouched.
- Full agent evaluations: **all six scenarios passed on gpt-5.5**,
including every functional check for Markdown blogs, reference
discovery, MDX article editing, authentication, fonts, and design input.
The account cannot use the old **gpt-5.4-mini** baseline model. Five
baseline comparisons are incompatible and the MDX scenario has no
accepted baseline; token-baseline gating was disabled explicitly.
Accepted baselines are unchanged.
- New preview and MDX regressions were demonstrated failing before their
fixes and passing afterward.
- Working-tree/diff checks passed. Main was merged without rewriting
branch history. CI was not checked, as requested.

### Final delivery checklist

- [x] Finish the full Builder E2E rerun.
- [x] Finish the full agent evaluation rerun with the authorized
accessible model.
- [x] Commit, verify the clean generated-documentation gate, and push.
GitHub reports the branch mergeable with current main.

No known code or functional-test blocker remains from this review. CI
was not checked at the user's request. Manual Storybook/screen-reader
checks remain deferred; the automated E2E and agent visual checks above
passed.

The historical review sections below retain their original run results.
This section supersedes earlier claims about strict entry validation,
entry editing, browsing reads, and evaluation authorization.

## Outcome

Content editors can create structured MDX entries from a collection
folder without writing frontmatter. Designers configure flat fields,
limits, optional slug behavior, and the Markdown template in the
Builder.

## High-level goals

- [x] Keep folders ordinary in the database and derive collection
behavior from a direct **collection.json**.
- [x] Add **Use as content collection** to folder creation.
- [x] Give designers a visual configurator for the JSON Schema and MDX
template.
- [x] Show a collection badge and a generic **New entry** action.
- [x] Generate independent MDX entries from the configured schema and
template.
- [x] Keep the current page unchanged after creating an entry; no
automatic preview navigation.
- [x] Select direct MDX entries with URLPattern filename rules; keep
ignored files as ordinary supporting assets.
- [x] Keep reserved collection files out of collection query results and
generated content indexes.
- [x] Preserve broken collections as visible, repairable collection
folders.
- [x] Restore normal folder behavior when **collection.json** is
removed.

## Implementation todos

- [x] Add the Content Engine collection contract, default
schema/template, slug normalization, validation, serialization, and
entry generation.
- [x] Discover collections from Assets files without a database folder
type or migration.
- [x] Create **template.mdx** and **collection.json** when the folder
checkbox is selected.
- [x] Add basic field controls for text, long text, slug, number,
integer, and Boolean values.
- [x] Support required fields, string length limits, numeric limits,
template defaults, and generated slugs.
- [x] Add designer collection settings and template editing.
- [x] Remove dynamic preview-page configuration and ignore its legacy
metadata.
- [x] Add the schema-generated **New entry** form and creation endpoint.
- [x] Preserve reserved-file portability in authenticated export/import
while excluding it from content query and generated-index results.
- [x] Document the feature in the authoritative Content Engine guide.

## Deep review checklist

A checked item means the area was inspected and evidence was recorded.
It does not mean the behavior passed. Unresolved defects and product
decisions remain listed in **Review findings**.

### 1. Product contract

- [x] Decide whether the collection checkbox creates defaults
immediately or opens the configurator first.
- [x] Decide whether existing folders can be converted through the UI.
- [x] Define collection activation when **collection.json** exists but
is invalid.
- [x] Define an entry: every valid direct MDX child or only files
created through **New entry**.
- [x] Confirm collection membership is direct-child only and nested
folders can define their own collections.
- [x] Define designer, content editor, viewer, service, CLI/MCP,
old-client, and direct-API authority.
- [x] Decide whether content mode is simplified UI or a server
authorization boundary.
- [x] Decide whether slugs are permanently immutable.
- [x] Decide whether schema changes may invalidate existing entries or
must migrate them.
- [x] Decide whether invalid unused collections block unrelated queries
or whole-project publication.
- [x] Decide what happens to the preserved template after collection
removal.
- [x] Define the promised JSON Schema dialect and supported subset.
- [x] Decide whether temporary invalid collection states are admitted
for repair.
- [x] Define duplicate-request semantics: no duplicates, exactly one
success, and retry behavior.
- [x] Decide whether entries require valid frontmatter only or valid
complete MDX.

### 2. State machine

- [x] Review ordinary folders and root-level **collection.json**.
- [x] Review empty-folder, template-only, config-uploading, and
config-loading initialization states.
- [x] Review ready collections.
- [x] Review invalid JSON, UTF-8, size, schema, and duplicate-config
states.
- [x] Review missing, duplicate, wrong-type, oversized,
invalid-frontmatter, and invalid-MDX template states.
- [x] Review forbidden direct files, invalid entries, and slug/filename
mismatches.
- [x] Distinguish transient read/network failures from persistent
invalid content.
- [x] Review removing, removed, moved, renamed, restored, duplicated,
and deleted collection states.
- [x] For every state, inspect visibility, allowed actions, server
admission, query/publish behavior, recovery, and concurrent-client
behavior; record recovery behavior.

### 3. Activation and deactivation paths

- [x] Review checkbox creation.
- [x] Review create/upload/rename/move/paste/duplicate of
**collection.json**.
- [x] Review restore, import, project duplication, and sync behavior.
- [x] Review delete/rename/move/replace of **collection.json**.
- [x] Verify all activation paths produce the same state and validation.
- [x] Verify all deactivation paths restore ordinary-folder behavior
without accidental data loss.

### 4. Folder membership and nested collections

- [x] Verify only direct children are collection entries or reserved
files.
- [x] Verify a nested folder with its own direct config becomes its own
collection.
- [x] Verify parent rules do not leak into nested folders.
- [x] Define filename and extension case sensitivity.
- [x] Define duplicate logical-name behavior over generated storage
names.
- [x] Define how uploading, deleted, and stale assets affect folder
state.
- [x] Review moving entries out and between collections with different
schemas.
- [x] Review ordinary subfolders with arbitrary files inside a
collection.
- [x] Review template behavior after collection removal.

### 5. Roles and authorization

- [x] Review config/template listing and download for every role.
- [x] Review config/template content edits for every role.
- [x] Review config/template rename, move, restore, and deletion for
every role.
- [x] Review entry create/edit/rename/move/delete/restore for every
role.
- [x] Review generic upload and subfolder operations for every role.
- [x] Review collection removal and repair for every role.
- [x] Verify server enforcement separately from hidden or disabled
controls.
- [x] Recheck permission changes while operations are in flight.

### 6. Mutation surfaces

- [x] Review REST upload-ticket creation and upload completion.
- [x] Review asset-content replacement and asset metadata changes.
- [x] Review runtime add/update/duplicate operations.
- [x] Review sync patches and direct persistence helpers.
- [x] Review clipboard, drag/drop, move, rename, and duplicate paths.
- [x] Review import/export, restore, and project/folder duplication
paths.
- [x] Review delete-unused-assets and recursive folder deletion.
- [x] Verify invariants at the final persistence boundary, not only in
UI preflight.

### 7. Concurrent operations

- [x] Review create entry against schema save, template save, raw config
edit, and collection removal.
- [x] Review edit/delete/restore entry against schema and template
changes.
- [x] Review two collection-settings editors and raw editor versus
configurator.
- [x] Review schema writer A interleaved with template writer B.
- [x] Review upload/move/rename against collection activation and
removal.
- [x] Review template/config deletion against creation and publication.
- [x] Review folder deletion/move and project switch against all async
operations.
- [x] Review final validity, conflict detection, cleanup, user outcome,
cache invalidation, and publish behavior across the implemented
intersections; record conflict and recovery behavior.
- [x] Review retry after the server commits but the HTTP response is
lost.

### 8. Collection initialization and recovery

- [x] Review project switch, permission revocation, dialog close, folder
rename/move/delete, and conflicting uploads during initialization.
- [x] Verify retry is idempotent and does not duplicate reserved files.
- [x] Verify partial setup has a visible finish, retry, or cleanup path.
- [x] Verify generic actions cannot corrupt the initialization window.
- [x] Verify project and folder identities are captured for the whole
operation.
- [x] Review asset-count, storage, filename, and folder limits with
cleanup.

### 9. JSON Schema conformance

- [x] Confirm the exact draft and validate configs against its
meta-schema.
- [x] Review unknown dialects, boolean schemas, malformed keyword types,
and duplicate JSON keys.
- [x] Review refs/defs, recursion, composition, conditionals,
dependencies, and nullable/multiple types.
- [x] Review enum, const, format, pattern, patternProperties, and
additionalProperties.
- [x] Review min/max properties, array constraints, contains, and
uniqueItems.
- [x] Review exclusive numeric bounds and all string/number/integer
limits.
- [x] Differential-test validation against an independent draft 2020-12
implementation.
- [x] Verify unsupported keywords are rejected or explicitly documented,
never silently ignored.

### 10. Schema resource limits and hostile input

- [x] Bound schema bytes, depth, field count, enum size, and reference
depth.
- [x] Bound validator compilation and validation time.
- [x] Review recursive references and excessive composition branches.
- [x] Review catastrophic patterns and regex denial of service.
- [x] Review huge labels, keys, defaults, and values.
- [x] Review prototype-sensitive and structurally awkward field names.
- [x] Review memory and CPU amplification across many collections.

### 11. Configurator ownership and round trips

- [x] Define owned, preserved, read-only, and rejected schema paths.
- [x] Verify parse/save/parse semantic equivalence and repeated-save
idempotence.
- [x] Verify a UI edit changes only explicitly owned schema paths.
- [x] Review property/form order and required-array order.
- [x] Review unknown root, property, and Webstudio extension metadata.
- [x] Review editable primitive fields that also contain enum, const,
format, composition, readOnly, or custom constraints.
- [x] Review type changes for incompatible preserved or removed
keywords.
- [x] Review rename/removal collisions with unsupported properties and
requirements.
- [x] Surface hidden preserved rules that can cause unexplained
validation errors.
- [x] Review field ordering/reordering.
- [x] Review choosing/changing slug field, slug source, and template
filename.
- [x] Review turning newly added fields into slug or slug-source fields.

### 12. Required/default/unset semantics

- [x] Review absent, empty string, false, zero, null, default, and
user-cleared values for every field type.
- [x] Define required-property versus non-empty-value behavior.
- [x] Verify required Boolean false remains valid and understandable.
- [x] Review optional Boolean absence versus explicit false.
- [x] Review explicitly clearing template-backed values.
- [x] Review JSON Schema default as annotation versus persisted entry
value.
- [x] Verify precedence among template values, schema defaults,
generated slug, and user values.
- [x] Review hidden/non-renderable required fields and template
requirements.
- [x] Verify a collection cannot be ready when every entry form must
fail.

### 13. Schema evolution and migration

- [x] Review add/remove optional and required fields.
- [x] Review field rename and label-only changes.
- [x] Review type changes and tightened/relaxed limits.
- [x] Review enum option, default, slug field/source, template name, and
additionalProperties changes.
- [x] Verify compatible save ordering and identify impossible two-write
migrations.
- [x] Decide whether entries are rewritten and whether migration is
atomic.
- [x] Review entry changes after validation but before schema commit.
- [x] Ensure designers see all incompatible entries or an actionable
summary.

### 14. Template, YAML, and MDX

- [x] Review absent/empty frontmatter and empty YAML.
- [x] Review BOM, CRLF, final-newline, and alternate YAML delimiter
behavior.
- [x] Review duplicate keys, anchors, aliases, tags, comments, quoting,
and multiline strings.
- [x] Review Unicode and byte limits.
- [x] Verify frontmatter and starter-body preservation.
- [x] Review invalid MDX bodies, imports, exports, expressions, and
unsafe nodes.
- [x] Review missing, renamed, duplicate, wrong-type, and oversized
templates.
- [x] Verify template changes affect future entries only.
- [x] Review reserved template exposure through direct asset URLs and
build copying.

### 15. New-entry form and creation

- [x] Verify every required field is renderable or supplied safely.
- [x] Verify deterministic field order and correct controls/constraints.
- [x] Compare client and authoritative server validation.
- [x] Prevent submitted values from overwriting protected hidden fields.
- [x] Preserve values and focus after failed creation.
- [x] Review per-field errors, error summary, and first-error focus.
- [x] Prevent duplicate submission and define lost-response recovery.
- [x] Verify local sync, reload persistence, and project-switch
handling.
- [x] Review no/stale preview page, query delay, and draft-filtered
entries.
- [x] Review large forms and narrow/mobile layouts.

### 16. Entry editing and lifecycle

- [x] Review body-only, frontmatter-only, and combined edits.
- [x] Review invalid intermediate frontmatter and invalid complete MDX.
- [x] Review slug change, filename rename, and reference consequences.
- [x] Review delete, restore, move out, move between collections,
duplicate, paste, and undo.
- [x] Review slug reuse after delete and restore collision.
- [x] Review edits concurrent with schema/template changes.
- [x] Review repair of legacy invalid entries and blocked intermediate
saves.
- [x] Decide whether existing entries receive a generated settings form
or raw MDX editing.

### 17. Slug and filename identity

- [x] Review slug source selection, same slug/source field, manual
override, and visible final normalization.
- [x] Enforce lowercase-dash identity independently of editable schema
metadata.
- [x] Review empty normalized slugs and template/schema slug defaults.
- [x] Review diacritics, non-Latin scripts, and emoji-only sources.
- [x] Use locale-independent case comparison and Unicode normalization.
- [x] Review character/byte limits and case-insensitive filesystems.
- [x] Review collisions with reserved config/template filenames.
- [x] Review concurrent creation, stale reservations, deletion, reuse,
restoration, and URL encoding.

### 18. Collection-root restrictions

- [x] Review upload, external drop, internal drag, generic text
creation, paste, duplicate, move, rename, and extension changes.
- [x] Review runtime add, sync patch, import, and restore bypasses.
- [x] Review upload tickets completed after activation.
- [x] Review moving or renaming **collection.json** through metadata
changes.
- [x] Review dedicated entry creation, initialization, repair, body
editing, and collection removal exceptions.
- [x] Verify subfolder creation and arbitrary files inside subfolders
remain allowed.

### 19. Storage, database, idempotency, and cleanup

- [x] Review object-without-File, File-without-Asset, stale UPLOADING,
and soft-deleted states.
- [x] Review storage success/database failure and database
success/storage failure.
- [x] Review postcheck failure, object cleanup, row cleanup, and cleanup
failure.
- [x] Verify cleanup preserves the original error and cannot delete
another request's asset.
- [x] Review timeout/lost response after commit and safe retries.
- [x] Review reservation lookup/cleanup failure and expiration.
- [x] Review failed creation cleanup and distinguish incomplete uploads
from existing entries with repairable field errors. Existing field
errors do not hide entries or block publication.
- [x] Verify orphan reclamation and project-scoped cleanup.

### 20. Query, build, and publication

- [x] Verify config/template exclusion only for an identified
collection.
- [x] Verify ordinary root-level **collection.json** remains ordinary
content.
- [x] Review template queryability after collection removal.
- [x] Validate actual entry frontmatter and decide complete-MDX
requirements.
- [x] Review invalid content versus transient read failure.
- [x] Decide invalid-collection blast radius across queries, field
catalog, build, and publish.
- [x] Review direct asset IDs, document references, build copying, and
public URLs for reserved-file leakage.
- [x] Verify preview and publish use the same collection rules.

### 21. Cache and snapshot consistency

- [x] Verify config, template, entries, filenames, folder movement,
activation, and removal affect source revision.
- [x] Verify immutable/revision-pinned reads under concurrent swaps.
- [x] Verify config and template come from one logical version.
- [x] Review revision consistency between entry reads and indexing.
Queries do not require entry-schema validation.
- [x] Review rejected-promise caching and retry after repair.
- [x] Verify no cross-project cache collision.

### 22. Performance and availability

- [x] Review sequential versus bounded parallel reads.
- [x] Review request cancellation and repeated preview-query work.
- [x] Review rate limits and resource-exhaustion protection.
- [x] Review progress/cancellation and actionable limit errors.

### 23. Invalid-state repair

- [x] Review repair for every config, template, forbidden-file, entry,
and slug mismatch state.
- [x] Show exact folder, file, field, and rule.
- [x] Distinguish retryable transport errors from content errors.
- [x] Provide missing-template creation despite normal root
restrictions.
- [x] Provide duplicate-file deletion and forbidden-file move recovery.
- [x] Preserve raw repair when the configurator cannot parse the schema.
- [x] Show collection health after repair.
- [x] Prevent custom-template leakage to content editors when config
cannot identify it.

### 24. Entry navigation

- [x] Remove dynamic preview configuration and automatic navigation
after entry creation.
- [x] Verify entry creation leaves the selected page unchanged, with or
without a slug field.

### 25. Accessibility and responsive behavior

- [x] Review native form submission and required semantics beyond an
asterisk.
- [x] Review linked errors, error summary, live announcements, and busy
state.
- [x] Review focus after errors, add/remove field, key edits,
confirmation transitions, and dialog close.
- [x] Review dirty-close warning, explicit cancel/retry, and durable
feedback beyond toasts.

### 26. Portability, compatibility, and rollback

- [x] Review export/import, folder/project duplication, and restore
ordering.
- [x] Verify logical filenames, not storage names, define the portable
contract.
- [x] Review case-insensitive filesystem behavior.
- [x] Review rollback after collection files already exist.
- [x] Verify removal or rollback leaves content recoverable.
- [x] Confirm database/migration assumptions.

### 27. Observability and supportability

- [x] Review state-transition and validation diagnostics.
- [x] Track cleanup failures and orphaned reservations.
- [x] Make publish errors actionable without logging private content.
- [x] Ensure client errors do not expose stack traces or sensitive
internals.

### 28. Verification strategy

- [x] Unit-test schema, slug, merge, serialization, and validators.
- [x] Demonstrate each new regression test fails without its fix.
- [x] Run affected unit suites, typechecks, lint, docs checks, E2E,
fixture-impact review, and final diff review.
- [x] Review authoritative documentation impact.

## Persistence and recovery

No database migration is needed. Folders remain ordinary database rows;
their direct **collection.json** activates collection behavior. This PR
adds no table, column, index, RPC, folder type, or backfill.

- Setup writes the template first and **collection.json** last.
Interrupted setup leaves an ordinary folder and can be retried.
- Existing immutable File revisions, conditional Asset updates, and
upload reservations provide conflict detection and retry-safe entry
creation.
- Settings saves validate the intended template and schema together,
then write only the files that changed. Existing entry field errors stay
repairable and do not block unrelated saves, queries, or publication.
New entry creation remains strict.
- Invalid or interrupted collections remain visible and repairable.
Configuration/template structure errors remain distinct from entry-field
errors; entry-field errors do not invalidate the entire collection.
- Public/service publication excludes configuration and template files.
Authenticated CLI exports retain them for lossless import.

## Review findings

The implementation review is complete. Confirmed defects were fixed and
the following tradeoffs remain explicit.

### Fixed

- Enforced collection structure and permissions across REST, runtime
patches, import, query, and publication, while keeping existing
entry-field errors non-blocking for queries and publication.
- Preserved Undo/Redo, including restoring shared File revisions in
cloned projects.
- Rejected forged shared-file metadata while preserving metadata edits
on legacy files with null uploader provenance.
- Added retry and recovery behavior for setup, settings saves, lost
responses, and transient reads.
- Moved collection setup into one retry-safe server operation and kept
the local Builder update as one Undoable transaction.
- Centralized collection inspection and limited schemas to the flat
fields the configurator can represent. Opening a collection still reads
its entries in the background to report field errors.
- Guarded stale dialogs and deferred saves against permission changes,
protected files, project switches, and replaced revisions.
- Kept Asset settings open while choosing nested folders; the existing
browser regression fails before the fix and passes afterward.
- Preserved required Boolean false values, field-level errors,
dirty-close prompts, and inline Markdown editing.
- Separated authenticated portable exports from public runtime assets.
- Updated the authoritative Content Engine guide.

### Known tradeoffs

- A failure after template content saves but before configuration saves
can temporarily leave an invalid collection. Retrying the settings save
completes it; otherwise the visible files allow repair. The
template/config rename endpoint remains retry-safe and rolls back a
rename when its config write fails.
- Cloned-project Undo can retain an unused prior File revision. Separate
read-then-delete cleanup could race with another restore, so this path
retains the revision.
- Build/asset persistence and import already use multiple calls. This PR
validates before writes and provides retry/conflict handling; it does
not provide a transaction spanning every resource or a global collection
lock.
- Opening a collection loads its configuration and template through
shared Builder discovery, then checks entry frontmatter in the
background for field errors. Unchanged configuration/template content is
reused across controls and entry edits. Frontmatter checks still require
entry reads; ordinary folder browsing does not discover every
collection’s file content.

## Latest unstaged-change review — 2026-09-07

- [x] Review collection settings, validation, autosave/error recovery,
entry creation, reserved-file actions, and conversion flows.
- [x] Review shared labels, list keyboard handling, dialog focus/Escape,
buttons, and panel spacing migrations.
- [x] Fix Enter/Space list activation lost when arrow navigation
replaced the existing press handler.
- [x] Recognize historical generated ASCII/Unicode slug patterns, use
the bundled Unicode slug schema, and save a locally resolved $ref
without changing existing filenames.
- [x] Prevent duplicate slugless entries after a lost response: New
entry sends a stable UUID per attempt, reused by the repository's
existing filename/content retry checks. No database migration. Callers
omitting the optional requestId retain the previous behavior.
- [x] Preserve Content Block template validation independently of page
HTML placement, without accepting invalid rendered nesting.
- [x] Demonstrate the new review regressions fail without their fixes
and pass afterward.
- [x] Review authoritative documentation and update legacy slug
behavior.
- [x] Run fixtures and inspect generated changes: none.
- Deferred manual verification: Storybook screenshots were not
authorized for this pass.
- Historical evaluation status: not run in this pass; superseded by the
final verification section above.

## Verification

Local checks for commit **2b8195692e**:

- Builder app: 1,739 tests; Builder core: 1,150 tests. After the final
fixes, entry/settings tests and shared asset REST checks also passed,
including the added stable-retry test.
- Content Engine: 535 tests.
- Asset Uploader: 279 tests.
- Design system: 249 tests.
- Project Build: 2,392 tests.
- All five affected package typechecks, changed-file lint/formatting,
package boundaries, and Content Engine documentation tests passed.
- Complete fixture link/sync/build pipeline passed with no tracked
fixture changes.
- Staged diff and credential-pattern checks passed. No database schema,
migration, or generated database type changes. The lockfile includes the
collection validator and filename matcher dependencies.
- CI for this commit is pending. Builder E2E, manual
screen-reader/mobile checks, Storybook screenshots, and agent
evaluations were not run in this pass.

## MDX editing review — commit c3702a7238

- [x] Review all 36 local changed files: MDX parsing/materialization,
template enforcement, mutation commits, persistence, Assets refresh,
Navigator, source binding, editor diagnostics, dialogs, and query menus.
- [x] Validate new custom components at the shared Builder mutation
commit before applying tree changes. Cover move, reparent, paste, nested
paste, duplicate, and batched operations; preserve unchanged authored
content.
- [x] Match template names and component structure, including renamed
templates, instead of accepting any component type found in Templates.
- [x] Remove the registered-component fallback from MDX materialization
and completion; report untemplated JSX in the editor.
- [x] Preserve unresolved JSX until its placeholder is deleted, then
persist its removal without clearing the Assets resource during refresh.
- [x] Keep custom designer content outside the MDX body; omit the extra
body for fresh unconnected Content Blocks.
- [x] Resolve Navigator's MDX-file action from the active content root
for dynamic sources.
- [x] Fix the first dialog drag jump while honoring subsequent explicit
size changes; contain long query menus and prevent editor Backspace from
deleting instances.
- [x] Demonstrate new review regression failures before the fixes and
passing results afterward.
- [x] Review and update authoritative Content Block documentation.
- [x] Run the complete fixture pipeline; no generated fixture changes.
- Historical evaluation status: deferred in this pass; superseded by the
final verification section above.
- Historical verification limits: no Storybook screenshots, Builder E2E,
or manual accessibility checks in this pass. See the final verification
section for the current E2E result; manual checks remain deferred.

### Latest verification

- Builder affected suites: **624 tests passed**.
- Project Build full suite: **2,394 tests passed**.
- Design system full suite: **250 tests passed**.
- Query Builder React full suite: **14 tests passed**.
- Component Registry full suite: **7 tests passed**.
- All five affected package typechecks, repository lint, changed-source
formatting, staged diff checks, and credential-pattern scan passed.
- Fixture link/sync/build passed without generated changes. No database
migration.
- CI for this commit has not yet been verified.

The new mutation-commit check covers Builder operations affecting loaded
external content roots. It is not a claim that every remote API mutation
is now guarded by this client-side check.

Closes #6316


## Local-change review — 2026-09-08

- [x] Review all 47 changed files and trace collection setup, filename
filters, public metadata/upload APIs, sync patches, Content-mode write
targets, local folder persistence, and MCP downloads.
- [x] Fix ignored MDX files being renamed into entries through the
metadata API. Selected filenames require New entry.
- [x] Fix supporting-asset moves/renames being blocked by unrelated
invalid entries. Configuration/template checks still run. The later
entry-repair work also removed entry-field validation as a blocker for
unrelated writes and publication.
- [x] Validate frontmatter directly with Ajv, resolve the bundled slug
$ref without network fetching, preserve the flat schema contract, and
avoid retaining old validator instances.
- [x] Verify Unicode generation, legacy-pattern upgrades, sibling
constraints, field error paths, optional binding paths, referenced-file
permissions, and independent queued edits.
- [x] Review authoritative documentation, correct contradictory
referenced-write guidance, and regenerate CLI/MCP documentation.
- [x] Demonstrate new review regression failures with the fixes
disabled, then restore and pass them.
- [x] Pass Content Engine (553), asset uploader (284), SDK (570),
project-build (2,394), MCP command (61), and collection/Content-mode
browser tests (279), plus expression tests and documentation checks.
- [x] Pass seven affected package typechecks, repository lint,
formatting, package boundaries, and fixture link/sync/build. No
generated fixture changes or database migration.
- Historical evaluation status: deferred in this pass; the user
subsequently authorized the full suite. See the final verification
section above.
- Historical verification limits: no Builder E2E or manual
visual/accessibility checks in this pass. See the final verification
section for the current E2E result; manual checks remain deferred.
2026-09-09 21:31:04 +02:00
Oleg Isonen d991b25b65 fix: Accept description-list groups wrapped in divs (#6346)
Pasting `<dl><div><dt>Company</dt><dd>XXX</dd></div></dl>` incorrectly
warns that placing `dt` inside `div` violates HTML. The [HTML
standard](https://html.spec.whatwg.org/multipage/grouping-content.html#the-dl-element)
allows these groups.

The shared validator now tracks the rendered HTML parent explicitly and
gives a `div` directly under `dl` the description-group content model.
Parent context passes through Slots and Fragments and works when
validation starts within the list. Ordinary divs and extra HTML wrappers
still reject `dt`/`dd`; group divs reject unrelated flow children. No
synthetic category can be mistaken for an allowed HTML tag.

- [x] Fix contextual validation without changing generated HTML data.
- [x] Add nine regression cases with before/after failure verification.
- [x] Run 170 tests covering content models, matching, components, and
pre-publish audits.
- [x] Pass project-build typecheck, repository lint, formatting, and
diff checks.
- [x] Review the patch and fix the internal-marker validation bypass.
- [x] Review documentation impact: no update needed; this restores valid
HTML support.
- [x] Review fixture impact: no description-list fixture inputs or
generated outputs are affected.
- [x] Confirm CI passes on the final commit before merging: all
workflows passed on `4ce4cf6696`, including all six browser-test shards.

This remains a nesting check; enforcing term/definition order or counts
is outside this fix. Agent evaluations and local browser E2E were not
run; CI runs the browser suites.

Closes #6345
2026-09-09 11:14:36 +01:00
Oleg Isonen e33ce76489 fix: Keep image picker width stable and accept quick clicks (#6344)
Choosing an image now works with a quick thumbnail click, and selecting
an asset keeps the Source picker at its intended width, including long
names and nested folder paths.

The dialog's content-based width was overriding the floating panel's
width. Focusing a thumbnail widened the panel, triggering resize
handling that briefly disabled pointer events before mouse release. Pass
the floating panel styles through the dialog's `css` override so the
intended width wins. Explicit dimensions and drag/resize handlers remain
as implemented.

Closes #6343

- [x] Reproduce both failures in Chromium using the actual Source
picker.
- [x] Fix floating panel style precedence.
- [x] Add three regressions for root and nested asset selection widths
and an 80 ms thumbnail click. All three fail without the fix and pass
with it.
- [x] Run related Builder tests (50 passed), the design-system suite
(228 passed), both package typechecks, repository lint, and formatting.
- [x] Review documentation impact: the Image Source instructions already
describe the restored behavior; no update needed.
- [x] Verify no fixture inputs or generated outputs are affected.

Validation used headless Chromium component tests. Full canvas E2E and
Storybook screenshot checks were not run.
2026-09-09 12:04:52 +02:00
Oleg Isonen 722b3af113 feat: Copy links to instances from the context menu (#6342)
Right-click an instance in the Navigator and choose **Copy link to
instance** to share its full Builder URL. Opening it restores the page
and the same instance occurrence, including descendants of shared Slots.
The shared menu also exposes the action on the canvas.

Copying and opening a link use the same existing
`getDeepLinkedInstanceSelection` function, moved unchanged from
`use-switch-page.ts` into `shared/instance-utils/link.ts`, alongside URL
creation and the existing instance URL parser. It validates the instance
path and page access. The context menu and pre-publish “Show element”
links both call `getInstanceLink(instanceSelector)`. It lives in
`shared/instance-utils/link.ts` and owns validation and the complete URL
setup: project, page, full selector, origin, share token, editor mode,
and safe mode, using the existing `builderUrl`. There is no separate
link-permission implementation.

The link keeps the current share access and editor mode, clears
unrelated scroll anchors, and shows clipboard success or failure. Body
is supported; Global Root and multiple selections are unavailable.

Instance-only navigation now preserves loaded Builder data: the route
comparison recognizes the `instance` parameter as well as the older
`instanceId`. A functional regression test confirms that changing the
target does not change the data URL comparison, while changing share
access does.

MCP guidance explains how to extract the target ID from the link for
`inspect-instance`, verify the project and shared Slot context, and
avoid guessing when an instance no longer exists. This does not add a
URL-taking MCP endpoint.

1. [x] Add the menu action and reuse complete instance-link construction
with pre-publish links.
2. [x] Share existing instance-link validation between copying and
opening links.
3. [x] Update the Navigator and MCP guidance; regenerate CLI
documentation.
4. [x] Extend the existing functional test to round-trip a shared Slot
URL, including share access and editor mode. Verify it fails without the
instance parameter and passes after restoration.
5. [x] Remove the React/clipboard test harness and duplicate
permission-test matrix. Confirm no fixture inputs or outputs are
affected.

Validation: 28 focused Builder tests, Builder typecheck, repository
lint, and `git diff --check` pass on this revision. The earlier CLI
documentation checks passed and their inputs are unchanged. An earlier
GPT-5.4 mini subagent at low reasoning passed a focused native MCP test
against commit 072a8dc534: given only a link and text-edit request, it
changed the correct heading among three identical-looking headings,
leaving all other fixture records unchanged. It used four MCP calls with
broad instance/text reads. That agent test used an isolated local
fixture; it was not a production or shared-Slot agent test. Screenshots
and the full agent evaluation suite were not run.

Closes #4610
Ref #4364
2026-09-08 15:23:29 +02:00
Oleg Isonen 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.
2026-09-06 15:19:54 +02:00
Oleg Isonen 2e39477303 feat: Expose Force Mount for Collapsible Content (#6338)
## Problem

Closed Collapsible Content is removed from the HTML, and the Builder
does not expose Radix’s Force Mount option.

## Solution

- Expose **Force Mount** as a boolean on Collapsible Content.
- Enable it for newly inserted Collapsible templates.
- Keep closed force-mounted content visually collapsed with template
styles.
- Preserve the existing Radix default for existing instances that do not
set the prop.
- Add regression coverage for the template default and generated Builder
metadata.

## Checklist

- [x] Expose the Force Mount control
- [x] Keep new Collapsible content mounted while closed
- [x] Preserve existing-instance behavior
- [x] Regenerate Collapsible prop metadata from source
- [x] Add regression coverage
- [x] Review documentation impact and update the Collapsible guide
- [x] Verify fixture inputs and outputs are not affected

## Verification

- [x] `pnpm --filter @webstudio-is/sdk-components-react-radix test` — 20
tests passed
- [x] `pnpm --filter @webstudio-is/sdk-components-react-radix typecheck`
- [x] `pnpm --filter @webstudio-is/sdk-components-react-radix build`
- [x] Targeted Oxlint and formatting checks
- [x] `git diff --check`

## Known limitations

Storybook visual verification was not run because it requires separate
approval.

Closes #6337
2026-09-05 17:36:25 +02:00
Oleg Isonen 55920c57c4 fix: Show all Markdown and Assets query diagnostics (#6308)
Ref #6305

## Outcome

Show actionable Markdown, MDX, and Assets query problems in the editor,
query Diagnostics, API, and MCP.

The current implementation substantially improves source diagnostics,
query error aggregation, query scoping, exact paths, MCP feedback, and
loading states. The exhaustive audit below also records what is not
complete yet.

## Completion rule

An error state is complete only when all applicable checks pass:

- [x] The editor uses the production validator and shows every issue for
the open file.
- [x] Query execution uses the same validation and returns every issue
for every file that matches the query, not only the returned page.
- [ ] MCP can reproduce the state through a real tool call and returns
the same code, severity, message, file, and location.
- [x] Diagnostics shows fatal errors and nonfatal warnings in the
correct place.
- [x] Exact file is always present; line and column are present whenever
the parser can determine them.
- [x] Multiple issues in the same file, multiple files, and multiple
validation phases are returned together.
- [ ] A regression test proves failure without the behavior and success
with it.

## Cross-surface contract

### Required validator parity

- [x] Editor, query execution, and MCP file operations invoke the same
exported production file-validation pipeline for byte decoding,
source/frontmatter, Markdown/MDX, Content Block semantics, and
references. Copied schemas, adapter-only checks, or reimplementations do
not count as parity.
- [x] Query builder, query execution, `validate-asset-query`, and
`preview-asset-query` invoke the same exported structural and
catalog-aware semantic query validator with the same project snapshot
and context.
- [x] Every surface preserves the same complete, deterministically
ordered diagnostic array: severity, code, message, phase/scope, asset
ID, file path, line/column/range, query path, and
fatal-versus-warning/result behavior. UI, API, and MCP serializers must
not drop or remap fields.
- [ ] A table-driven contract suite calls the real editor, query, API,
and MCP entry points—including MCP upload, update, download, validate,
and preview—and compares full normalized arrays for multiple errors,
multiple warnings, multiple files, and pre-pagination matches.

- [x] Markdown and MDX source syntax/frontmatter use
validateTextAssetSource in editor, query compilation, and MCP file
tools.
- [x] Query request execution and MCP local parsing use
assetQueryRequest.
- [x] Fatal query setup errors retain all detected semantic errors and
accompanying warnings.
- [x] Nonfatal query warnings retain a stable code and exact query path.
- [x] Source diagnostics retain severity, phase, code, asset ID, path,
and available source position.
- [x] Query Diagnostics excludes diagnostics produced only by unrelated
published queries.
- [x] Query, Preview, and Diagnostics tabs show loading states.
- [x] Authenticated diagnostics can show a sanitized internal message;
public/pre-auth errors remain redacted.
- [x] Share byte-level UTF-8 validation instead of decoding separately
in query and MCP.
- [x] Make the query editor and MCP call one literal shared
query-validation function before execution.
- [x] Share cross-file reference validation across editor, query, and
MCP file operations.
- [x] Share contextual Content Block/template validation across all
applicable surfaces.
- [x] Preserve an invalid stored query in the editor and explain it
instead of replacing it with defaults.
- [x] Define and enforce whether “matches the query” includes every
pre-pagination match; current source validation follows
returned/paginated IDs.
- [x] Continue through source, reference, and runtime phases where safe
so one phase does not hide later issues.
- [x] Map every user-triggerable graph/storage/runtime failure to a
structured public error instead of INTERNAL_ERROR.
- [ ] Label candidate-file warnings separately from returned-file
diagnostics.

## File transport and encoding

- [x] Empty Markdown and MDX are accepted.
- [x] UTF-8 BOM and CRLF are accepted.
- [x] Whole Markdown/MDX file over 1 MiB is detected.
- [x] Missing or unavailable selected content is reported.
- [x] Invalid UTF-8 frontmatter returns the same structured diagnostic
in query and MCP.
- [x] Invalid UTF-8 body returns the same structured diagnostic in query
and MCP.
- [x] MCP download must use fatal UTF-8 decoding rather than replacement
characters.
- [x] Validate bytes before committing an upload, or return the
committed Asset ID and explicit cleanup state when post-commit feedback
fails.
- [x] Reject filename/format mismatches instead of allowing either hint
to bypass Markdown/MDX validation.
- [x] Storage status, missing body, invalid metadata, revision mismatch,
format mismatch, short read, and excess read each return structured file
context.
- [x] Content changing during validation has a clear retryable conflict
diagnostic.

## Frontmatter

- [x] No frontmatter.
- [x] Empty frontmatter.
- [x] Opening delimiter without a closing delimiter.
- [x] Frontmatter over 64 KiB.
- [x] Invalid YAML grammar.
- [x] Multiple YAML parser errors.
- [x] Duplicate keys.
- [x] YAML aliases.
- [x] Unsupported YAML tags.
- [x] Scalar or array root instead of an object.
- [x] Non-JSON values, NaN, Infinity, and unsafe integers.
- [x] Nesting deeper than 8.
- [x] More than 256 fields/array entries.
- [x] One string over 16 KiB.
- [x] Normalized indexed properties over 64 KiB.
- [x] Frontmatter warnings remain nonfatal and visible.
- [x] Collect all post-parse normalization/limit errors; this phase
currently stops at the first.
- [ ] Prove every state above through real MCP upload, update, download,
query, and Diagnostics paths.

## Markdown body

- [x] Treat ordinary CommonMark/GFM as permissive; malformed-looking
Markdown and raw HTML are not automatically syntax errors.
- [x] Preserve and validate the body when frontmatter is invalid.
- [x] Detect the 1 MiB body/file limit.
- [x] Handle body extraction and bounded excerpts.
- [x] Return a shared structured invalid-UTF-8 body error from every
byte-reading surface.
- [ ] Distinguish author errors from internal
excerpt-limit/configuration errors.
- [ ] Prove body-size and decoding states through real MCP and
Diagnostics.

## MDX grammar and safety

- [x] Malformed MDX/JSX grammar.
- [x] Imports.
- [x] Exports.
- [x] Executable flow expressions.
- [x] Executable text expressions.
- [x] JSX expression attributes.
- [x] JSX spread attributes.
- [x] Arbitrary components.
- [x] HTML-looking JSX outside ws.element.
- [x] Unsupported/raw MDX node types.
- [x] Duplicate attributes and normalized alias collisions.
- [x] Event-handler attributes.
- [x] srcdoc, srcset, and dangerouslySetInnerHTML.
- [x] Unsafe URL protocols in link/source/action-like props.
- [x] Forbidden tags: base, link, meta, noscript, script, style,
template, title.
- [x] Conflicting, empty, or non-static ws:name and ws:tag.
- [x] More than 20,000 nodes.
- [x] Nesting deeper than 100.
- [x] More than 4,000 props.
- [x] Multiple recoverable unsafe-mdx warnings are collected.
- [ ] Collect multiple fatal grammar/structural errors when parser
recovery can do so; unrecoverable parsing currently stops at the first
fatal error.
- [x] Preserve sibling expression warnings when unsupported JSX is also
present; {first()}, <Card />, {second()} currently reports only the Card
warning.
- [ ] Prove every state above through real MCP and Diagnostics, not only
validator unit tests.

## Content Block semantics

- [x] Unresolved template warning exists.
- [x] Unknown template prop warning exists.
- [x] Incompatible template prop warning exists.
- [x] Design-only template prop warning exists.
- [x] Stale template prop warning exists.
- [x] Invalid materialized HTML content model error exists.
- [x] Run these checks for an MDX file even when no active editor
Content Block root has materialized it.
- [x] Run applicable semantic checks in query Diagnostics.
- [x] Run applicable semantic checks for MCP upload as well as
contextual update/download.
- [x] Return all Content Block frontmatter diagnostics; one path still
throws only diagnostics[0].
- [ ] Add real MCP and UI coverage for every semantic state.

## Cross-file references

- [x] Non-string $ref marker.
- [x] Invalid reference URL.
- [x] Invalid fragment encoding.
- [x] Invalid fragment.
- [x] Invalid JSON Pointer.
- [x] Missing referenced document.
- [x] Missing referenced JSON path.
- [x] Missing referenced local Asset warning.
- [x] Unsupported target representation.
- [x] Reference cycle.
- [x] Duplicate document ID or URL.
- [x] Missing graph source or target.
- [x] Root not found.
- [x] Duplicate/unused/unresolved reference.
- [x] Invalid resolved value.
- [x] Content identity conflict.
- [x] Target revision mismatch.
- [x] Representation selector required/invalid.
- [ ] Diagnose {$ref: value, extraKey: value} as an invalid marker or
explicitly document it as ordinary data.
- [x] Collect all bad references and targets instead of stopping at the
first graph failure.
- [x] Validate references from the editor and MCP file tools, not only
query compilation.
- [x] Map graph compilation/resolution, Markdown-document, and
JSON-document errors to exact structured Diagnostics rows.
- [x] Prevent an unrelated file's graph error or duplicate canonical URL
from blocking an exact-file query.
- [ ] Prove missing targets, cycles, bad representations, and bad JSON
paths through real MCP.

## Query request and structure

- [x] Invalid JSON, invalid request UTF-8, empty/non-object request, and
request over 512 KiB.
- [x] Missing query and unknown request/query keys.
- [x] Invalid result mode.
- [x] Malformed where/all/any/filter shape.
- [x] More than 32 filters or children.
- [x] Filter nesting deeper than 8.
- [x] Empty field path or segment.
- [x] Field path deeper than 9.
- [x] Bare properties path.
- [x] Unknown or nested standard field.
- [x] Missing or unknown operator.
- [x] Non-JSON operator value.
- [x] Invalid in value or more than 1,000 in values.
- [x] Non-boolean exists/isEmpty value.
- [x] More than 8 sort entries.
- [x] Invalid sort field or direction.
- [x] first/last without a sort.
- [x] Invalid output mode or includeMetadata.
- [x] Duplicate output fields or more than 256 fields.
- [x] No selected output.
- [x] Negative, fractional, or over-1,000 limit.
- [x] Negative, fractional, or over-1,000 offset.
- [x] Invalid content mode.
- [x] Invalid full/markdown-body-ref maxBytes outside 1..1 MiB.
- [x] Invalid range offset/length or range over 256 KiB.
- [x] Empty or over-255-character index revision.
- [x] Multiple simultaneous schema issues retain exact query paths.
- [x] Make content option objects strict; unknown keys are currently
stripped.
- [x] Show an invalid persisted resource body rather than silently
loading the default query.
- [x] Aggregate unknown-key errors with every other structural issue;
MCP currently stops at the first unsupported key.
- [x] Add field-value type validation, for example reject size gt
"large" and id contains {}.
- [x] Warn about unobserved dynamic fields selected only as output.
- [ ] Prove the full structural matrix through the MCP server/API end to
end; the current broad matrix tests the local adapter.

## Query semantic states

- [x] INCOMPATIBLE_OPERATOR is fatal for standard fields.
- [x] UNOBSERVED_FIELD is a nonfatal warning.
- [x] INCOMPATIBLE_OBSERVED_TYPES is a nonfatal warning.
- [x] STRUCTURED_SORT_ORDER is a nonfatal warning.
- [x] Every incompatible standard-field operator is collected.
- [x] Fatal semantic errors retain nonfatal warnings.
- [x] Successful warnings appear with exact query paths.
- [ ] Reproduce every catalog-dependent semantic state through real MCP.
- [x] Test repeated warnings at distinct query paths without accidental
deduplication.

## Query execution and hydration

- [x] Stale index revision.
- [x] result one matching multiple files, including matchedCount.
- [x] Candidate document limit of 1,000.
- [x] Result payload limit of 16 MiB.
- [x] Hydrated file count limit of 20.
- [x] Per-file hydration limit of 1 MiB.
- [x] Total hydration limit of 2 MiB.
- [x] Range limit of 256 KiB.
- [x] Binary content requested as text.
- [x] Invalid UTF-8 during hydration.
- [x] Missing, stale, or conflicting content identity.
- [x] Canonical metadata/byte-count mismatch.
- [x] Storage returning more bytes than requested.
- [x] Missing content reader.
- [x] Selected content not returned.
- [x] Runtime Asset URL unavailable.
- [x] Document graph document-count/per-file/total limits.
- [x] Classify candidate-document and result-byte limits as
CONTENT_LIMIT_EXCEEDED instead of INVALID_REQUEST.
- [x] Give unavailable URL, missing reader/content, storage, selection,
and assembly failures stable codes and exact asset/file details instead
of INTERNAL_ERROR.
- [x] Never silently turn an exact-file unavailable-content failure into
an empty successful result.
- [x] Add direct regression tests for duplicate identity, excess bytes,
and canonical byte mismatch.
- [ ] Reproduce every runtime/hydration state through real MCP.

## Scoping and aggregation

- [x] Errors and warnings from multiple loaded result files are returned
together.
- [x] Nonfatal errors do not suppress successful query results.
- [x] Unrelated published-query diagnostics do not replace current-query
diagnostics.
- [x] File path and available line/column are shown.
- [x] Exact nested query paths are shown.
- [x] Validate and report every pre-pagination matching file if that is
the product definition.
- [ ] Explain candidate metadata warnings when invalid metadata prevents
deciding whether a file matches.
- [ ] Stop including METADATA_PREPARATION_FAILED globally unless the UI
labels its candidate scope.
- [x] Aggregate reference errors instead of stopping at the first.
- [x] Aggregate across validation phases where safe.
- [x] Add a matrix for same-file multiple errors, multiple files,
outside-page errors, filter-only candidate files, and unrelated queries.

## Transport, authorization, cancellation, and internal failures

- [x] UNAUTHORIZED and FORBIDDEN.
- [x] REQUEST_CANCELLED in query HTTP paths.
- [x] Sanitized authenticated internal message.
- [x] Generic unauthenticated/public internal message.
- [x] Preserve structured causes for document-load HTTP
status/body/metadata failures.
- [x] Preserve structured causes for object-storage read failures.
- [x] Preserve structured causes for graph selection/assembly failures.
- [ ] Test MCP/API cancellation directly.
- [ ] Reproduce authorization, cancellation, storage, and
sanitized-internal states through real MCP where safe.

## MCP reproduction

- [x] Real MCP reproduction: multiple Markdown YAML errors.
- [x] Real MCP reproduction: multiple simultaneous structural query
errors.
- [x] MCP update returns Markdown and MDX source diagnostics.
- [x] MCP upload returns Markdown and MDX source diagnostics.
- [x] MCP download returns Markdown and MDX source diagnostics.
- [x] Temporary remote fixtures are removed after reproduction.
- [ ] Real MCP reproduction for every frontmatter code.
- [ ] Real MCP reproduction for every MDX fatal/warning class.
- [ ] Real MCP reproduction for every Content Block semantic diagnostic.
- [ ] Real MCP reproduction for every reference/graph error.
- [ ] Real MCP reproduction for every semantic query warning/error.
- [ ] Real MCP reproduction for every hydration/runtime/limit error.
- [ ] Real MCP reproduction for auth, cancellation, storage, and
internal errors where safe.
- [ ] A durable table-driven test proves code/severity/message/path
parity between editor, query, Diagnostics, and MCP.
- [x] Preserve stable top-level error identity and structured details
through MCP instead of flattening known errors to MCP_TOOL_FAILED.
- [x] Prove failed post-commit MCP feedback reports whether a mutation
committed and returns enough identity for cleanup.

## Real MCP recheck and follow-up fixes (2026-09-03)

Ran the branch CLI against the PR preview backend. The first run exposed
that the saved CLI origin pointed to a different backend, so those
results were discarded. Every MCP result below came from the PR preview.
All temporary remote and local fixtures were removed afterward. The
failures found in that run were then fixed and covered by regression
tests.

- [x] Exact broken Markdown query returns FRONTMATTER_INVALID with
severity, filename, message, line, and column.
- [x] Exact MDX query returns unsafe-mdx with severity, filename,
message, line, and column.
- [x] A no-extension query returns warnings from both matching Markdown
and MDX files.
- [x] A Markdown-only query excludes the unrelated MDX warning.
- [x] Two fatal MDX files inside the returned page are both reported;
matching warnings remain attached.
- [x] Structural query validation returns three independent errors with
exact paths in one call.
- [x] Semantic query validation returns three fatal operator errors and
two warnings with exact paths in one call.
- [x] MCP upload, update, download, and exact-file query return multiple
source diagnostics for the same MDX source.
- [x] Query preview reports a missing Asset reference as ASSET_NOT_FOUND
with the source file and reference.
- [x] All temporary MCP fixtures were deleted and the saved CLI
connection was restored.
- [x] Fixed after recheck: validate every matching file before
pagination, including broken files outside the returned page.
- [x] Fixed after recheck: unrelated graph failures no longer block an
exact-file query.
- [x] Fixed after recheck: update, download, editor, and query paths use
shared reference validation.
- [x] Fixed after recheck: graph failures map to structured diagnostics
with the exact file.
- [x] Fixed after recheck: shared fatal UTF-8 validation runs before
upload writes and preserves structured error identity.
- [x] Fixed after recheck: MCP rejects filename/format mismatches.
- [x] Fixed after recheck: MDX validation keeps sibling and nested
warnings around unsupported components.
- [x] Fixed after recheck: MCP preserves stable top-level error codes
and structured details.
- [x] Fixed after recheck: query and MCP serializers preserve full
normalized diagnostic fields.
- [x] Fixed after recheck: unknown keys are aggregated with all other
structural query errors.
- [x] Fixed after recheck: shared query validation covers literal types,
output-only fields, and strict content options.
- [x] Fixed after recheck: unavailable selected content returns an
exact-file diagnostic instead of a silent empty result.

## Tests and delivery

- [x] Full Builder suite: 2,707 tests.
- [x] Content Engine suite: 445 tests.
- [x] Asset uploader suite: 211 tests.
- [x] Project runtime/MCP targeted suite: 128 tests.
- [x] CLI/MCP suite: 1,051 tests.
- [x] Relevant package typechecks, lint, generated docs, and diff
checks.
- [x] Documentation impact reviewed and generated Content Engine/MCP
reference updated.
- [x] Add the missing cross-surface error matrix described above.
- [ ] Demonstrate red/green behavior for every new matrix case.
- [ ] Run the complete agent evaluation suite after explicit approval.
- [ ] Re-run all checks after the remaining checklist is implemented.

## Compatibility and risk

Normal query result and expression-binding shapes remain unchanged.
Diagnostics add optional fields. Existing public/pre-auth error
redaction remains intact. The remaining unchecked items are broader
exhaustive real-MCP coverage, parser-recovery limits, candidate-warning
labeling, cancellation coverage, and agent evaluations.
2026-09-04 16:00:28 +02:00
Oleg Isonen cb1a17dc9b fix: Allow approved fork workflow checkouts (#6335)
Approved fork workflows currently fail before tests start. GitHub
rejects the fork-head checkout in the trusted `pull_request_target`
context even after the `safe-to-deploy` label is applied.

## Implementation

- Opt into the fork checkout for every reusable Main workflow job only
when the event is a `safe-to-deploy` label approval.
- Apply the same guard to the nested fixture workflow.
- Keep normal pushes and regular pull request checkouts unchanged.

## Todo

- [x] Guard all Main workflow checkouts.
- [x] Guard the nested fixture workflow checkout.
- [x] Verify the opt-in remains false for normal pushes and pull
requests.
- [x] Review documentation impact: no product documentation is affected.
- [x] Review fixture impact: no fixture inputs or generated outputs are
affected.
- [ ] Merge this change into `main`, then reapply `safe-to-deploy` to
the affected fork PR.

## Verification

- [x] Prettier check for both changed workflows.
- [x] `git diff --check`.
- [x] Confirmed all seven Main checkouts and the fixture checkout use
the guarded opt-in.

## Known limitation

GitHub loads `pull_request_target` workflow definitions from the default
branch. The repaired approved-fork run can only be verified after this
change reaches `main`.

Ref #6332

Ref #6329
2026-09-04 15:36:47 +02:00
d292502037 fix: Paste in Firefox navigator (#6329)
## Summary

- Read every clipboard mime type before the first await in both paste
handlers.
- Call the paste handler registered by the active mode from `emitPaste`
instead of dispatching a synthetic clipboard event.
- Add regression coverage for both paths: a clipboard event that
disconnects after dispatch, and a Firefox-shaped `ClipboardEvent`.

## Root cause

Two independent Firefox behaviors, one per entry point.

Keyboard paste: the plugin loop re-read `event.clipboardData` on every
iteration. Firefox disconnects a paste event's `DataTransfer` as soon as
the handler yields, so once `pageText` — first in the list, and async —
awaited, every later plugin read an empty string and `instanceText`
never received the payload. Chrome keeps the data readable across
microtasks, which is why this only surfaced in Firefox. Introduced in
#5798, which put `pageText` at the head of the plugin list.

Context menu paste: `emitPaste` dispatched `new ClipboardEvent("paste",
{ clipboardData })`. Gecko's `ClipboardEventInit` has no `clipboardData`
member, so the event arrived with a non-null but empty `clipboardData`.
`validateClipboardEvent` only rejects a null one, so the paste silently
did nothing — no toast, no error.

Measured in Firefox 134 against Chrome and Edge 152: `getData` after one
microtask returns the payload in Chromium and an empty string in
Firefox; a synthetic event's `getData` likewise returns the payload in
Chromium and an empty string in Firefox.

No behavior change in Chromium. Every plugin returns `pasteIgnored`
before its first await, so reading all mime types up front yields the
same input the loop received before.

## Verification

- `pnpm --filter @webstudio-is/builder exec vitest run
app/shared/copy-paste app/builder/shared/asset-manager
app/shared/commands-emitter.test.ts` — 335 tests passed.
- `pnpm --filter @webstudio-is/builder typecheck` — passed.
- `pnpm exec oxlint --deny-warnings apps/builder/app/shared/copy-paste/`
— passed.
- `pnpm exec oxfmt --check
apps/builder/app/shared/copy-paste/copy-paste.ts
apps/builder/app/shared/copy-paste/copy-paste.test.ts` — passed.
- Reverted each fix in turn against the new tests: 13 failures for the
keyboard path, 2 for the context menu, all passing again once restored.
- e2e runs Chromium only, and the previous test `DataTransfer` never
disconnected, so neither path was covered before.

Closes #6288

---------

Co-authored-by: Anmol <anmol@fathershops.com>
Co-authored-by: Oleg Isonen <oleg008@gmail.com>
2026-09-04 15:36:20 +02:00
Oleg Isonen 0ffdf36665 fix: Explain dynamic Content Block asset sources (#6333)
## Summary

Content Block source operations now explain how to resolve a dynamic
Assets resource for one rendered occurrence. A caller can keep the
persisted expression, supply the concrete resource result through
`variables`, and receive safe structured diagnostics when the expression
does not resolve to a non-empty MDX Asset id.

The operation still does not load page routes or resource results
itself. That work remains in the rendering or caller context.

## Checklist

- [x] Reproduce the unresolved result-one Assets resource expression
- [x] Preserve the dynamic source expression while validating a concrete
Asset id
- [x] Return the evaluated value shape without exposing supplied values
- [x] Reuse the shared Content Block source resolver
- [x] Describe `renderScope` and `variables` in the operation schema
- [x] Add a result-one Assets resource example
- [x] Verify connection keeps the dynamic expression after resolving the
supplied result
- [x] Add regression coverage for structured unresolved-source
diagnostics
- [x] Regenerate runtime contracts and CLI documentation
- [x] Review fixture impact: fixture inputs and outputs are not affected
- [x] Review documentation impact: MCP and CLI guidance are updated

## Verification

- New diagnostic regression failed against the old generic error and
passed with this fix
- Project Build: 88 files, 2,284 tests passed
- Project Build typecheck
- CLI typecheck
- Generated documentation and contract checks
- Formatting and lint with warnings denied
- Full CLI suite from the implementation review: 66 files, 1,038 tests
passed

Agent evaluations were not run because they require separate explicit
approval.

Closes #6330
2026-09-04 15:15:18 +02:00
Oleg Isonen e33a269802 fix: show unsupported browser warning in a dialog (#6332)
The unsupported-browser warning now uses the Builder’s regular modal
dialog. Users can acknowledge it and continue, while the small-window
state remains a blocking alert.

Closes #6331

## Implementation

- Render the existing browser guidance and links inside the
design-system `Dialog`.
- Set the dialog to modal and keep close, Escape, outside-click, and
**Continue** dismissal behavior.
- Keep unsupported-browser detection, preview-mode suppression, and
Builder-ready timing unchanged.
- Keep the small-window alert blocking and remove its now-unused
dismissible-alert path.
- Add a focused Storybook state and browser regression test.
- Increase Main and Visual Regression workflow job timeouts from five to
seven minutes after otherwise healthy jobs were cancelled during slow
dependency setup.
- Run failed E2E diagnostics and service teardown through the backend
shell script so cleanup does not depend on pnpm remaining on `PATH`
after cancellation.
- Route `safe-to-deploy` label events through one reusable gate that
starts both Main and Vercel workflows.
- Remove revision-specific deployment and visual approval labels in one
synchronization-only cleanup job.
- Reduce expected skipped jobs from 6 to 1 on a normal branch update and
from 17 to 3 on a visual approval label event.

No migrations or compatibility changes are required.

## Todo

- [x] Replace the unsupported-browser alert with a modal dialog.
- [x] Preserve the small-window blocking alert.
- [x] Add regression coverage.
- [x] Give CI enough time for slower dependency setup.
- [x] Keep E2E cleanup available after runner cancellation.
- [x] Simplify label-triggered Main and Vercel workflows.
- [x] Consolidate revision-specific approval cleanup.
- [x] Review documentation impact: no product documentation changes are
needed for this presentation-only warning update.
- [x] Review fixture impact: no fixture inputs or generated fixture
outputs are affected.

## Verification

- [x] Focused browser regression test
- [x] Builder typecheck
- [x] Oxlint on changed files
- [x] Oxfmt and `git diff --check`
- [x] Workflow YAML parsing and backend shell syntax checks
- [x] Workflow trigger, nesting, permissions, and reusable-job contract
assertions
- [x] Prettier checks for changed workflows
- [ ] Storybook screenshot review was not run locally because explicit
visual-test approval was not available.

## Known limitations

- The visual state was not screenshot-tested locally.
- The reduced `pull_request_target` fan-out takes effect after these
workflow files reach the default branch, because GitHub loads those
workflow definitions from that branch.
2026-09-04 15:06:34 +02:00
Oleg Isonen 2e4efb52f9 docs: update Link target values (#6326)
## Summary

Update the Link documentation to match the current code.

The Builder uses `_self`, `_blank`, `_parent`, and `_top` as the Link
`target` values. The old docs described these as `Self`, `Blank`,
`Parent`, and `Top`, which could make setting a new-tab link confusing.

## Changes

- Document the four current target values.
- Explain that `_blank` opens a link in a new tab or window.
- Remove the outdated Target screenshot.
- Checked the rest of `docs/` for the same outdated target names; no
other related occurrences were found.

## Verification

- `git diff --check` passed.
- Confirmed the values in
`packages/sdk-components-react/src/__generated__/link.props.ts` and
`packages/sdk-components-react/src/create-link.tsx`.
- Checked that the docs file and navigation file exist.
- `pnpm exec oxfmt --check docs/university/core-components/link.md` did
not run because the Markdown file is excluded by the formatter
configuration.

## Checklist

- [x] Review the current Builder implementation.
- [x] Review the authoritative docs page.
- [x] Check for similar outdated documentation.
- [x] Review documentation impact: this PR updates the affected page; no
other docs need changes.
- [ ] Merge the pull request.
2026-09-03 20:23:42 +02:00
Oleg Isonen 75840a119c fix: Start previews and diagnose runtime failures (#6321)
## Summary

Preview startup now binds loopback directly instead of scanning network
interfaces first. MCP no longer exposes or accepts a fixed preview host
or port: the CLI asks the operating system for an available loopback
port and returns the resulting URL.

The CLI also returns safe, structured diagnostics when the runtime
blocks preview networking or Chromium IPC, when generated IDs collide,
and when a listening preview fails its HTTP readiness probe. Preview
startup retries only a genuine automatic-port race; HTTP and connection
failures return immediately instead of repeating the full startup
timeout five times.

## Issue checklist

- [x] #6318 — reproduced the `uv_interface_addresses` failure, replaced
interface discovery with a direct TCP bind, and added a regression test
for the exact failure.
- [x] #6319 — reproduced Chromium aborting before DevTools startup when
the managed runtime blocks browser IPC/socket operations. The host
restriction cannot be bypassed by the CLI, but future reports now
include a safe `browser_ipc_permission_denied` issue without raw stderr,
paths, or tokens.
- [x] #6320 — ran four reported insert variants sequentially across two
pages; all generated unique IDs. Future collision errors now include the
affected namespace and insertion stage without exposing the generated
ID.
- [x] #6324 — reproduced the generic readiness timeout with a real
generated preview whose root probe returned HTTP 500 after the server
printed its Local URL. The MCP contract and documentation also allowed
agents to force a busy port; network binding is now runner-owned.
- [x] #6325 — confirmed the responsive screenshot uses the same
readiness path. Future reports distinguish HTTP, refused-connection,
timeout, and permission failures, non-port failures are no longer
retried, and screenshots cannot request a fixed preview port.
- [x] Documentation impact reviewed. Updated MCP examples and vision
guidance to omit runner-owned host and port fields.

## Technical notes

- Removes the CLI's direct `get-port` dependency. That package calls
`os.networkInterfaces()` even when a host is supplied.
- Binds `{ host: "127.0.0.1", port: 0 }`, reads the assigned TCP port,
then closes the temporary server.
- Removes `host` and `port` from MCP preview and screenshot input
schemas and rejects those fields at runtime.
- Returns the selected preview URL from `preview.start`; managed
screenshots reuse that active target.
- Returns `PREVIEW_NETWORK_RESTRICTED` with a stable
`network_permission_denied` issue when the OS rejects loopback binding.
- Reads at most the last 4 KB of Chromium startup stderr, maps only the
reproduced IPC/socket denial to a safe diagnostic, and discards the
buffer after startup.
- Preserves browser startup issues through CLI browser fallback
aggregation and the MCP screenshot error boundary.
- Reports generated record collisions with `generated_id_conflict`, the
namespace constraint, and the fragment insertion stage.
- Preserves structured readiness diagnostics when preview output is
appended.
- Reports HTTP 5xx and local connection failures without exposing raw
fetch errors.
- Retries with a newly allocated port only for `EADDRINUSE` /
`PREVIEW_PORT_IN_USE`.

## Verification

- [x] New regression tests were shown to fail before implementation and
pass afterward.
- [x] Reproduced the old generic timeout through the real preview
controller; after the fix, the same path returned `PREVIEW_HTTP_ERROR`
with `http_status:500`.
- [x] Verified MCP advertised and accepted fixed host/port fields before
the change, then rejected both fields after the change.
- [x] Preview readiness/controller tests: 2 files, 29 tests passed.
- [x] Vision tests: 13 files, 81 tests passed.
- [x] Project-build tests: 88 files, 2,282 tests passed.
- [x] CLI tests: 66 files, 1,038 tests passed.
- [x] Vision, project-build, and CLI typechecks passed.
- [x] Lint passed with warnings denied.
- [x] Package-boundary and MCP documentation checks passed.
- [x] Fixture impact reviewed; no fixture inputs or generated fixture
outputs are affected. Generated documentation was updated; the generated
runtime operation contract did not change.

Agent evaluations were not run because they require separate approval.

Closes #6318

Ref #6319

Ref #6320

Ref #6324

Ref #6325
2026-09-03 16:34:20 +02:00
Oleg Isonen b832a8e808 refactor: Centralize Builder ID generation (#6323)
## Outcome

Builder-owned code now routes standard ID generation through
`@webstudio-is/sdk/createId` while preserving existing NanoID and UUID
formats.

Closes #6322

## Technical choices

- Use `createId("nano")` for Builder records, assets, transaction IDs,
and other existing NanoID-backed values.
- Use `createId()` for UUID-backed database records and browser-side
client IDs.
- Keep `BuilderRuntimeContext.createId` as an injectable dependency.
Runtime operations, copy/paste, and deterministic tests rely on that
injection.
- Keep Node's `randomUUID` in CLI paths that explicitly support running
without the Web Crypto global.
- Keep specialized generators for custom-alphabet project domains and
actual hash generation.
- Remove direct `nanoid` dependencies from packages where no specialized
use remains.
- Do not migrate existing data or change database schemas.

## Todo

- [x] Migrate Builder app ID creation.
- [x] Migrate project runtime and template defaults.
- [x] Migrate asset, project, domain, and authorization-token IDs.
- [x] Preserve runtime ID injection.
- [x] Review remaining direct generators as explicit exceptions.
- [x] Add NanoID-format regression coverage for the default runtime
context.
- [x] Review documentation impact: no user-facing documentation changes
are needed.
- [x] Review fixture impact: no fixture input or generated output format
changes are needed.
- [x] Run targeted tests, typechecks, lint, and package-boundary checks.

## Verification

- Targeted Builder tests: 153 passed after preserving runtime-context
injection.
- Project-build runtime tests: 270 passed.
- Asset uploader tests: 61 passed.
- Authorization-token tests: 17 passed.
- Project tests: 52 passed.
- Domain tests: 18 passed.
- CLI tests: 32 passed.
- Typechecks passed for Builder, project-build, asset-uploader,
authorization-token, project, and domain.
- `pnpm lint` passed.
- `pnpm check:package-boundaries` passed.
- The runtime-context format test was demonstrated to fail with a UUID
default and pass with the NanoID default.
- Agent evaluations were not run because they require separate explicit
approval.
2026-09-03 15:56:23 +02:00