[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6QQCPA1EYBASNK6FNHWY7FQ":3,"skill-same:01M6QQCPA1EYBASNK6FNHWY7FQ":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":49,"commitSha":50,"skillDirectory":51,"forksCount":52,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":55,"install":56,"resources":69},"01M6QQCPA1EYBASNK6FNHWY7FQ","app-e2e-tests",null,"Use when writing, changing, reviewing, or debugging Playwright e2e tests in packages\u002Fapp\u002Fe2e (regression keepers and user stories). Covers the mocked opencode server harness and the Bun static server, per-test isolation, page-load and route cost, the suite-wide service-worker block, races with first mount, hover, measurement, clock, dialogs and shortcuts, projects, shards and worker scheduling, rehearsing Windows timing locally, and diagnosing a CI flake from its artifacts. Not for Storybook component tests or e2e\u002Fperformance benchmarks.","SimaDevelopment\u002Fopsiforce","SimaDevelopment","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F153810655?v=4","01M6QQ054MKBDSS43AND3FNHDK","Opsima","01M6QQ058VFT6EY7HCBVN49G8Z",43,"AGPL-3.0","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002F.opencode\u002Fskills\u002Fapp-e2e-tests\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"app e2e tests","e2e tests","app","tests","e2e","regression","static","flake","playwright e2e tests","worker","service-worker","route","writing","projects","ci","server","packages","debugging","component","performance","2026-11-10 10:12:30",false,"---\nname: app-e2e-tests\ndescription: Use when writing, changing, reviewing, or debugging Playwright e2e tests in packages\u002Fapp\u002Fe2e (regression keepers and user stories). Covers the mocked opencode server harness and the Bun static server, per-test isolation, page-load and route cost, the suite-wide service-worker block, races with first mount, hover, measurement, clock, dialogs and shortcuts, projects, shards and worker scheduling, rehearsing Windows timing locally, and diagnosing a CI flake from its artifacts. Not for Storybook component tests or e2e\u002Fperformance benchmarks.\n---\n\n# App e2e tests\n\nRead `packages\u002Fapp\u002Fe2e\u002FAGENTS.md` and the Tests section of `packages\u002Fapp\u002FAGENTS.md` first. They define what to test, which locators to use, and how to assert. `packages\u002Fapp\u002FREADME.md` (E2E Testing) documents run modes and env vars. This skill covers what makes these tests slow or flaky in CI, which runs the suite as two shards per OS on 4-vCPU runners (`.github\u002Fworkflows\u002Ftest.yml:207-229`). Most races below were found on Windows.\n\nAll paths below are relative to `packages\u002Fapp` unless they start with `packages\u002F`, `services\u002F` or `.github\u002F`.\n\n## What a test runs against\n\n- **No opencode backend.** Each shard leg runs `bun --cwd packages\u002Fapp test:e2e:built --shard=\u003Cn>\u002F2` (`test.yml:274-279`). It builds once and starts one Bun static server, `e2e\u002Futils\u002Fserve.ts`, on `127.0.0.1:${PLAYWRIGHT_PORT ?? 3000}`. All 5 workers share it (`playwright.config.ts:23-27`).\n  - `serve.ts` keeps vite preview's SPA fallback rules. Do not move built e2e back to vite preview. On case-insensitive file systems it synchronously reads the whole `dist\u002F_assets` directory (about 3,100 files) for every asset request, so every worker's page loads wait in line. The `e2e\u002Fperformance` configs still use it to keep their baselines comparable.\n- **The API is an in-memory mock per page,** running in the test worker. `mockOpenCodeServer` (`e2e\u002Futils\u002Fmock-server.ts:202`):\n  - answers `page.route(\"**\u002Fapi\u002F**\")` with an Effect HttpApi handler;\n  - fakes `\u002Fapi\u002Fevent` SSE with an init-script `fetch` override;\n  - serves PTY sockets through `routeWebSocket`.\n- **Unmocked routes fail the test** with a 501 and `Unmocked API request` (`mock-server.ts:403-411`). Add the route to `mock-server.ts` once instead of routing around it in a spec.\n- **Build pages with the helpers:**\n  - `mockWorkspace`, `openSession`, `openDraft`, `openWorktreeDraft`, `mockRemoteServer`, `openSettings` (`e2e\u002Futils\u002Fworkspace.ts`);\n  - `setupTimeline` (`e2e\u002Futils\u002Ftimeline.ts:209`) for streamed timelines, `mockServers` (`mock-server.ts:495`) for several origins;\n  - `seed` (`e2e\u002Futils\u002Fapp.ts:61`) for storage, `holdRoute` (`app.ts:207`) to hold requests until you release them.\n- **Deliver events** with the mock's `push`, which buffers until the app connects, or with `installSseTransport` (`e2e\u002Futils\u002Fsse-transport.ts:60`), whose `send` resolves on delivery. After `page.reload()`, await `transport.waitForConnection()` before sending (`e2e\u002Fregression\u002Ftimeline-stream.spec.ts:1700-1702`).\n- **Windows-only failures are races, not Windows code paths.** Mock directories are `C:\u002FOpenCode\u002F\u003Cname>` on every OS (`workspace.ts:33`), and nothing touches the disk. Lazy chunks, stylesheets and responses finish out of order more often on the Windows runners.\n\n## Isolation\n\n- **No shared state between tests.** Each test gets a fresh context, mock handler and init scripts. Outside `e2e\u002Fservice-worker`, which runs under its own config and shares builds through a worker fixture (`e2e\u002Fservice-worker\u002Fcache.spec.ts:21`, `:74`), app e2e has no `beforeAll`, serial mode, `test.extend` or worker fixtures. Keep it that way so `-g` and `PLAYWRIGHT_FULLY_PARALLEL=1` keep working.\n- **Handlers mutate the objects you pass.** Creating a session pushes into `sessions` (`mock-server.ts:839`). Renaming writes `session.title` (`:1340`), and VCS init writes `project.vcs` (`:1019`).\n  - Call `session()`\u002F`project()` inside the test.\n  - `mockWorkspace` and `openSession` copy what you pass (`workspace.ts:36-48`).\n  - `mockOpenCodeServer` and `mockServers` use your objects as they are. Module-level fixtures like `multi-server.spec.ts:10-19` are therefore shared by every test in the file.\n- **Timeline builders keep state per test attempt** (`timeline.ts:165-198`). Call stateful builders such as `assistantMessage` or `partUpdated` inside the test. Only pure ones such as `toolPart` belong at module scope.\n- **Seed before the first `goto`.** `seed` writes storage once per tab behind a `sessionStorage` marker, so reloads keep what the app stored (`app.ts:59-90`). It cannot reset state mid-test.\n\n## Keep it fast\n\n- **Navigate once.** Every `goto` or `reload` is a cold load of the production bundle: about 150 requests per session open, about 125 of them static files. Each test has a new context, and any `page.route` disables the HTTP cache. Reach later states through the UI or through pushed events. Reload only when surviving a reload is the contract.\n- **Service workers are already blocked suite-wide** (`serviceWorkers: \"block\"`, `playwright.config.ts:66-67`). Do not add a per-file block; the 11 in `e2e\u002Fregression` are redundant.\n  - Production registers `\u002Fsw.js` on load (`src\u002Fentry.tsx:54-55`) and precaches about 1,600 files (`globPatterns: [\"**\u002F*\"]`, `vite.pwa.ts:34`). `e2e\u002Fservice-worker\u002F` covers it under its own config. The main config ignores that directory (`playwright.config.ts:33-36`), and CI runs it after shard 2 (`test.yml:281-285`).\n  - Opt back in with `test.use({ serviceWorkers: \"allow\" })` only when the worker is the contract. It never controls the first load (`clientsClaim: false`, `vite.pwa.ts:26-28`). After a reload it can control the page, and page routes may then miss its requests.\n  - Every built-mode page load logs Playwright's block warning. Console collectors must ignore it, as `captureConsoleWarnings` does (`e2e\u002Futils\u002Fcommand-palette.ts:15-26`).\n- **Match new routes with a RegExp or a query-aware glob, not a function.**\n  - A single function matcher makes Playwright intercept `**\u002F*` for the page. Every static asset then round-trips to the test worker (250 continued requests in one Windows test).\n  - Existing `holdRoute` calls such as `review.spec.ts:51` and `mockMissingLocation` (`mock-server.ts:480`) still use functions and pay this. Converting one route gains nothing while another function route is registered on the page.\n  - Globs match the whole URL, so `**\u002Fapi\u002Fvcs\u002Finit` misses `\u002Fapi\u002Fvcs\u002Finit?location...`. Write `\u002F\\\u002Fapi\\\u002Fvcs\\\u002Finit\\?\u002F` or `\"**\u002Fapi\u002Fsession?*\"` (`command-palette.spec.ts:10`, `:28`).\n- **Use the stress fixture only when scale is the contract.** `fixture`, `mockStressTimeline` and `installStressSessionTabs` (`e2e\u002Futils\u002Fsession-fixture.ts:336`, `:436`, `:448`) serve a long timeline with 300-line diffs.\n  - Its cold mount reveals rows only after worker-rendered markdown arrives. Mounting one of its diffs blocked the renderer for 4-11 s on CI.\n  - The 14 regression files that use it held about half of the e2e flakes CI found.\n  - Prefer `openSession` or `setupTimeline` with the few messages the test needs.\n- **Assert inside loops only when each step is the contract.** A web-first assertion that fails its first check retries after 100, 250, 500, then 1,000 ms. The 10-step loop at `mobile-timeline-scroll.spec.ts:688-693` takes about 8 s, mostly backoff.\n- **Real-time waits cost their full length on every run and retry** (`settings-wsl.spec.ts:268-276` waits out a 10 s health check). Prefer `page.clock`:\n  - install it before the `goto` or `reload` whose timers you need (`timeline-stream.spec.ts:1700`);\n  - keep fast-forwards under the client's 45 s event-stream idle timeout (`packages\u002Fclient\u002Fsrc\u002Fsolid\u002Fconnection.ts:35`).\n- **Leave benchmark pacing out of regression tests.** The `sendAll` delays and `messageDelay` exist for benchmarks (`timeline.ts:306`, `mock-server.ts:82`). Hold message pages with `beforeMessagesResponse` instead.\n\n## Wait for state, not time: races CI found\n\nMost of these rarely reproduce on an idle local machine.\n\n- **First mount happens in stages.** `openSession` and `expectSessionTitle` return once the composer is editable and a title is visible. Later the loading header's `\u003Ch1>` is replaced, and a cold timeline keeps its rows hidden while it scrolls itself to the bottom. Before you measure or hover in the session view, wait for something that only the final mount renders, such as the last `[data-timeline-row=\"UserMessage\"]` or the row you click next.\n- **Hovering during a scroll loses the tooltip.** A tooltip closes when its trigger scrolls, and Playwright's mouse never moves again. `session-summary.spec.ts:21-26` waits for the last row before it hovers; without that wait it needed a retry in 6 of 36 Windows runs.\n- **One-shot measurements can hit a detached node.** `locator.evaluate` resolves the element, then evaluates in a second round trip. If the node is replaced in between, `getBoundingClientRect` returns an all-zero rect and `boundingBox` returns `null`. The subagent title-anchor test failed with \"Expected: 0\" in 11 runs until it waited for a timeline-only button (`subagent-child-navigation.spec.ts:60-64`). Take reference measurements once the final structure is in place. Polled reads re-resolve and are safe.\n- **`goto` returns late, so a modal may already be open.**\n  - `goto` waits for `load`, which can fire after the app is already interactive.\n  - An open dialog sets `aria-hidden` on the app root, which hides everything behind it from role locators.\n  - What's New opens 500 ms after the changelog response, and it raced the Settings check in 11 of 36 Windows runs. Hold the request that opens a modal until readiness is asserted, as `startUpgraded` does (`whats-new.spec.ts:227-234`).\n- **`pauseAt(new Date())` right after `install()` can throw** \"Cannot fast-forward to the past\", because the installed clock starts slightly ahead of the runner's clock. Install at an explicit time and pause at an offset from it (`tabs.spec.ts:443-445`).\n- **A shortcut that does nothing is usually a product guard, not timing.** The global handler ignores keys while `dialog.active` (`src\u002Fshell\u002Fcommands\u002Fcommand.tsx:489`). `active` is the top dialog that has not started closing (`packages\u002Fui\u002Fsrc\u002Fcontext\u002Fdialog.tsx:52`, `:310-311`). A dialog that has started closing does not block keys during its 100 ms exit (`dialog.tsx:87`), but any open dialog does, including one you did not expect. Check that guard before you add a wait.\n- **Shortcut tests fail on local macOS; that is not a regression.**\n  - The Desktop Chrome profile reports a Windows UA and `navigator.platform = \"Win32\"`, so the app (`IS_MAC`, `command.tsx:14`) expects Ctrl. On macOS, `ControlOrMeta` sends Meta.\n  - Every test that opens the palette through `openCommandPalette` (`e2e\u002Futils\u002Fcommand-palette.ts:50`) fails locally and passes in CI, as do `tabs.spec.ts:835` and `timeline-cache.spec.ts:163`. Verify on Linux or in CI, or press `Control` in a throwaway copy. Do not edit tests for macOS.\n  - A new test can pick the modifier from the page, as `session-summary.spec.ts:29-30` does.\n- **Font probes mislead.** `document.fonts.check()` can stay false after the font has loaded: when a lazy `@layer` stylesheet activates late, Chromium rebuilds font faces as unloaded. The code-font test failed outright in 9 of 36 Windows runs that way. Polling a probe that lays out text can make itself true. Await `document.fonts.ready`, lay out new text, and then check once (`timeline-cache.spec.ts:281-293`).\n- **Use the harness's signals.** `holdRoute(...).arrived` resolves when the request reaches the route, and `transport.send` resolves after delivery. `setupTimeline(...).waitForPart` resolves once a part renders (`timeline.ts:328`).\n- **Decide which side is wrong.** Three of eight investigated e2e flakes were product bugs, each since fixed in the product:\n  - a stale scroll offset restored when a scroll settles (`src\u002Fsession\u002Ftimeline\u002Fobserve-element-offset.ts`);\n  - a video double-click that also entered native fullscreen (`packages\u002Fgui-extensions\u002Fsrc\u002Ffile\u002Fartifact-view.tsx:617`);\n  - shortcuts dropped while a dialog closed.\n\n  If a user could hit the interleaving, fix the product and keep the assertion.\n\n## Scheduling\n\n- **Two projects split the suite** (`playwright.config.ts:69-86`). `chromium-touch-scroll` runs only `mobile-timeline-scroll.spec.ts`, which the `touchScroll` regex (`:29`) matches, with `fullyParallel: true`.\n  - Projects queue in declaration order, so its 37 tests start after every `chromium` file and spread across all workers. As one serial group, the file took 3-5 minutes in CI.\n  - A rename that the regex misses quietly puts the file back in `chromium` as that serial group.\n  - `--project chromium`, UI mode and the VS Code extension's default project skip the file.\n- **In `chromium`, files are the scheduling unit.** `fullyParallel` is off unless `PLAYWRIGHT_FULLY_PARALLEL=1` (`:42`), so a file's tests run in order on one worker. A long file sets a floor and leaves an idle tail. New regressions go into the area's keeper file (`e2e\u002FAGENTS.md`), so keep each case cheap.\n- **Shards split whole files by test count, not duration** (`test.yml:219-222`). The touch project queues last, so its tests always fall in shard 2. To reproduce a leg's schedule, pass the same `--shard`.\n- **Worker-scoped options fork a worker.** `launchOptions`, `channel`, `headless`, `video`, `trace`, `screenshot` and `defaultBrowserType` give a file its own worker hash.\n  - Playwright groups a project's files by worker hash in order of first appearance, so such a file runs after every default file in its project.\n  - Use test-scoped options instead: `viewport`, `deviceScaleFactor`, `hasTouch`, `isMobile`, `userAgent`, `colorScheme`, `serviceWorkers`, `locale`.\n  - Use `launchOptions` only when a Chromium flag is the contract, with a comment (`patch-border.spec.ts:5-9`).\n- **Never spread `devices[...]` into `test.use`.** iPhone entries set `defaultBrowserType: \"webkit\"`, which forks a worker and needs a browser that CI never installs (`test.yml:269-272`). Copy the fields you need, as `mobile-timeline-scroll.spec.ts:23-28` does.\n- **Retries are not free.** CI retries twice (`playwright.config.ts:44`). Each failure restarts the worker's browser and costs 10-30 s of cleanup on Windows. The first retry then reruns with a trace and a video (`:62-65`).\n- **Timeouts are not a fix.** The limits are 60 s per test and 10 s per assertion (`playwright.config.ts:38-41`). `APP_READY_TIMEOUT` (30 s, `e2e\u002Futils\u002Fwaits.ts:4`) covers only first readiness.\n  - When one wait is genuinely long, scope the timeout to that assertion and say what takes the time. `settings-wsl.spec.ts:268-276` does this with `{ timeout: 20_000 }` for a 10 s health check.\n  - `test.setTimeout` does not lengthen assertions. `timeline-history.spec.ts:73` sets 240 s without a comment and still fails at the 10 s poll at `:127`.\n\n## Before you push\n\nRun from `packages\u002Fapp`:\n\n- [ ] Run in built mode, as CI does: `bun run test:e2e:built -- e2e\u002Fregression\u002F\u003Cfile>.spec.ts`. Dev mode serves unbundled modules, with the API on another origin (`playwright.config.ts:19-21`).\n- [ ] Use a free port when another worktree or agent may hold 3000: `PLAYWRIGHT_PORT=4310 bun run test:e2e:built -- ...`. Built runs never reuse a server, and `serve.ts` fails on a busy port. Stay below 49152 on Windows: it reserves ports from there up and hands them out for outgoing connections (`services\u002Fwww\u002Fsrc\u002Fdocs\u002Fcontent\u002Fcli\u002Fweb.mdx:43`, `packages\u002Fclient\u002Ftest\u002Ffixture\u002Fservice-fixture.ts:108-109`).\n- [ ] To repeat without rebuilding:\n  - build once with `VITE_OPENCODE_TEST_FIXTURES=1 bun run build`;\n  - keep `bun e2e\u002Futils\u002Fserve.ts --host 127.0.0.1 --port 4310` running;\n  - run `PLAYWRIGHT_BUILD=1 PLAYWRIGHT_BASE_URL=http:\u002F\u002F127.0.0.1:4310 bun run test:e2e -- ...`.\n- [ ] Repeat new or changed tests at CI concurrency without retries: `--repeat-each=10 --workers=5 --retries=0 -g \"\u003Ctitle>\"`. Run the file once with `PLAYWRIGHT_FULLY_PARALLEL=1` to prove that order does not matter.\n- [ ] Rehearse Windows timing by delaying assets, not only the CPU. CPU throttling at 4x and 6x did not reproduce the tooltip or title-anchor races, but delayed responses did.\n  - In a throwaway copy, hold the suspect lazy chunk, stylesheet or API response with `holdRoute`, and release it 0.6-1.5 s after `arrived`.\n  - Route worker scripts with `page.context().route`.\n  - A race fix must fail before the fix and pass after it (`packages\u002Fapp\u002FAGENTS.md`, Tests).\n- [ ] Check the cost on a noisy machine. Other agents add load, and local A\u002FBs at a load average of 20-35 were inconclusive.\n  - Compare the medians of interleaved before\u002Fafter runs at `--workers=5`, or the step durations in the report (`bun run test:e2e:report`).\n  - Count every `goto` and `reload` you added.\n- [ ] `bun run typecheck:e2e` reports 4 existing errors: `timeline-stream.spec.ts:1822`, `:1825`, and two `e2e\u002Fperformance` probes. Add none. If you changed `playwright.config.ts`, update `test-browser\u002Fplaywright-config.test.ts`.\n\n## When CI flakes\n\n- **Find it.** e2e runs on pull requests that affect `@opencode\u002Fapp` or `@opencode\u002Fstorybook`, including their workspace dependencies, and on every manual dispatch (`.github\u002Fworkflows\u002Ftest.yml:42-54`). It never runs on dev (`:28`).\n  - Each OS has the legs `app-1` and `app-2` (the shards), `session-ui-components` and `app-components`.\n  - The required `e2e (linux)` and `e2e (windows)` checks only aggregate those legs (`:316-335`).\n  - A pass on retry leaves a leg green. Check each Windows shard report's Flaky list, and make sure your new tests are not on it.\n- **Download:** `gh run download \u003Crun-id> -n playwright-windows-app-2-1 -D \u003Cdir>`. Artifacts are named `playwright-\u003Cos>-\u003Cleg>-\u003Crun attempt>` (`test.yml:301-314`). App e2e results are in `\u003Cdir>\u002Fapp\u002Fe2e\u002F`.\n- **In CI, the failing first attempt has no trace and no video** (`playwright.config.ts:62-65`).\n  - `test-results\u002F\u003Ctest>-\u003Cproject>\u002F` holds `error-context.md` (the aria snapshot at the failure) and `test-failed-1.png`.\n  - `\u003Ctest>-\u003Cproject>-retry1\u002F` holds `trace.zip` and `video.webm` whether the retry passed or failed, plus failure files only if it failed. A second retry records neither.\n- **Read `error-context.md` first.** An open dialog or an element with no accessible name explains most \"element(s) not found\" errors.\n- **Get timings from the retry trace:** `bunx playwright show-trace \u003Cdir>\u002Fapp\u002Fe2e\u002Ftest-results\u002F\u003Ctest>-\u003Cproject>-retry1\u002Ftrace.zip`. To see which side won, compare when a screencast frame changed with when the response landed.\n- **Get the transient state from a failing video,** sampled frame by frame (`ffmpeg -i video.webm -vf fps=4 frame-%03d.png`). That is how the tooltip race showed the tooltip closing in the same frame the timeline rows appeared.\n  - In CI, retry1's video shows the failure only if the retry failed too.\n  - Locally, a failing test keeps its video (`retain-on-failure`), so you can record the race with `--repeat-each` and delayed assets.\n- **Open the report:** `bunx playwright show-report \u003Cdir>\u002Fapp\u002Fe2e\u002Fplaywright-report`. It shows step durations for every attempt, including passing ones.\n","# App e2e tests\n\nRead `packages\u002Fapp\u002Fe2e\u002FAGENTS.md` and the Tests section of `packages\u002Fapp\u002FAGENTS.md` first. They define what to test, which locators to use, and how to assert. `packages\u002Fapp\u002FREADME.md` (E2E Testing) documents run modes and env vars. This skill covers what makes these tests slow or flaky in CI, which runs the suite as two shards per OS on 4-vCPU runners (`.github\u002Fworkflows\u002Ftest.yml:207-229`). Most races below were found on Windows.\n\nAll paths below are relative to `packages\u002Fapp` unless they start with `packages\u002F`, `services\u002F` or `.github\u002F`.\n\n## What a test runs against\n\n- **No opencode backend.** Each shard leg runs `bun --cwd packages\u002Fapp test:e2e:built --shard=\u003Cn>\u002F2` (`test.yml:274-279`). It builds once and starts one Bun static server, `e2e\u002Futils\u002Fserve.ts`, on `127.0.0.1:${PLAYWRIGHT_PORT ?? 3000}`. All 5 workers share it (`playwright.config.ts:23-27`).\n  - `serve.ts` keeps vite preview's SPA fallback rules. Do not move built e2e back to vite preview. On case-insensitive file systems it synchronously reads the whole `dist\u002F_assets` directory (about 3,100 files) for every asset request, so every worker's page loads wait in line. The `e2e\u002Fperformance` configs still use it to keep their baselines comparable.\n- **The API is an in-memory mock per page,** running in the test worker. `mockOpenCodeServer` (`e2e\u002Futils\u002Fmock-server.ts:202`):\n  - answers `page.route(\"**\u002Fapi\u002F**\")` with an Effect HttpApi handler;\n  - fakes `\u002Fapi\u002Fevent` SSE with an init-script `fetch` override;\n  - serves PTY sockets through `routeWebSocket`.\n- **Unmocked routes fail the test** with a 501 and `Unmocked API request` (`mock-server.ts:403-411`). Add the route to `mock-server.ts` once instead of routing around it in a spec.\n- **Build pages with the helpers:**\n  - `mockWorkspace`, `openSession`, `openDraft`, `openWorktreeDraft`, `mockRemoteServer`, `openSettings` (`e2e\u002Futils\u002Fworkspace.ts`);\n  - `setupTimeline` (`e2e\u002Futils\u002Ftimeline.ts:209`) for streamed timelines, `mockServers` (`mock-server.ts:495`) for several origins;\n  - `seed` (`e2e\u002Futils\u002Fapp.ts:61`) for storage, `holdRoute` (`app.ts:207`) to hold requests until you release them.\n- **Deliver events** with the mock's `push`, which buffers until the app connects, or with `installSseTransport` (`e2e\u002Futils\u002Fsse-transport.ts:60`), whose `send` resolves on delivery. After `page.reload()`, await `transport.waitForConnection()` before sending (`e2e\u002Fregression\u002Ftimeline-stream.spec.ts:1700-1702`).\n- **Windows-only failures are races, not Windows code paths.** Mock directories are `C:\u002FOpenCode\u002F\u003Cname>` on every OS (`workspace.ts:33`), and nothing touches the disk. Lazy chunks, stylesheets and responses finish out of order more often on the Windows runners.\n\n## Isolation\n\n- **No shared state between tests.** Each test gets a fresh context, mock handler and init scripts. Outside `e2e\u002Fservice-worker`, which runs under its own config and shares builds through a worker fixture (`e2e\u002Fservice-worker\u002Fcache.spec.ts:21`, `:74`), app e2e has no `beforeAll`, serial mode, `test.extend` or worker fixtures. Keep it that way so `-g` and `PLAYWRIGHT_FULLY_PARALLEL=1` keep working.\n- **Handlers mutate the objects you pass.** Creating a session pushes into `sessions` (`mock-server.ts:839`). Renaming writes `session.title` (`:1340`), and VCS init writes `project.vcs` (`:1019`).\n  - Call `session()`\u002F`project()` inside the test.\n  - `mockWorkspace` and `openSession` copy what you pass (`workspace.ts:36-48`).\n  - `mockOpenCodeServer` and `mockServers` use your objects as they are. Module-level fixtures like `multi-server.spec.ts:10-19` are therefore shared by every test in the file.\n- **Timeline builders keep state per test attempt** (`timeline.ts:165-198`). Call stateful builders such as `assistantMessage` or `partUpdated` inside the test. Only pure ones such as `toolPart` belong at module scope.\n- **Seed before the first `goto`.** `seed` writes storage once per tab behind a `sessionStorage` marker, so reloads keep what the app stored (`app.ts:59-90`). It cannot reset state mid-test.\n\n## Keep it fast\n\n- **Navigate once.** Every `goto` or `reload` is a cold load of the production bundle: about 150 requests per session open, about 125 of them static files. Each test has a new context, and any `page.route` disables the HTTP cache. Reach later states through the UI or through pushed events. Reload only when surviving a reload is the contract.\n- **Service workers are already blocked suite-wide** (`serviceWorkers: \"block\"`, `playwright.config.ts:66-67`). Do not add a per-file block; the 11 in `e2e\u002Fregression` are redundant.\n  - Production registers `\u002Fsw.js` on load (`src\u002Fentry.tsx:54-55`) and precaches about 1,600 files (`globPatterns: [\"**\u002F*\"]`, `vite.pwa.ts:34`). `e2e\u002Fservice-worker\u002F` covers it under its own config. The main config ignores that directory (`playwright.config.ts:33-36`), and CI runs it after shard 2 (`test.yml:281-285`).\n  - Opt back in with `test.use({ serviceWorkers: \"allow\" })` only when the worker is the contract. It never controls the first load (`clientsClaim: false`, `vite.pwa.ts:26-28`). After a reload it can control the page, and page routes may then miss its requests.\n  - Every built-mode page load logs Playwright's block warning. Console collectors must ignore it, as `captureConsoleWarnings` does (`e2e\u002Futils\u002Fcommand-palette.ts:15-26`).\n- **Match new routes with a RegExp or a query-aware glob, not a function.**\n  - A single function matcher makes Playwright intercept `**\u002F*` for the page. Every static asset then round-trips to the test worker (250 continued requests in one Windows test).\n  - Existing `holdRoute` calls such as `review.spec.ts:51` and `mockMissingLocation` (`mock-server.ts:480`) still use functions and pay this. Converting one route gains nothing while another function route is registered on the page.\n  - Globs match the whole URL, so `**\u002Fapi\u002Fvcs\u002Finit` misses `\u002Fapi\u002Fvcs\u002Finit?location...`. Write `\u002F\\\u002Fapi\\\u002Fvcs\\\u002Finit\\?\u002F` or `\"**\u002Fapi\u002Fsession?*\"` (`command-palette.spec.ts:10`, `:28`).\n- **Use the stress fixture only when scale is the contract.** `fixture`, `mockStressTimeline` and `installStressSessionTabs` (`e2e\u002Futils\u002Fsession-fixture.ts:336`, `:436`, `:448`) serve a long timeline with 300-line diffs.\n  - Its cold mount reveals rows only after worker-rendered markdown arrives. Mounting one of its diffs blocked the renderer for 4-11 s on CI.\n  - The 14 regression files that use it held about half of the e2e flakes CI found.\n  - Prefer `openSession` or `setupTimeline` with the few messages the test needs.\n- **Assert inside loops only when each step is the contract.** A web-first assertion that fails its first check retries after 100, 250, 500, then 1,000 ms. The 10-step loop at `mobile-timeline-scroll.spec.ts:688-693` takes about 8 s, mostly backoff.\n- **Real-time waits cost their full length on every run and retry** (`settings-wsl.spec.ts:268-276` waits out a 10 s health check). Prefer `page.clock`:\n  - install it before the `goto` or `reload` whose timers you need (`timeline-stream.spec.ts:1700`);\n  - keep fast-forwards under the client's 45 s event-stream idle timeout (`packages\u002Fclient\u002Fsrc\u002Fsolid\u002Fconnection.ts:35`).\n- **Leave benchmark pacing out of regression tests.** The `sendAll` delays and `messageDelay` exist for benchmarks (`timeline.ts:306`, `mock-server.ts:82`). Hold message pages with `beforeMessagesResponse` instead.\n\n## Wait for state, not time: races CI found\n\nMost of these rarely reproduce on an idle local machine.\n\n- **First mount happens in stages.** `openSession` and `expectSessionTitle` return once the composer is editable and a title is visible. Later the loading header's `\u003Ch1>` is replaced, and a cold timeline keeps its rows hidden while it scrolls itself to the bottom. Before you measure or hover in the session view, wait for something that only the final mount renders, such as the last `[data-timeline-row=\"UserMessage\"]` or the row you click next.\n- **Hovering during a scroll loses the tooltip.** A tooltip closes when its trigger scrolls, and Playwright's mouse never moves again. `session-summary.spec.ts:21-26` waits for the last row before it hovers; without that wait it needed a retry in 6 of 36 Windows runs.\n- **One-shot measurements can hit a detached node.** `locator.evaluate` resolves the element, then evaluates in a second round trip. If the node is replaced in between, `getBoundingClientRect` returns an all-zero rect and `boundingBox` returns `null`. The subagent title-anchor test failed with \"Expected: 0\" in 11 runs until it waited for a timeline-only button (`subagent-child-navigation.spec.ts:60-64`). Take reference measurements once the final structure is in place. Polled reads re-resolve and are safe.\n- **`goto` returns late, so a modal may already be open.**\n  - `goto` waits for `load`, which can fire after the app is already interactive.\n  - An open dialog sets `aria-hidden` on the app root, which hides everything behind it from role locators.\n  - What's New opens 500 ms after the changelog response, and it raced the Settings check in 11 of 36 Windows runs. Hold the request that opens a modal until readiness is asserted, as `startUpgraded` does (`whats-new.spec.ts:227-234`).\n- **`pauseAt(new Date())` right after `install()` can throw** \"Cannot fast-forward to the past\", because the installed clock starts slightly ahead of the runner's clock. Install at an explicit time and pause at an offset from it (`tabs.spec.ts:443-445`).\n- **A shortcut that does nothing is usually a product guard, not timing.** The global handler ignores keys while `dialog.active` (`src\u002Fshell\u002Fcommands\u002Fcommand.tsx:489`). `active` is the top dialog that has not started closing (`packages\u002Fui\u002Fsrc\u002Fcontext\u002Fdialog.tsx:52`, `:310-311`). A dialog that has started closing does not block keys during its 100 ms exit (`dialog.tsx:87`), but any open dialog does, including one you did not expect. Check that guard before you add a wait.\n- **Shortcut tests fail on local macOS; that is not a regression.**\n  - The Desktop Chrome profile reports a Windows UA and `navigator.platform = \"Win32\"`, so the app (`IS_MAC`, `command.tsx:14`) expects Ctrl. On macOS, `ControlOrMeta` sends Meta.\n  - Every test that opens the palette through `openCommandPalette` (`e2e\u002Futils\u002Fcommand-palette.ts:50`) fails locally and passes in CI, as do `tabs.spec.ts:835` and `timeline-cache.spec.ts:163`. Verify on Linux or in CI, or press `Control` in a throwaway copy. Do not edit tests for macOS.\n  - A new test can pick the modifier from the page, as `session-summary.spec.ts:29-30` does.\n- **Font probes mislead.** `document.fonts.check()` can stay false after the font has loaded: when a lazy `@layer` stylesheet activates late, Chromium rebuilds font faces as unloaded. The code-font test failed outright in 9 of 36 Windows runs that way. Polling a probe that lays out text can make itself true. Await `document.fonts.ready`, lay out new text, and then check once (`timeline-cache.spec.ts:281-293`).\n- **Use the harness's signals.** `holdRoute(...).arrived` resolves when the request reaches the route, and `transport.send` resolves after delivery. `setupTimeline(...).waitForPart` resolves once a part renders (`timeline.ts:328`).\n- **Decide which side is wrong.** Three of eight investigated e2e flakes were product bugs, each since fixed in the product:\n  - a stale scroll offset restored when a scroll settles (`src\u002Fsession\u002Ftimeline\u002Fobserve-element-offset.ts`);\n  - a video double-click that also entered native fullscreen (`packages\u002Fgui-extensions\u002Fsrc\u002Ffile\u002Fartifact-view.tsx:617`);\n  - shortcuts dropped while a dialog closed.\n\n  If a user could hit the interleaving, fix the product and keep the assertion.\n\n## Scheduling\n\n- **Two projects split the suite** (`playwright.config.ts:69-86`). `chromium-touch-scroll` runs only `mobile-timeline-scroll.spec.ts`, which the `touchScroll` regex (`:29`) matches, with `fullyParallel: true`.\n  - Projects queue in declaration order, so its 37 tests start after every `chromium` file and spread across all workers. As one serial group, the file took 3-5 minutes in CI.\n  - A rename that the regex misses quietly puts the file back in `chromium` as that serial group.\n  - `--project chromium`, UI mode and the VS Code extension's default project skip the file.\n- **In `chromium`, files are the scheduling unit.** `fullyParallel` is off unless `PLAYWRIGHT_FULLY_PARALLEL=1` (`:42`), so a file's tests run in order on one worker. A long file sets a floor and leaves an idle tail. New regressions go into the area's keeper file (`e2e\u002FAGENTS.md`), so keep each case cheap.\n- **Shards split whole files by test count, not duration** (`test.yml:219-222`). The touch project queues last, so its tests always fall in shard 2. To reproduce a leg's schedule, pass the same `--shard`.\n- **Worker-scoped options fork a worker.** `launchOptions`, `channel`, `headless`, `video`, `trace`, `screenshot` and `defaultBrowserType` give a file its own worker hash.\n  - Playwright groups a project's files by worker hash in order of first appearance, so such a file runs after every default file in its project.\n  - Use test-scoped options instead: `viewport`, `deviceScaleFactor`, `hasTouch`, `isMobile`, `userAgent`, `colorScheme`, `serviceWorkers`, `locale`.\n  - Use `launchOptions` only when a Chromium flag is the contract, with a comment (`patch-border.spec.ts:5-9`).\n- **Never spread `devices[...]` into `test.use`.** iPhone entries set `defaultBrowserType: \"webkit\"`, which forks a worker and needs a browser that CI never installs (`test.yml:269-272`). Copy the fields you need, as `mobile-timeline-scroll.spec.ts:23-28` does.\n- **Retries are not free.** CI retries twice (`playwright.config.ts:44`). Each failure restarts the worker's browser and costs 10-30 s of cleanup on Windows. The first retry then reruns with a trace and a video (`:62-65`).\n- **Timeouts are not a fix.** The limits are 60 s per test and 10 s per assertion (`playwright.config.ts:38-41`). `APP_READY_TIMEOUT` (30 s, `e2e\u002Futils\u002Fwaits.ts:4`) covers only first readiness.\n  - When one wait is genuinely long, scope the timeout to that assertion and say what takes the time. `settings-wsl.spec.ts:268-276` does this with `{ timeout: 20_000 }` for a 10 s health check.\n  - `test.setTimeout` does not lengthen assertions. `timeline-history.spec.ts:73` sets 240 s without a comment and still fails at the 10 s poll at `:127`.\n\n## Before you push\n\nRun from `packages\u002Fapp`:\n\n- [ ] Run in built mode, as CI does: `bun run test:e2e:built -- e2e\u002Fregression\u002F\u003Cfile>.spec.ts`. Dev mode serves unbundled modules, with the API on another origin (`playwright.config.ts:19-21`).\n- [ ] Use a free port when another worktree or agent may hold 3000: `PLAYWRIGHT_PORT=4310 bun run test:e2e:built -- ...`. Built runs never reuse a server, and `serve.ts` fails on a busy port. Stay below 49152 on Windows: it reserves ports from there up and hands them out for outgoing connections (`services\u002Fwww\u002Fsrc\u002Fdocs\u002Fcontent\u002Fcli\u002Fweb.mdx:43`, `packages\u002Fclient\u002Ftest\u002Ffixture\u002Fservice-fixture.ts:108-109`).\n- [ ] To repeat without rebuilding:\n  - build once with `VITE_OPENCODE_TEST_FIXTURES=1 bun run build`;\n  - keep `bun e2e\u002Futils\u002Fserve.ts --host 127.0.0.1 --port 4310` running;\n  - run `PLAYWRIGHT_BUILD=1 PLAYWRIGHT_BASE_URL=http:\u002F\u002F127.0.0.1:4310 bun run test:e2e -- ...`.\n- [ ] Repeat new or changed tests at CI concurrency without retries: `--repeat-each=10 --workers=5 --retries=0 -g \"\u003Ctitle>\"`. Run the file once with `PLAYWRIGHT_FULLY_PARALLEL=1` to prove that order does not matter.\n- [ ] Rehearse Windows timing by delaying assets, not only the CPU. CPU throttling at 4x and 6x did not reproduce the tooltip or title-anchor races, but delayed responses did.\n  - In a throwaway copy, hold the suspect lazy chunk, stylesheet or API response with `holdRoute`, and release it 0.6-1.5 s after `arrived`.\n  - Route worker scripts with `page.context().route`.\n  - A race fix must fail before the fix and pass after it (`packages\u002Fapp\u002FAGENTS.md`, Tests).\n- [ ] Check the cost on a noisy machine. Other agents add load, and local A\u002FBs at a load average of 20-35 were inconclusive.\n  - Compare the medians of interleaved before\u002Fafter runs at `--workers=5`, or the step durations in the report (`bun run test:e2e:report`).\n  - Count every `goto` and `reload` you added.\n- [ ] `bun run typecheck:e2e` reports 4 existing errors: `timeline-stream.spec.ts:1822`, `:1825`, and two `e2e\u002Fperformance` probes. Add none. If you changed `playwright.config.ts`, update `test-browser\u002Fplaywright-config.test.ts`.\n\n## When CI flakes\n\n- **Find it.** e2e runs on pull requests that affect `@opencode\u002Fapp` or `@opencode\u002Fstorybook`, including their workspace dependencies, and on every manual dispatch (`.github\u002Fworkflows\u002Ftest.yml:42-54`). It never runs on dev (`:28`).\n  - Each OS has the legs `app-1` and `app-2` (the shards), `session-ui-components` and `app-components`.\n  - The required `e2e (linux)` and `e2e (windows)` checks only aggregate those legs (`:316-335`).\n  - A pass on retry leaves a leg green. Check each Windows shard report's Flaky list, and make sure your new tests are not on it.\n- **Download:** `gh run download \u003Crun-id> -n playwright-windows-app-2-1 -D \u003Cdir>`. Artifacts are named `playwright-\u003Cos>-\u003Cleg>-\u003Crun attempt>` (`test.yml:301-314`). App e2e results are in `\u003Cdir>\u002Fapp\u002Fe2e\u002F`.\n- **In CI, the failing first attempt has no trace and no video** (`playwright.config.ts:62-65`).\n  - `test-results\u002F\u003Ctest>-\u003Cproject>\u002F` holds `error-context.md` (the aria snapshot at the failure) and `test-failed-1.png`.\n  - `\u003Ctest>-\u003Cproject>-retry1\u002F` holds `trace.zip` and `video.webm` whether the retry passed or failed, plus failure files only if it failed. A second retry records neither.\n- **Read `error-context.md` first.** An open dialog or an element with no accessible name explains most \"element(s) not found\" errors.\n- **Get timings from the retry trace:** `bunx playwright show-trace \u003Cdir>\u002Fapp\u002Fe2e\u002Ftest-results\u002F\u003Ctest>-\u003Cproject>-retry1\u002Ftrace.zip`. To see which side won, compare when a screencast frame changed with when the response landed.\n- **Get the transient state from a failing video,** sampled frame by frame (`ffmpeg -i video.webm -vf fps=4 frame-%03d.png`). That is how the tooltip race showed the tooltip closing in the same frame the timeline rows appeared.\n  - In CI, retry1's video shows the failure only if the retry failed too.\n  - Locally, a failing test keeps its video (`retain-on-failure`), so you can record the race with `--repeat-each` and delayed assets.\n- **Open the report:** `bunx playwright show-report \u003Cdir>\u002Fapp\u002Fe2e\u002Fplaywright-report`. It shows step durations for every attempt, including passing ones.\n",{"name":5,"description":7},"main","c54d8e35f50ac35b3b2c7ba8d4fca2205a740cb9","frontend\u002Fopencode\u002F.opencode\u002Fskills\u002Fapp-e2e-tests",9,"https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce","https:\u002F\u002Fgithub.com\u002FSimaDevelopment","2026-11-05 06:13:10",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add SimaDevelopment\u002Fopsiforce --skill app-e2e-tests",{"tool":62,"command":63},"bunx","bunx skills add SimaDevelopment\u002Fopsiforce --skill app-e2e-tests",{"tool":65,"command":66},"pnpm","pnpm dlx skills add SimaDevelopment\u002Fopsiforce --skill app-e2e-tests","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002F.opencode\u002Fskills\u002Fapp-e2e-tests\u002FSKILL.md",true,[],[71,109,129,150,160,186],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":88,"publishedAt":44},"01M6QQKHZFGFB6A111RW2A9WC0","figma-use","**MANDATORY prerequisite** — you MUST invoke this skill BEFORE every `use_figma` tool call. NEVER call `use_figma` directly without loading this skill first. Skipping it causes common, hard-to-debug failures. Trigger whenever the user wants to perform a write action or a unique read action that requires JavaScript execution in the Figma file context — e.g. create\u002Fedit\u002Fdelete nodes, set up variables or tokens, build components and variants, modify auto-layout or fills, bind variables to properties, or inspect file structure programmatically.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-use\u002FSKILL.md",[77,80,85],{"categoryNo":78,"categoryName":79,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":81,"categoryName":82,"taxonomyType":83,"taxonomyTypeDesc":84},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",{"categoryNo":86,"categoryName":87,"taxonomyType":83,"taxonomyTypeDesc":84},"01JBSEEDCAT000000000000032","设计师",[89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108],"figma use","figma","use","components","create","delete","tool","read","write","edit","context","auto","structure","tokens","bind","Variables","you","inspect","variants","invoke",{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":120,"publishedAt":44},"01M6QQK1AMA90K6DFAVANYQ9X3","figma-use-slides","This skill helps agents use Figma's use_figma MCP tool in the Slides context. Can be used alongside figma-use which has foundational context for using the use_figma tool.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-use-slides\u002FSKILL.md",[115,118,119],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":78,"categoryName":79,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":86,"categoryName":87,"taxonomyType":83,"taxonomyTypeDesc":84},[89,90,121,122,91,123,95,99,124,125,126,127,128],"figma use slides","slides","MCP","false","which","can","for","figma mcp",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":137,"publishedAt":44},"01M6QQJTR9QYK297WQ1X792D63","figma-use-motion","Motion \u002F animation context for the `use_figma` MCP tool — animating Figma nodes via manual keyframes, animation styles, easing, and timeline duration. Load alongside figma-use whenever a task involves adding, editing, or inspecting animation on a node.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-use-motion\u002FSKILL.md",[135,136],{"categoryNo":78,"categoryName":79,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":86,"categoryName":87,"taxonomyType":83,"taxonomyTypeDesc":84},[89,90,138,91,139,123,140,95,141,142,99,143,144,145,124,146,147,148,149,127],"figma use motion","motion","Animation","node","editing","Nodes","task","keyframes","via","styles","manual","load",{"skillNo":151,"name":152,"nameZh":6,"description":153,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":154,"categories":155,"keywords":158,"publishedAt":44},"01M6QQJNZ067ZRXZ6A0QB9M6SB","figma-use-figjam","This skill helps agents use Figma's use_figma MCP tool in the FigJam context. Can be used alongside figma-use which has foundational context for using the use_figma tool.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-use-figjam\u002FSKILL.md",[156,157],{"categoryNo":78,"categoryName":79,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":86,"categoryName":87,"taxonomyType":83,"taxonomyTypeDesc":84},[89,90,159,91,123,95,99,124,125,126,127,128],"figma use figjam",{"skillNo":161,"name":162,"nameZh":6,"description":163,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":164,"categories":165,"keywords":167,"publishedAt":44},"01M6QQJ7NEHXPE33VRVHYYM7YE","figma-swiftui","SwiftUI ↔ Figma translation. Use whenever the user mentions Swift, SwiftUI, iOS, iPhone, or iPad — in EITHER direction — translating a Figma design into SwiftUI (design → code), or pushing SwiftUI views \u002F screens \u002F tokens back into a Figma file (code → design). Triggers on phrases like 'implement this Figma design in SwiftUI', 'build this screen in Swift', 'push this SwiftUI view to Figma', 'mirror my Swift code in a Figma file', or whenever a Figma URL appears alongside `.swift` files \u002F an `.xcodeproj`. Routes to a direction-specific reference doc; loads alongside `figma-use` for the code → design path.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-swiftui\u002FSKILL.md",[166],{"categoryNo":86,"categoryName":87,"taxonomyType":83,"taxonomyTypeDesc":84},[90,168,169,170,171,172,173,174,175,176,177,178,179,180,124,181,182,183,184,185],"figma swiftui","swiftui","design","swift","code","push","doc","files","translation","back","either","iphone","ipad","screen","direction","URL","my","path",{"skillNo":187,"name":188,"nameZh":6,"description":189,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":190,"categories":191,"keywords":193,"publishedAt":44},"01M6QQJ3WZ3M90BZQ5TD305PKN","figma-shaders","**MANDATORY prerequisite** — load this skill before calling `create_shader` or `update_shader`. Use when the user asks to create, author, change, fix, or iterate on a shader effect, shader fill, custom effect, custom fill, or procedural shader in Figma.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-shaders\u002FSKILL.md",[192],{"categoryNo":86,"categoryName":87,"taxonomyType":83,"taxonomyTypeDesc":84},[194,90,195,196,93,197,198,199,200,91,201,202,124,203,149,204,205,206,207],"shaders","figma shaders","change","update","effect","user","fix","shader","calling","procedural","fill","iterate","custom","author"]