[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XCPRM7MFX44N8FBJNA2MS2":3,"skill-same:01M6XCPRM7MFX44N8FBJNA2MS2":73},{"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":54,"ownerHtmlUrl":55,"ownerBlogUrl":56,"ownerDescription":57,"updatedAt":58,"install":59,"resources":72},"01M6XCPRM7MFX44N8FBJNA2MS2","cl_route-every-surface",null,"Use whenever you add a new page\u002Fview or a dialog\u002Fmodal to the web UI (apps\u002Fweb), or when a user says \"I want to link to X\", \"refresh loses my place\", \"make this bookmarkable\", \"this dialog doesn't survive reload\". Every page and cold-load-coherent dialog gets a URL param.","ferterahadi\u002Fcanary-lab","ferterahadi","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F148174839?v=4","01M6XCHCY39C7QEF3JZJ3VZGNZ","Fer Terahadi","01M6XCHD1ZE9926NBC97ZMZZRY",4,"MIT","https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab\u002Fblob\u002Fmain\u002F.codex\u002Fskills\u002Fcl_route-every-surface\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],"cl route every surface","cl","route","cold","when","load","my","you","URL","web ui","make","ui","add","apps","coherent","use","refresh","view","dialog","page","2026-11-10 10:12:30",false,"---\nname: cl_route-every-surface\ndescription: Use whenever you add a new page\u002Fview or a dialog\u002Fmodal to the web UI (apps\u002Fweb), or when a user says \"I want to link to X\", \"refresh loses my place\", \"make this bookmarkable\", \"this dialog doesn't survive reload\". Every page and cold-load-coherent dialog gets a URL param.\n---\n\n\u003C!-- GENERATED FROM .claude\u002Fskills — DO NOT EDIT.\n     Run `npm run gen:skills` after editing the source skill (the build does this too). -->\n\n# Route Every Surface — A New Page\u002FDialog Gets a URL\n\nThe pattern the project enforces: **the URL is the source of truth for where you\nare.** A view, a selected run, and an open dialog all serialize to the URL so a\nrefresh, a copy-pasted link, or a new tab rehydrates the exact same screen.\n\nForgetting this is how a new dialog \"vanishes on refresh\" and a new page can't be\nlinked to.\n\n## The cold-load test (decides whether to route a dialog)\n\n> **Route a dialog only if landing on its URL cold — fresh tab, no prior in-memory\n> state — produces something coherent.**\n\n| Route it | Don't route it |\n|---|---|\n| Pages\u002Fviews (coverage, cleanup, flights) | Collision-confirm (fires off a live run event) |\n| Selected run | Services \u002F Runs-list \u002F Portify-picker (transient pickers) |\n| Config \u002F settings panels; changed-tests review and notifications | Any modal that reacts to a momentary event you can't reconstruct |\n| Wizards (open-state, not per-step) | Confirmation prompts |\n| Verification, flight-start, flight-new | |\n\nPages always route. Dialogs route selectively. When unsure, ask: *if I paste this\nURL into a clean browser, does the dialog have everything it needs?* If it depends\non live in-memory state, leave it ephemeral.\n\n## The one home — extend, don't fork\n\nAll route serialization lives in **one module**:\n`apps\u002Fweb\u002Fsrc\u002Fshared\u002Flib\u002Fworkspace-view-state.ts` (`readPersistedView` \u002F\n`persistView` \u002F `onViewChangedInOtherTab`). Navigation state and dialog\nprecedence live in `apps\u002Fweb\u002Fsrc\u002Fshared\u002Fstate\u002Fnav-state.ts`; the\n`use-workspace-navigation.ts` hook hydrates and persists them. Add a param in\nthat path — **never** a second URL-writing mechanism, and **never** react-router\n(deliberate: query-param style, no router dependency). See [[cl_reuse-shared-logic]].\n\nThe server already serves `index.html` for any deep link (catch-all in\n`apps\u002Fweb-server\u002Fsrc\u002Fserver.ts`) — refreshing `\u002F?...` never 404s. No server change is\nneeded for new routes.\n\n## Two tiers — pick the right one\n\n| Tier | Params | Channels | Use for |\n|---|---|---|---|\n| **Durable nav** | `view`, `feature` | URL + localStorage + cross-tab `storage` | top-level location shared across tabs |\n| **URL-only** | `run`, `dialog`, `flight`, and their qualifiers | URL only | run selection (two tabs may compare runs) + dialog open-state (a dialog open in one tab must NOT pop open in another) + stage, tab, review, and drill-through context |\n\nA new dialog is almost always **URL-only** (the `dialog` param). Do not mirror\ndialog open-state to localStorage or broadcast it cross-tab.\n\n## Current URL schema\n\n```\n?view=coverage&feature=checkout                    → coverage page\n?feature=checkout&run=7cvh                          → run selected in detail pane\n?feature=checkout&dialog=config                     → feature config (Playwright — the no-tab default)\n?feature=checkout&dialog=config&tab=ports           → feature config, on a named tab\n?feature=checkout&dialog=verification               → Verify-config dialog\n?feature=checkout&dialog=flight-start               → flight stage-entry launcher (feature-scoped)\n?feature=checkout&dialog=flight-fresh               → same launcher in START-FRESH intent\n?dialog=flight-new                                  → new-flight launcher (intent + repo picker)\n?dialog=demo                                        → demo chooser (needs only GET \u002Fapi\u002Fonboarding)\n?dialog=settings                                    → Project Settings (needs only GET \u002Fapi\u002Fproject-config)\n?view=flights&flight=fl_abc                         → flight detail (omit flight = flights list)\n?view=flights&flight=fl_abc&stage=docs              → that flight, on a named stage (omit = follow-mode)\n?dialog=notifications                               → notifications center\n?dialog=tests-review&reviewFile=e2e\u002Fcart.spec.ts    → changed-tests review focused on a file\n?dialog=settings&models=codex                      → Project Settings with Codex models open\n?feature=checkout&run=7cvh&test=cart%20fails        → run detail focused on a failure\n?feature=checkout&run=7cvh&tests=recorded           → recorded tests for that run\n?feature=checkout&run=7cvh&runtab=changes           → arrival: the run's Changes dialog\n?feature=checkout&run=7cvh&changes=1                → the run's Changes dialog open (legacy rview=run-wide reads as this)\n?feature=checkout&run=7cvh&runtab=results&test=cart%20fails&attempt=2&journal=entry\n                                                    → that test's dialog on its 2nd run, its journal entry open\n?feature=checkout&run=7cvh&runtab=results&test=cart%20fails&ttab=artifacts\n                                                    → that test's dialog on its Artifacts tab\n?feature=checkout&run=7cvh&runtab=results&test=cart%20fails&cycle=2\n                                                    → older link: the run that checked repair cycle 2\n?feature=checkout&run=7cvh&runtab=services&svc=api&exec=3&lines=120-140\n                                                    → api's retained log for execution 3, lines highlighted\n```\n\n`RouteDialog = 'config' | 'verification' | 'flight-start' | 'flight-fresh' |\n'flight-new' | 'demo' | 'settings' | 'tests-review' | 'notifications'`.\n`flight` only qualifies `view=flights` (absent = the flights landing list), and\n`stage` only qualifies an open flight. `tab` only qualifies `dialog=config`;\n`models` only qualifies `dialog=settings`; `review*` params only qualify\n`dialog=tests-review`; `test`, `runtab`, and `tests` qualify a selected run.\nInside a run, the detail reports the reader's place (`RunLocation`: `runtab`,\n`rview`, `changes`, `test`, `attempt`, `cycle`, `ttab`, `journal`, `svc`, `exec`\u002F`lines`\u002F`match`) through\n`setRunLocation`; each qualifier is gated on the one it belongs to, a reported\nplace supersedes the arrival that opened the run, and a stale name is explained\nby the view (`stale-*-link`) rather than dropped. Embedded run details pass no\n`onLocationChange` and stay unrouted.\n`from` records the flight a workspace\u002Fcoverage drill-through came from and is\ndropped on the flights view. `persistView`\u002F`readPersistedView` own these gates.\nBecause the config dialog is qualified by the DURABLE `feature` param, any opener\nthat opens it for a feature other than the selected one must `setSelectedFeature`\ntoo — otherwise the deep link names the wrong suite.\n`wf`, `task` and `draft` are tombstoned qualifiers (see Gotchas). Unknown\ndialog values are ignored on read.\n\n## Checklist — adding a new page\n\n1. Add the value to the `WorkspaceView` union in `workspace-view-state.ts` and the\n   `VIEWS` array.\n2. Render it from `App.tsx` and add the nav entry that calls `setView(...)` from\n   `use-workspace-navigation.ts`.\n3. The hook's persist effect already serializes `view` — nothing else to wire.\n4. Confirm refresh restores it (it reads from the URL on load).\n\n## Checklist — adding a new routed dialog\n\n1. **Add it to the enum** — `RouteDialog` + `DIALOGS` array in\n   `workspace-view-state.ts`. If it needs an id qualifier, follow the live\n   precedent — `flight` qualifying `view=flights` — add a param and gate it in\n   `persistView`\u002F`readPersistedView` so it's dropped unless your dialog\u002Fview is\n   active. Don't reuse `wf`, `task` or `draft` — all three are tombstoned (see Gotchas).\n2. **Hydrate on mount** — add the dialog to `initialNavState` in `nav-state.ts`\n   and seed its open-state from `SEED` in `use-workspace-navigation.ts`.\n3. **Derive it into the route** — add it to `routedDialog` in `nav-state.ts`\n   (precedence = z-order), and expose its setter from the hook so the persist\n   effect writes the `dialog` param when it is open.\n4. **Where does the open-state live?**\n   - **Navigation-owned** (like config and settings): seed it in the navigation\n     hook and render from `App.tsx`.\n   - **In a context:** hydrate the navigation state first, then pass its open\n     flag to the context instead of adding a second URL writer.\n   - **In a child component** (like the Verify dialog in `RunsColumn`): make the\n     child's open-state a **controlled\u002Funcontrolled hybrid** — accept\n     `open?`\u002F`onOpenChange?` props, fall back to internal state when absent (keeps\n     the child's own unit tests working), and have the navigation hook own the\n     state + drive the route. Lift, don't duplicate.\n5. **Add a round-trip test** to `workspace-view-state.test.ts` (URL round-trip +\n   the URL-only\u002FlocalStorage-exclusion assertion).\n\n## Gotchas\n\n- **Hook order** — keep any state the persist effect reads in\n  `use-workspace-navigation.ts` before that effect and include its primitive\n  fields in the dependency list. A dialog can stay open while a qualifier\n  changes, so depending only on `dialog` leaves its URL stale.\n- **Stale-run guard wipes a hydrated run** — the run-selection reconciliation\n  effect clears `selectedRunId` to \"latest\" before runs arrive over the WS. Seed\n  `pendingRunSelectionRef` with the persisted run so the hydrated run survives\n  until its run loads.\n- **Cross-tab scope** — `onViewChangedInOtherTab` emits the **durable tier only**\n  (view\u002Ffeature). Never push run\u002Fdialog through it.\n- **Qualifiers belong to their dialog\u002Fview only** — `persistView` drops `flight`\n  unless `view === 'flights'` (the live precedent to copy — same\n  drop-unless-active gate). `wf` (old portify-revisit id), `task` (old\n  evaluation-dialog id) and `draft` (old external-authoring id) are tombstoned:\n  `persistView` force-deletes all three on every write regardless of state, so\n  stale deep links don't carry them forward. If your dialog needs its own id,\n  add a new param with the flight-style gate; never reuse `wf`, `task` or `draft`.\n\n## Deliberately NOT done (don't \"fix\" these without a reason)\n\n- **Back-button closes dialog** — would need user-vs-programmatic transition\n  tracking (auto-select-first-feature would spam history). The goal is *revisit*\n  (deep-link + refresh), which `replaceState` delivers. Left out on purpose.\n- **Pretty paths** (`\u002Fcoverage\u002Fcheckout`) — query-param style is intentional; a\n  router buys nothing for a single-screen internal tool.\n\n## Relationship to neighbours\n\n- [[cl_reuse-shared-logic]] — routing has ONE home (`workspace-view-state.ts`);\n  extend it, never add a second URL-writing path or a router lib.\n- [[cl_ui-design-philosophy]] — a new surface's *look* is governed here; its\n  *route* is governed by this skill.\n- [[cl_ws-driven-state]] — that skill keeps UI state live after a server mutation;\n  this skill keeps UI state addressable in the URL. Different axes, both required\n  for a surface to feel native.\n- [[cl_verify-changes]] — `apps\u002Fweb\u002F**` changes need the canary-apply cycle (Tier 3\n  says who runs it) to confirm refresh\u002Fdeep-link behaviour end-to-end; unit tests\n  cover the serialization.\n","\u003C!-- GENERATED FROM .claude\u002Fskills — DO NOT EDIT.\n     Run `npm run gen:skills` after editing the source skill (the build does this too). -->\n\n# Route Every Surface — A New Page\u002FDialog Gets a URL\n\nThe pattern the project enforces: **the URL is the source of truth for where you\nare.** A view, a selected run, and an open dialog all serialize to the URL so a\nrefresh, a copy-pasted link, or a new tab rehydrates the exact same screen.\n\nForgetting this is how a new dialog \"vanishes on refresh\" and a new page can't be\nlinked to.\n\n## The cold-load test (decides whether to route a dialog)\n\n> **Route a dialog only if landing on its URL cold — fresh tab, no prior in-memory\n> state — produces something coherent.**\n\n| Route it | Don't route it |\n|---|---|\n| Pages\u002Fviews (coverage, cleanup, flights) | Collision-confirm (fires off a live run event) |\n| Selected run | Services \u002F Runs-list \u002F Portify-picker (transient pickers) |\n| Config \u002F settings panels; changed-tests review and notifications | Any modal that reacts to a momentary event you can't reconstruct |\n| Wizards (open-state, not per-step) | Confirmation prompts |\n| Verification, flight-start, flight-new | |\n\nPages always route. Dialogs route selectively. When unsure, ask: *if I paste this\nURL into a clean browser, does the dialog have everything it needs?* If it depends\non live in-memory state, leave it ephemeral.\n\n## The one home — extend, don't fork\n\nAll route serialization lives in **one module**:\n`apps\u002Fweb\u002Fsrc\u002Fshared\u002Flib\u002Fworkspace-view-state.ts` (`readPersistedView` \u002F\n`persistView` \u002F `onViewChangedInOtherTab`). Navigation state and dialog\nprecedence live in `apps\u002Fweb\u002Fsrc\u002Fshared\u002Fstate\u002Fnav-state.ts`; the\n`use-workspace-navigation.ts` hook hydrates and persists them. Add a param in\nthat path — **never** a second URL-writing mechanism, and **never** react-router\n(deliberate: query-param style, no router dependency). See [[cl_reuse-shared-logic]].\n\nThe server already serves `index.html` for any deep link (catch-all in\n`apps\u002Fweb-server\u002Fsrc\u002Fserver.ts`) — refreshing `\u002F?...` never 404s. No server change is\nneeded for new routes.\n\n## Two tiers — pick the right one\n\n| Tier | Params | Channels | Use for |\n|---|---|---|---|\n| **Durable nav** | `view`, `feature` | URL + localStorage + cross-tab `storage` | top-level location shared across tabs |\n| **URL-only** | `run`, `dialog`, `flight`, and their qualifiers | URL only | run selection (two tabs may compare runs) + dialog open-state (a dialog open in one tab must NOT pop open in another) + stage, tab, review, and drill-through context |\n\nA new dialog is almost always **URL-only** (the `dialog` param). Do not mirror\ndialog open-state to localStorage or broadcast it cross-tab.\n\n## Current URL schema\n\n```\n?view=coverage&feature=checkout                    → coverage page\n?feature=checkout&run=7cvh                          → run selected in detail pane\n?feature=checkout&dialog=config                     → feature config (Playwright — the no-tab default)\n?feature=checkout&dialog=config&tab=ports           → feature config, on a named tab\n?feature=checkout&dialog=verification               → Verify-config dialog\n?feature=checkout&dialog=flight-start               → flight stage-entry launcher (feature-scoped)\n?feature=checkout&dialog=flight-fresh               → same launcher in START-FRESH intent\n?dialog=flight-new                                  → new-flight launcher (intent + repo picker)\n?dialog=demo                                        → demo chooser (needs only GET \u002Fapi\u002Fonboarding)\n?dialog=settings                                    → Project Settings (needs only GET \u002Fapi\u002Fproject-config)\n?view=flights&flight=fl_abc                         → flight detail (omit flight = flights list)\n?view=flights&flight=fl_abc&stage=docs              → that flight, on a named stage (omit = follow-mode)\n?dialog=notifications                               → notifications center\n?dialog=tests-review&reviewFile=e2e\u002Fcart.spec.ts    → changed-tests review focused on a file\n?dialog=settings&models=codex                      → Project Settings with Codex models open\n?feature=checkout&run=7cvh&test=cart%20fails        → run detail focused on a failure\n?feature=checkout&run=7cvh&tests=recorded           → recorded tests for that run\n?feature=checkout&run=7cvh&runtab=changes           → arrival: the run's Changes dialog\n?feature=checkout&run=7cvh&changes=1                → the run's Changes dialog open (legacy rview=run-wide reads as this)\n?feature=checkout&run=7cvh&runtab=results&test=cart%20fails&attempt=2&journal=entry\n                                                    → that test's dialog on its 2nd run, its journal entry open\n?feature=checkout&run=7cvh&runtab=results&test=cart%20fails&ttab=artifacts\n                                                    → that test's dialog on its Artifacts tab\n?feature=checkout&run=7cvh&runtab=results&test=cart%20fails&cycle=2\n                                                    → older link: the run that checked repair cycle 2\n?feature=checkout&run=7cvh&runtab=services&svc=api&exec=3&lines=120-140\n                                                    → api's retained log for execution 3, lines highlighted\n```\n\n`RouteDialog = 'config' | 'verification' | 'flight-start' | 'flight-fresh' |\n'flight-new' | 'demo' | 'settings' | 'tests-review' | 'notifications'`.\n`flight` only qualifies `view=flights` (absent = the flights landing list), and\n`stage` only qualifies an open flight. `tab` only qualifies `dialog=config`;\n`models` only qualifies `dialog=settings`; `review*` params only qualify\n`dialog=tests-review`; `test`, `runtab`, and `tests` qualify a selected run.\nInside a run, the detail reports the reader's place (`RunLocation`: `runtab`,\n`rview`, `changes`, `test`, `attempt`, `cycle`, `ttab`, `journal`, `svc`, `exec`\u002F`lines`\u002F`match`) through\n`setRunLocation`; each qualifier is gated on the one it belongs to, a reported\nplace supersedes the arrival that opened the run, and a stale name is explained\nby the view (`stale-*-link`) rather than dropped. Embedded run details pass no\n`onLocationChange` and stay unrouted.\n`from` records the flight a workspace\u002Fcoverage drill-through came from and is\ndropped on the flights view. `persistView`\u002F`readPersistedView` own these gates.\nBecause the config dialog is qualified by the DURABLE `feature` param, any opener\nthat opens it for a feature other than the selected one must `setSelectedFeature`\ntoo — otherwise the deep link names the wrong suite.\n`wf`, `task` and `draft` are tombstoned qualifiers (see Gotchas). Unknown\ndialog values are ignored on read.\n\n## Checklist — adding a new page\n\n1. Add the value to the `WorkspaceView` union in `workspace-view-state.ts` and the\n   `VIEWS` array.\n2. Render it from `App.tsx` and add the nav entry that calls `setView(...)` from\n   `use-workspace-navigation.ts`.\n3. The hook's persist effect already serializes `view` — nothing else to wire.\n4. Confirm refresh restores it (it reads from the URL on load).\n\n## Checklist — adding a new routed dialog\n\n1. **Add it to the enum** — `RouteDialog` + `DIALOGS` array in\n   `workspace-view-state.ts`. If it needs an id qualifier, follow the live\n   precedent — `flight` qualifying `view=flights` — add a param and gate it in\n   `persistView`\u002F`readPersistedView` so it's dropped unless your dialog\u002Fview is\n   active. Don't reuse `wf`, `task` or `draft` — all three are tombstoned (see Gotchas).\n2. **Hydrate on mount** — add the dialog to `initialNavState` in `nav-state.ts`\n   and seed its open-state from `SEED` in `use-workspace-navigation.ts`.\n3. **Derive it into the route** — add it to `routedDialog` in `nav-state.ts`\n   (precedence = z-order), and expose its setter from the hook so the persist\n   effect writes the `dialog` param when it is open.\n4. **Where does the open-state live?**\n   - **Navigation-owned** (like config and settings): seed it in the navigation\n     hook and render from `App.tsx`.\n   - **In a context:** hydrate the navigation state first, then pass its open\n     flag to the context instead of adding a second URL writer.\n   - **In a child component** (like the Verify dialog in `RunsColumn`): make the\n     child's open-state a **controlled\u002Funcontrolled hybrid** — accept\n     `open?`\u002F`onOpenChange?` props, fall back to internal state when absent (keeps\n     the child's own unit tests working), and have the navigation hook own the\n     state + drive the route. Lift, don't duplicate.\n5. **Add a round-trip test** to `workspace-view-state.test.ts` (URL round-trip +\n   the URL-only\u002FlocalStorage-exclusion assertion).\n\n## Gotchas\n\n- **Hook order** — keep any state the persist effect reads in\n  `use-workspace-navigation.ts` before that effect and include its primitive\n  fields in the dependency list. A dialog can stay open while a qualifier\n  changes, so depending only on `dialog` leaves its URL stale.\n- **Stale-run guard wipes a hydrated run** — the run-selection reconciliation\n  effect clears `selectedRunId` to \"latest\" before runs arrive over the WS. Seed\n  `pendingRunSelectionRef` with the persisted run so the hydrated run survives\n  until its run loads.\n- **Cross-tab scope** — `onViewChangedInOtherTab` emits the **durable tier only**\n  (view\u002Ffeature). Never push run\u002Fdialog through it.\n- **Qualifiers belong to their dialog\u002Fview only** — `persistView` drops `flight`\n  unless `view === 'flights'` (the live precedent to copy — same\n  drop-unless-active gate). `wf` (old portify-revisit id), `task` (old\n  evaluation-dialog id) and `draft` (old external-authoring id) are tombstoned:\n  `persistView` force-deletes all three on every write regardless of state, so\n  stale deep links don't carry them forward. If your dialog needs its own id,\n  add a new param with the flight-style gate; never reuse `wf`, `task` or `draft`.\n\n## Deliberately NOT done (don't \"fix\" these without a reason)\n\n- **Back-button closes dialog** — would need user-vs-programmatic transition\n  tracking (auto-select-first-feature would spam history). The goal is *revisit*\n  (deep-link + refresh), which `replaceState` delivers. Left out on purpose.\n- **Pretty paths** (`\u002Fcoverage\u002Fcheckout`) — query-param style is intentional; a\n  router buys nothing for a single-screen internal tool.\n\n## Relationship to neighbours\n\n- [[cl_reuse-shared-logic]] — routing has ONE home (`workspace-view-state.ts`);\n  extend it, never add a second URL-writing path or a router lib.\n- [[cl_ui-design-philosophy]] — a new surface's *look* is governed here; its\n  *route* is governed by this skill.\n- [[cl_ws-driven-state]] — that skill keeps UI state live after a server mutation;\n  this skill keeps UI state addressable in the URL. Different axes, both required\n  for a surface to feel native.\n- [[cl_verify-changes]] — `apps\u002Fweb\u002F**` changes need the canary-apply cycle (Tier 3\n  says who runs it) to confirm refresh\u002Fdeep-link behaviour end-to-end; unit tests\n  cover the serialization.\n",{"name":5,"description":7},"main","2c85305cab69e5f773d59ea79a64a0b049a98345",".codex\u002Fskills\u002Fcl_route-every-surface",0,"https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab","https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fcanary-lab","https:\u002F\u002Fgithub.com\u002Fferterahadi","https:\u002F\u002Fgithub.com\u002Fferterahadi-oddle","Shipping well-tested, production-ready, vibe-coded code using today’s latest form of engineering - harness, agentic, loop, graph, or whatever drops next","2026-11-12 23:36:35",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add ferterahadi\u002Fcanary-lab --skill cl_route-every-surface",{"tool":65,"command":66},"bunx","bunx skills add ferterahadi\u002Fcanary-lab --skill cl_route-every-surface",{"tool":68,"command":69},"pnpm","pnpm dlx skills add ferterahadi\u002Fcanary-lab --skill cl_route-every-surface","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab\u002Fblob\u002Fmain\u002F.codex\u002Fskills\u002Fcl_route-every-surface\u002FSKILL.md",true,[],[74,85,95,112,140,160],{"skillNo":75,"name":76,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":83,"publishedAt":44},"01M6XCWFJ75NNVF0N8C0XGNC17","off-by-one","https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab\u002Fblob\u002Fmain\u002Fapps\u002Fweb-server\u002Fprompts\u002Fsabotage-skills\u002Foff-by-one\u002Fskill.md",[79,80],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[84],"off by one",{"skillNo":86,"name":87,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":92,"publishedAt":44},"01M6XCWBYW1QHVFKSDQCCRRRDT","multi-failure-cascade","https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab\u002Fblob\u002Fmain\u002Fapps\u002Fweb-server\u002Fprompts\u002Fsabotage-skills\u002Fmulti-failure-cascade\u002Fskill.md",[90,91],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},[93,94],"multi","multi failure cascade",{"skillNo":96,"name":97,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":98,"categories":99,"keywords":107,"publishedAt":44},"01M6XCW8XA7QY74Q18D9PGFFMD","broken-delete-contract","https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab\u002Fblob\u002Fmain\u002Fapps\u002Fweb-server\u002Fprompts\u002Fsabotage-skills\u002Fbroken-delete-contract\u002Fskill.md",[100,101,102],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":103,"categoryName":104,"taxonomyType":105,"taxonomyTypeDesc":106},"01JBSEEDCAT000000000000037","律师与法务","PROFESSION","适用职业",[108,109,110,111],"delete","broken","contract","broken delete contract",{"skillNo":113,"name":114,"nameZh":6,"description":115,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":120,"publishedAt":44},"01M6XCW439KA486AN2QZACNYTC","canary-lab","Use when the user wants Canary Lab to take a product repo end to end — \"test this app\", \"onboard this repo\", \"run a flight\", \"evaluate what I just built\" — through the flight pipeline (start_flight \u002F get_flight \u002F respond_flight_checkpoint over MCP). One conducted pipeline continues a configured suite or takes bare repo(s) to a green, covered, healed run and publishes a downloadable Report; Parallel setup continues as Canary-owned background work. Use a focused skill only when the user asks for that single capability without asking for a Flight or \u002Fcanary-lab.","https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab\u002Fblob\u002Fmain\u002Fagent-integrations\u002Fplugin\u002Fcanary-lab\u002Fskills\u002Fcanary-lab\u002FSKILL.md",[118,119],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},[121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,28,137,138,139],"lab","canary","canary lab","capability","pipeline","work","only","take","green","get","end","app","respond","suite","product","bare","start","MCP","run",{"skillNo":141,"name":142,"nameZh":6,"description":143,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":144,"categories":145,"keywords":148,"publishedAt":44},"01M6XCVYQ31KC4ZXCD5QMRKXEE","canary-lab-verify","Use when verifying a Canary Lab feature against a RUNNING target — a deployed environment (\"verify checkout on staging\", \"run the saved verify config\", \"check the deployed app\") or a locally booted app (\"verify a running app\") — through the verify MCP tools (list\u002Fget\u002Fcreate\u002Fupdate_verification_config, boot_services, execute_verification, get_verification_result). No heal loop. For run + heal use canary-lab-run; for end-to-end onboarding use canary-lab (flight).","https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab\u002Fblob\u002Fmain\u002Fagent-integrations\u002Fplugin\u002Fcanary-lab\u002Fskills\u002Fcanary-lab-verify\u002FSKILL.md",[146,147],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},[122,149,121,150,123,130,138,151,152,139,153,154,155,156,157,131,132,158,159,28],"verify","canary lab verify","config","create","update","verification","Tools","loop","list","checkout","result",{"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":171,"publishedAt":44},"01M6XCVSWW705CN3C69FQMPZXN","canary-lab-run","Use when running or healing an EXISTING Canary Lab feature locally — \"run \u003Cfeature>\", \"fix the failing run\", \"rerun 7cvh\", \"drive the heal loop\" — through the repair MCP tools (start_run, wait_for_heal_task, signal_run, get_heal_context, pause_run, cancel_heal, abort_run). Fix failing runs by editing app\u002Fservice code, not tests. For end-to-end onboarding of a new repo use canary-lab (flight); for deployed-environment checks use canary-lab-verify.","https:\u002F\u002Fgithub.com\u002Fferterahadi\u002Fcanary-lab\u002Fblob\u002Fmain\u002Fagent-integrations\u002Fplugin\u002Fcanary-lab\u002Fskills\u002Fcanary-lab-run\u002FSKILL.md",[166,167,168],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":169,"categoryName":170,"taxonomyType":105,"taxonomyTypeDesc":106},"01JBSEEDCAT000000000000030","开发工程师",[172,123,139,122,121,131,130,173,174,156,155,137,175,138,28,176,177,132,178,179],"canary lab run","fix","context","code","rerun","wait","new repo","failing"]