You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As an engineer — and as a coding agent — picking up Mecatl Studio after the in-repo bootstrap
stack has landed, I want the app to look like the approved design, carry its own agent
contract, be covered by browser and unit proofs, sit in a folder structure that scales
past the skeleton, and expose one coherent configuration surface, so that Studio is
maintainable by the whole team instead of only by its author.
Companion to #1736. That story brings Studio into the repository as apps/ and ships its
image; this one makes the result maintainable. It does not block #1736's stack (#1746–#1752) —
it starts as that stack lands, axis by axis.
Scope
1. Align Studio UI and design (UX, UI, components, colors, CSS)
Scope includes navigation and interaction flows, page layouts, typography, shared
controls, color palettes, responsive behavior, and accessibility across the shell,
chat, schedules, knowledge, settings, and global search.
Keep the existing local controls and tokens for this batch. Each surface lands as
its own reviewable change, with desktop/mobile screenshots and interaction evidence.
2. Agent contract for apps/
apps/AGENTS.md (with the repo's CLAUDE.md symlink convention) as the lean correction
file for the TypeScript workspace — in the spirit of the root AGENTS.md, not a second copy
of the docs: the pnpm-workspace commands, the contracts → committed openapi.json →
generated-client drift rule, the BFF security invariants (sealed HttpOnly cookies,
double-submit CSRF, same-origin check, rate limiting with trusted proxy hops, the STUDIO_IMAGE=1 / STUDIO_ALLOW_UNAUTHENTICATED=1 refusals), and the standing
"published @stacklok-oss/mecatl-sdk only, never sdk/typescript" rule.
A pointer to it from the root AGENTS.md (the root file stays lean).
Skills under .claude/skills/ for the recurring Studio loops — e.g. add a surface
(contracts schema → BFF route → regenerate client → web feature → proofs) and touch Studio
configuration. Held to the same bar as the existing skills (to-acceptance-plan, plan-orchestrate, test-writer).
Rules / markdown instructions for the conventions that are currently tribal: TanStack
file-route layout, feature-folder boundaries, "no fetch outside lib/api-client", the
Hono route + Zod schema + problem-details shape, and the Biome formatting contract.
Acceptance: an agent given "add surface X" follows the contract without reading the tree.
3. Cover the app with e2e and unit tests
Browser e2e with Playwright, reusing the conventions already in the repo
(sdk/typescript/e2e/browser/playwright.config.ts) and the playwright: resolver already
wired in .actrace.yml, so the proofs are citable from acceptance plans.
Journeys: unauthenticated → login → workspace; chat send → streamed run → mid-run controls;
capability-gated schedules empty state; knowledge inventory; settings; global search and the
shortcuts reference; connection-lost banner; a rejected CSRF mutation.
Unit/component gaps above the BFF suite.test(studio): integration suite over the real SDK against mecated --mock #1767 gives 7 integration tests over the real
SDK against a spawned mecated --mock, and the bootstrap gives 26 server proofs — but only 3 web proofs (auth-gate, connection-status-banner, api-client). Bring apps/web to
real coverage with Vitest + Testing Library as the feature stacks land.
CI: a Playwright job (or an extension of the studio job) with cached browsers and
failure artifacts, offline by default (mock runtime, no live provider, no network).
Decide: is there an enforced coverage floor, or are named proofs per acceptance criterion
the only gate?
Questions to settle: feature-sliced vs layer-first in apps/web; where domain hooks and
shared state live; whether apps/contracts stays one package or splits schemas from the
generated client; whether the BFF's routes/ + mecatl/ + auth/ split holds at five
surfaces; and whether the workspace stays apps/ now that it holds exactly one app.
Deliverable: a short structure decision, then the move as one mechanical PR with no
behavior change, so the diff stays reviewable — done before the e2e work lands, so the
proofs are not written against paths that are about to move.
5. Look at all the configuration
apps/.env.example against the real MECATL_* (target) / STUDIO_* (Studio-owned) matrix:
every variable documented once, with default, required-ness, and the fail-closed behaviour
(e.g. RFC 9728 discovery when MECATL_RESOURCE_URL is absent).
apps/Dockerfile (Chainguard node pinned by digest), apps/docker/mecated.Dockerfile, apps/docker-compose.yml, apps/.dockerignore: layer caching, non-root, healthcheck,
image-only refusals, and who refreshes the digest pin.
apps/package.json scripts vs apps/Taskfile.yml vs root task studio:*: one entry point
per action, no third way to run the same thing.
apps/biome.json, tsconfig.base.json, .node-version, .npmrc, the pnpm workspace and
lockfile policy; and a re-read of the studio CI job and the publish-studio release job
against whatever the four axes above change.
6. Release and deploy automation
Every v* release of mecak8s must produce and roll out the matching Studio UI. Today publish-studio runs on the tag with needs: guard only — a sibling of the mecak8s image,
coupled to nothing, and deployed by nothing; the mecak8s chart does not know Studio exists.
Release train: publish-studio joins the ordered train the chart job already relies on
(needs: [guard, publish-mecak8s], "publish the chart only after its default image tag
exists"), signed and attested like its siblings. The SDK ordering is the risk to settle —
Studio builds against the published@stacklok-oss/mecatl-sdk.
Deploy surface: Studio Deployment + Service + Ingress in external mode against the in-cluster
mecak8s Service, off by default, with the image selected by the chart's existing digest / tag / v<chart-version> convention so a chart release deploys the matching UI
with no values edit.
Version pins: studio.image.tag joins the release pin set (Chart.yaml version/appVersion, image.tag, the cut-release bump list) so a chart can never ship a stale UI.
Rollout: GitOps auto-sync, a deploy workflow, or a release-opened values PR — the actual
"automatic deploy" decision; everything above only makes it possible.
Steps
Delivered as sub-issues. Ordering: contract and configuration first (cheap, unblocks everyone),
structure before tests (so proofs are not written against paths that are about to move),
release/deploy once there is a UI worth shipping. Design alignment is active as
its own stream under #1736.
#1776 supersedes the empty placeholders #1769, #1770, and #1771; #1778 subsumes or hands off #1772 (README screenshots).
Delivery
Same acceptance-plan spine as #1736, classified per docs/development-process.md at plan time
rather than asserted here. Expected shape: the agent contract and the configuration sweep look
Routine/Cleanup (no behaviour, contract, or interface change); the folder rethink is Bounded
with a structure decision and a no-behaviour-change move; the e2e coverage is Bounded (new
named proofs, reusing the playwright: / vitest: ac-trace resolvers); design alignment is
Bounded per surface where it changes UI alone. #1845 may be Architectural because it
changes the public auth/status boundary;
the release and deploy automation is Architectural (it changes the release artifact set, the
published chart's contract, and the deployment surface).
User story
As an engineer — and as a coding agent — picking up Mecatl Studio after the in-repo bootstrap
stack has landed, I want the app to look like the approved design, carry its own agent
contract, be covered by browser and unit proofs, sit in a folder structure that scales
past the skeleton, and expose one coherent configuration surface, so that Studio is
maintainable by the whole team instead of only by its author.
Companion to #1736. That story brings Studio into the repository as
apps/and ships itsimage; this one makes the result maintainable. It does not block #1736's stack (#1746–#1752) —
it starts as that stack lands, axis by axis.
Scope
1. Align Studio UI and design (UX, UI, components, colors, CSS)
the component/token gap inventory and maintainer design review.
controls, color palettes, responsive behavior, and accessibility across the shell,
chat, schedules, knowledge, settings, and global search.
its own reviewable change, with desktop/mobile screenshots and interaction evidence.
2. Agent contract for
apps/apps/AGENTS.md(with the repo'sCLAUDE.mdsymlink convention) as the lean correctionfile for the TypeScript workspace — in the spirit of the root AGENTS.md, not a second copy
of the docs: the pnpm-workspace commands, the contracts → committed
openapi.json→generated-client drift rule, the BFF security invariants (sealed HttpOnly cookies,
double-submit CSRF, same-origin check, rate limiting with trusted proxy hops, the
STUDIO_IMAGE=1/STUDIO_ALLOW_UNAUTHENTICATED=1refusals), and the standing"published
@stacklok-oss/mecatl-sdkonly, neversdk/typescript" rule..claude/skills/for the recurring Studio loops — e.g. add a surface(contracts schema → BFF route → regenerate client → web feature → proofs) and touch Studio
configuration. Held to the same bar as the existing skills (
to-acceptance-plan,plan-orchestrate,test-writer).file-route layout, feature-folder boundaries, "no
fetchoutsidelib/api-client", theHono route + Zod schema + problem-details shape, and the Biome formatting contract.
3. Cover the app with e2e and unit tests
(
sdk/typescript/e2e/browser/playwright.config.ts) and theplaywright:resolver alreadywired in
.actrace.yml, so the proofs are citable from acceptance plans.capability-gated schedules empty state; knowledge inventory; settings; global search and the
shortcuts reference; connection-lost banner; a rejected CSRF mutation.
SDK against a spawned
mecated --mock, and the bootstrap gives 26 server proofs — but only3 web proofs (
auth-gate,connection-status-banner,api-client). Bringapps/webtoreal coverage with Vitest + Testing Library as the feature stacks land.
studiojob) with cached browsers andfailure artifacts, offline by default (mock runtime, no live provider, no network).
the only gate?
4. Rethink the app folder structure
apps/{contracts,server,web}, withweb/src/{components/{shell,ui},features,lib,routes}— was sized for a skeleton. Fivefeature stacks (Studio chat: sessions, streamed runs, controls, chat workspace #1747–Studio global search palette and keyboard-shortcuts reference #1751) land on top of it.
apps/web; where domain hooks andshared state live; whether
apps/contractsstays one package or splits schemas from thegenerated client; whether the BFF's
routes/+mecatl/+auth/split holds at fivesurfaces; and whether the workspace stays
apps/now that it holds exactly one app.behavior change, so the diff stays reviewable — done before the e2e work lands, so the
proofs are not written against paths that are about to move.
5. Look at all the configuration
apps/.env.exampleagainst the realMECATL_*(target) /STUDIO_*(Studio-owned) matrix:every variable documented once, with default, required-ness, and the fail-closed behaviour
(e.g. RFC 9728 discovery when
MECATL_RESOURCE_URLis absent).apps/Dockerfile(Chainguard node pinned by digest),apps/docker/mecated.Dockerfile,apps/docker-compose.yml,apps/.dockerignore: layer caching, non-root, healthcheck,image-only refusals, and who refreshes the digest pin.
apps/README.mdreconciled with theuser-docs/building/deployment/studio.mdpage (docs(user-docs): Mecatl Studio web UI deployment page #1765) —one source of truth, not a drifting duplicate.
apps/package.jsonscripts vsapps/Taskfile.ymlvs roottask studio:*: one entry pointper action, no third way to run the same thing.
apps/biome.json,tsconfig.base.json,.node-version,.npmrc, the pnpm workspace andlockfile policy; and a re-read of the
studioCI job and thepublish-studiorelease jobagainst whatever the four axes above change.
6. Release and deploy automation
v*release of mecak8s must produce and roll out the matching Studio UI. Todaypublish-studioruns on the tag withneeds: guardonly — a sibling of the mecak8s image,coupled to nothing, and deployed by nothing; the
mecak8schart does not know Studio exists.publish-studiojoins the ordered train the chart job already relies on(
needs: [guard, publish-mecak8s], "publish the chart only after its default image tagexists"), signed and attested like its siblings. The SDK ordering is the risk to settle —
Studio builds against the published
@stacklok-oss/mecatl-sdk.mecak8s Service, off by default, with the image selected by the chart's existing
digest/tag/v<chart-version>convention so a chart release deploys the matching UIwith no values edit.
studio.image.tagjoins the release pin set (Chart.yamlversion/appVersion,image.tag, thecut-releasebump list) so a chart can never ship a stale UI."automatic deploy" decision; everything above only makes it possible.
Steps
Delivered as sub-issues. Ordering: contract and configuration first (cheap, unblocks everyone),
structure before tests (so proofs are not written against paths that are about to move),
release/deploy once there is a UI worth shipping. Design alignment is active as
its own stream under #1736.
apps/—apps/AGENTS.md, skills, rulesapps/webunit gaps, CI jobactive; implementation under Mecatl Studio: bring the web UI into the repository as apps/ and release its image #1736
#1776 supersedes the empty placeholders #1769, #1770, and #1771; #1778 subsumes or hands off
#1772 (README screenshots).
Delivery
Same acceptance-plan spine as #1736, classified per
docs/development-process.mdat plan timerather than asserted here. Expected shape: the agent contract and the configuration sweep look
Routine/Cleanup (no behaviour, contract, or interface change); the folder rethink is Bounded
with a structure decision and a no-behaviour-change move; the e2e coverage is Bounded (new
named proofs, reusing the
playwright:/vitest:ac-trace resolvers); design alignment isBounded per surface where it changes UI alone. #1845 may be Architectural because it
changes the public auth/status boundary;
the release and deploy automation is Architectural (it changes the release artifact set, the
published chart's contract, and the deployment surface).
Open questions and resolved design decisions
studioCI job or its own? Enforced coverage floor or not?apps/, or becomestudio/?mecak8schart or its own chart, and what rolls the new version out —GitOps auto-sync, a deploy workflow, or a values PR?