Files
webstudio/packages/cli/scripts
Oleg Isonen e80ca3b54c fix: Improve MCP authoring and screenshot reliability (#5957)
Closes #5954
Closes #5951
Closes #5950
Closes #5953

## Summary

- route external screenshot `url` and `baseUrl` targets through the
self-closing browser path
- keep reusable Chromium sessions limited to Webstudio-managed session
previews
- validate typed style values at the public runtime boundary while
retaining CSS strings for design tokens
- build MCP and shell validation messages from compact structured issues
- advertise typed style objects with a concrete keyword example
- persist `update-asset` metadata patches to the related file record and
verify the saved value
- store multi-sentence page titles and metadata as fixed text without
misclassifying sentence punctuation as member access
- capture privacy-safe aggregate token usage and MCP workflow metrics in
every high-impact evaluation result
- compare each evaluation with a compatible checked-in baseline and fail
on correctness, token, tool-use, error/retry, broad-read, or MCP-latency
regressions

## Root causes

### External screenshot lifecycle

The fast iterative preview change made `session` the default preview
source. External captures were then treated as managed session-preview
captures and opened a reusable Chromium session. One-shot MCP processes
do not call `preview.stop`, so the browser kept the process alive and
could be orphaned when the caller killed the hung CLI.

### Validation errors and schemas

`update-styles` declared `value` as unconstrained input, and
design-token objects were checked only for a string `type`. Invalid
values therefore passed the advertised/input schema and failed later in
the full `StyleValue` parser, exposing the raw Zod union message. Error
serialization already carried flattened issues but did not use them for
`error.message`.

### Asset metadata persistence

`update-asset` correctly produced an `assets/[id]/meta` patch, but asset
persistence compared only Asset-table fields. Metadata lives on the
related File row, so the patch was treated as unchanged and the
successful mutation never wrote the new value.

### Page text expression disambiguation

Page text normalization treated a period followed by whitespace and a
word as JavaScript member access. Normal prose such as `Marques. Toutes
nos marques` was therefore validated as an expression and rejected
instead of being stored as a string literal.

### Evaluation observability

The evaluation result only stored duration and two call counts, so token
cost, failures, retries, mutation flow, verification coverage, and MCP
latency could regress without a durable comparison. Results now use a
bounded schema-v2 aggregate and never persist prompts, transcripts,
stdout/stderr, credentials, arguments, or response payloads.

## Impact

- external screenshots complete without retaining browser processes
- repeated managed-preview screenshots retain browser reuse
- invalid style inputs fail at the public boundary with paths such as
`updates.0.value`
- `error.message` is concise and actionable instead of a repeated raw
union dump
- generated schemas no longer advertise primitive or array values for
`update-styles`
- supported design-token CSS strings remain accepted
- `update-asset` font metadata remains changed after commit, refresh,
and reload
- `create-page` and `update-page` accept ordinary multi-sentence titles
and SEO descriptions while preserving computed expressions
- every high-impact run reports input, cached-input, cache-write,
output, reasoning-output, total-token, and cache-rate aggregates
- MCP results report calls, success/failure, retries, focused/broad
reads, dry runs, planned/committed mutations, verifications,
total/p50/p95 latency, and first-mutation/verification timing
- compatible results are compared with accepted baselines using a 15%
relative regression threshold; correctness failures and zero-baseline
errors, retries, or broad reads remain zero-tolerance
- stochastic wall-clock and milestone timings remain visible as deltas
without making a single noisy run fail

## Todo

- [x] Reproduce external URL browser-session routing
- [x] Use self-closing capture for external screenshots
- [x] Preserve browser reuse for managed session previews
- [x] Reproduce primitive style-value schema acceptance
- [x] Validate typed style values at the public boundary
- [x] Preserve CSS-string design-token inputs
- [x] Format stdio and shell validation messages from structured issues
- [x] Regenerate and verify runtime operation contracts
- [x] Reproduce dropped asset metadata at the persistence boundary
- [x] Persist and verify related File metadata updates
- [x] Reproduce multi-sentence page text failures on create and update
- [x] Distinguish sentence punctuation from computed member access
- [x] Add privacy-safe token and MCP workflow metrics
- [x] Add per-fixture baseline comparisons and regression gates
- [x] Refresh accepted schema-v2 baselines for all fixtures
- [x] Review the complete diff
- [x] Run repository checks and the complete agent evaluation suite

## Verification

- `pnpm checks` — passed, including workspace tests, typechecks, lint,
package-boundary checks, generated API/docs checks, fixture sync, and
fixture builds
- builder tests — 189 files, 2,356 tests passed
- CLI tests — 53 files, 852 tests passed
- project-build tests — 81 files, 2,023 tests passed
- asset-uploader tests — 11 files, 98 tests passed
- targeted evaluation metrics/regression/trace/runner tests — 5 files,
17 tests passed
- all four issue regression tests and the new evaluation harness tests
were demonstrated failing without their implementations and passing with
them
- `pnpm evaluations -- --update-baselines` — 3/3 fixtures passed and
schema-v2 baselines were updated
- a subsequent real `font-assets-v1` result compared successfully after
the regression policy was finalized
2026-07-29 19:33:24 +02:00
..