Files
webstudio/.github/workflows/release.yml
Oleg Isonen 12ad74584a feat: Improve MCP authoring, auditing, and verification (#5886)
## Summary

This improves how coding agents discover, author, verify, and audit
Webstudio projects through the CLI and MCP. The work focuses on making
agents more reliable with less context, while keeping edits semantic and
Builder-editable.

## What changes for users

- Agents can verify dynamic bindings after authoring and receive
actionable diagnostics when expressions, resources, scopes, or component
props do not line up.
- Agents can create complete resource-backed runtime UI through a
semantic operation instead of coordinating fragile low-level edits.
- MCP discovery and command output are compact by default, with
pagination and focused verbose detail when an agent needs more context.
- Asset, style, token, font, page, and component inspection provides
stronger usage context so agents can preserve the existing design
system.
- Rendered audits provide better evidence for responsive behavior,
geometry, contrast, generated-build size, and large-project runs.
- Cross-project MCP workflows can run from a manifest with explicit
mutation approval, isolated failures, resumable progress, and persistent
checkpoints.
- Project-specific agent onboarding is clearer and does not add
telemetry.
- Packaged CLI releases gain stronger smoke verification for install,
MCP startup, authoring, preview, screenshots, and audits.

## Reliability and architecture

- Runtime operation output schemas are generated from the runtime
registry and remain client-compatible.
- Project-build exports are consolidated around architectural boundaries
instead of feature-by-feature convenience entry points.
- HTTP, preview, screenshot, and project-session paths are hardened for
longer autonomous workflows.
- Craft compatibility work is read-only and provenance-aware; it does
not silently opt ordinary projects into Craft behavior.
- A minimal-context evaluation harness exercises key workflows with a
weaker model and stores bounded, privacy-safe evidence.

## Validation

- Rebased on the latest `origin/main`.
- `pnpm lint`
- Repository-wide `oxfmt --check`
- CLI typecheck and full test suite: 773 tests passed.
- Project-build typecheck and full test suite: 1,866 tests passed.
- Protocol typecheck and full test suite: 83 tests passed.
- HTTP client tests: 23 passed.
- CSS data tests: 3,620 passed, 1 skipped.
- Focused Builder tests: 67 passed.
- Package-boundary, generated API, and generated documentation checks
passed.
- Minimal-context agent evaluations passed for authenticated
resource-backed UI and design-system-aware responsive authoring.
2026-07-16 17:53:16 +02:00

141 lines
5.1 KiB
YAML

name: Release
on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+"
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo,
})
const commits = await github.rest.repos.compareCommitsWithBasehead({
owner: context.repo.owner,
repo: context.repo.repo,
basehead: `refs/tags/${latestRelease.data.tag_name}...${context.ref}`,
})
const groups = {
feat: [`## Features\n`],
fix: [`## Fixes\n`],
docs: [`## Documentation\n`],
experimental: [`## Experimental\n`],
other: [`## Other changes\n`],
}
for (const commit of commits.data.commits) {
const match = commit.commit.message.match(/^(?<type>\w+)\s*:\s*(?<message>.+)\n*/)
const type = match?.groups?.type
const message = match?.groups?.message
if (type && message) {
const availableType = type in groups ? type : 'other'
const capitalized = message[0].toLocaleUpperCase() + message.slice(1)
groups[availableType].push(`- ${capitalized}`)
}
}
const tag_name = context.ref.slice('refs/tags/'.length)
const fullChangelog = `**Full Changelog**: https://github.com/${context.repo.owner}/${context.repo.repo}/compare/${latestRelease.data.tag_name}...${tag_name}`
const changelog = Object.values(groups)
.filter(lines => lines.length > 1)
.map(lines => lines.join('\n'))
.concat(fullChangelog)
.join('\n\n')
console.info(changelog)
await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name,
name: tag_name,
body: changelog,
})
publish:
runs-on: ubuntu-latest
permissions:
contents: write
environment:
name: development
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }} # tag name
- uses: ./.github/actions/submodules-checkout
with:
submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- id: version
run: echo "value=$(echo ${{ github.ref }} | sed 's/refs\/tags\///')" >> $GITHUB_OUTPUT
- id: registry-baseline
name: Resolve pre-publish CLI baseline
run: echo "value=$(npm view webstudio@latest version)" >> "$GITHUB_OUTPUT"
- name: bump version to ${{ steps.version.outputs.value }}
run: |
pnpx replace-in-files-cli \
--string="0.0.0-webstudio-version" \
--replacement="${{ steps.version.outputs.value }}" \
"**/package.json"
- name: pnpm instal
run: pnpm install --ignore-scripts
- run: pnpm --filter="webstudio..." build
- run: pnpm --filter="webstudio..." dts
- name: Creating .npmrc
run: |
cat << EOF > "$HOME/.npmrc"
//registry.npmjs.org/:_authToken=$NPM_TOKEN
EOF
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: pnpm -r publish --access public --no-git-checks
- name: Wait for exact CLI registry artifact
env:
VERSION: ${{ steps.version.outputs.value }}
run: |
for attempt in $(seq 1 30); do
integrity=$(npm view "webstudio@$VERSION" dist.integrity --json 2>/dev/null || true)
if [ -n "$integrity" ] && [ "$integrity" != "null" ]; then
exit 0
fi
sleep 10
done
echo "Exact registry artifact webstudio@$VERSION was not observable with integrity metadata." >&2
exit 1
- name: Gate exact published CLI against baseline
id: registry-smoke
env:
DATABASE_URL: postgres://
AUTH_SECRET: test
WEBSTUDIO_RELEASE_SMOKE_TOKEN_BUDGET: 6000
WEBSTUDIO_RELEASE_SMOKE_CANDIDATE: webstudio@${{ steps.version.outputs.value }}
WEBSTUDIO_RELEASE_SMOKE_BASELINE: webstudio@${{ steps.registry-baseline.outputs.value }}
WEBSTUDIO_RELEASE_SMOKE_EXPECT_REGISTRY_VERSION: ${{ steps.version.outputs.value }}
WEBSTUDIO_RELEASE_SMOKE_REPORT: ${{ github.workspace }}/artifacts/cli-release-smoke.json
run: pnpm --filter webstudio test:release-smoke:registry
- name: Upload published CLI comparison report
if: ${{ always() && (steps.registry-smoke.outcome == 'success' || steps.registry-smoke.outcome == 'failure') }}
uses: actions/upload-artifact@v4
with:
name: cli-release-smoke-${{ steps.version.outputs.value }}
path: artifacts/cli-release-smoke.json
if-no-files-found: error