[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DC30MJKQCG03SX4D8Z52HC":3,"skill-same:01M6DC30MJKQCG03SX4D8Z52HC":78},{"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":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":58,"commitSha":59,"skillDirectory":60,"forksCount":14,"repositoryHtmlUrl":61,"repositoryHomepageUrl":6,"ownerHtmlUrl":62,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":63,"install":64,"resources":77},"01M6DC30MJKQCG03SX4D8Z52HC","ui-invariant-walk",null,"Use when you want a property-based random-walk oracle that DRIVES a running web\u002FElectron app and asserts UI invariants over a randomized command sequence — \"no enabled button is a dead click\" (an enabled, visible control must produce an observable state change) and \"no route errors\" (no navigation may reach an error route, throw an uncaught exception, or log a console error). Model-based testing via fast-check (`fc.commands` + `fc.asyncModelRun`) driving Playwright, with automatic shrinking to a minimal reproducing click\u002Fnav sequence. Ships as a SHADOW-mode advisory oracle (observe + log to a bounded rotating sink, never blocks CI or nags). Optionally deepen the existing Python Hypothesis suite with HypoFuzz nightly — but ONLY after verifying HypoFuzz's shifted license; the fast-check oracle ships regardless. NOT for pixel\u002Fdesign critique (use ui-audit), NOT for deterministic single-flow E2E (use Playwright directly).","Prekzursil\u002Fagent-skills-toolchain","Prekzursil","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F54636077?v=4","01M6DACZ1PCQHD99SCR1YXG28B","Andrei Mihai Visalon","01M6DACZ670WZF535V3WYYSS4Z",0,"MIT","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fui-invariant-walk\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000032","设计师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"ui invariant walk","ui","command","click","app","route","pixel","observe","log","only","never","testing","model","critique","ci","audit","change","agentic","oracle","design","2026-11-10 10:12:30",false,"---\nname: ui-invariant-walk\ndescription: Use when you want a property-based random-walk oracle that DRIVES a running web\u002FElectron app and asserts UI invariants over a randomized command sequence — \"no enabled button is a dead click\" (an enabled, visible control must produce an observable state change) and \"no route errors\" (no navigation may reach an error route, throw an uncaught exception, or log a console error). Model-based testing via fast-check (`fc.commands` + `fc.asyncModelRun`) driving Playwright, with automatic shrinking to a minimal reproducing click\u002Fnav sequence. Ships as a SHADOW-mode advisory oracle (observe + log to a bounded rotating sink, never blocks CI or nags). Optionally deepen the existing Python Hypothesis suite with HypoFuzz nightly — but ONLY after verifying HypoFuzz's shifted license; the fast-check oracle ships regardless. NOT for pixel\u002Fdesign critique (use ui-audit), NOT for deterministic single-flow E2E (use Playwright directly).\nmetadata:\n  origin: gem-hunt-2026-07-12\n  item: ui-invariant-walk-oracle\n  evidence: MASTER A-Tier2 (agentic-repair-2 #9, agentic-repair-1 #10); COUNCIL-VERDICT C5 shadow-first\n---\n\n# ui-invariant-walk Skill\n\nA **model-based property test** that fans a *randomized* walk of Click \u002F Navigate commands across a\nrunning app and asserts UI **invariants** after every step. Where a hand-written E2E test proves one\nscripted path, this oracle explores thousands of paths and, on failure, **shrinks** to the shortest\nsequence that reproduces the violation.\n\n**Skill location:** `~\u002F.claude\u002Fskills\u002Fui-invariant-walk\u002F`\n\nTwo invariants ship by default:\n\n| ID | Invariant | Fails when |\n|----|-----------|-----------|\n| **INV1 DEAD_CLICK** | *No enabled button is a dead click.* | An **enabled + visible** control is clicked and produces **no observable state change** (no URL change, no DOM mutation, no network request, no dialog). |\n| **INV2 ROUTE_ERROR** | *No route errors.* | After any step the app is on an **error route** (`\u002Ferror`, `\u002F404`, `\u002F500`), logged a **`console.error`**, or threw an **uncaught page exception**. |\n\nBoth are cheap, app-agnostic, and catch a real class of agentic-repair regression: green unit tests +\na UI where a button silently does nothing or a route 500s. (Evidence: gem-hunt MASTER A-Tier2\nagentic-repair-2 #9 \u002F agentic-repair-1 #10 — \"invariant random-walk UI oracle\".)\n\n---\n\n## When to use\n\n- After any UI change to **Reframe** (Electron+React), **momentstudio** (web), **bilbo-web**, WebCoder.\n- As a **nightly \u002F pre-PR** advisory sweep — surface dead controls and broken routes the scripted suite never visits.\n- When a \"tests pass\" claim needs an **external, executable** UI check (see COUNCIL-VERDICT: the done-gate is spoofable; only executable\u002Fexternal evidence counts).\n\n**Not for:** design\u002Fcontrast\u002Fspacing critique → `ui-audit`; one deterministic scripted flow → plain Playwright; Python data-model properties → `python-testing` (+ HypoFuzz below).\n\n---\n\n## Shadow-mode contract (COUNCIL-VERDICT C5 — MANDATORY)\n\nThis oracle is **advisory \u002F observe-only**. It is a *heuristic random explorer*, so it enters the ladder at **SHADOW**, never blocks, never nags:\n\n- **Exit code is always 0** in shadow mode — a violation is **logged, not failed**. CI stays green; the oracle only *reports*.\n- Findings + counterexamples go to a **separate, bounded, auto-rotating** sink: `~\u002F.claude\u002Fshadow-logs\u002Fui-invariant-walk.jsonl` (rotates at 5 MB, keeps 3). **Never** the main event ledger \u002F `terminal.jsonl` (C5(iii): 40 shadow modules would DoS the F1 scorer).\n- Graduation SHADOW→ADVISE→ENFORCE is a **later, human** decision, justified only by a **measured escape\u002Ffalse-positive rate** from the shadow log — not by this skill.\n- Set `WALK_MODE=advise` to print a non-zero-styled summary to stdout (still exit 0); `WALK_MODE=enforce` (opt-in, human-graduated) exits 1 on a confirmed violation.\n\n---\n\n## Install once\n\n```powershell\ncd ~\u002F.claude\u002Fskills\u002Fui-invariant-walk\nnpm init -y\nnpm install fast-check@3 playwright\nnpx playwright install chromium\n```\n\n`fast-check@3` supplies model-based commands (`fc.commands`, `fc.asyncModelRun`) with shrinking;\nPlaywright drives the real page. (Verified locally: `fast-check@3.23.2`, node v24.16.0.)\n\n---\n\n## The oracle — `oracle.mjs`\n\nSave this alongside the skill as `oracle.mjs`. It drives a URL you pass and walks it. Swap the\nselectors block for your app if needed; defaults target generic `\u003Cbutton>` \u002F `[role=button]` \u002F `\u003Ca>`.\n\n```javascript\n\u002F\u002F oracle.mjs — invariant random-walk UI oracle (fast-check + Playwright), SHADOW mode.\nimport fc from 'fast-check';\nimport { chromium } from 'playwright';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst URL       = process.env.WALK_URL   || process.argv[2] || 'http:\u002F\u002Flocalhost:5173';\nconst MODE      = process.env.WALK_MODE  || 'shadow';        \u002F\u002F shadow | advise | enforce\nconst NUM_RUNS  = Number(process.env.WALK_RUNS  || 25);      \u002F\u002F walks\nconst MAX_STEPS = Number(process.env.WALK_STEPS || 20);      \u002F\u002F steps per walk\nconst ERR_ROUTES = (process.env.WALK_ERR_ROUTES || '\u002Ferror,\u002F404,\u002F500').split(',');\n\n\u002F\u002F --- bounded, rotating shadow sink (C5(iii)) ---\nconst SINK_DIR = path.join(os.homedir(), '.claude', 'shadow-logs');\nconst SINK = path.join(SINK_DIR, 'ui-invariant-walk.jsonl');\nfunction logShadow(rec) {\n  try {\n    fs.mkdirSync(SINK_DIR, { recursive: true });\n    if (fs.existsSync(SINK) && fs.statSync(SINK).size > 5 * 1024 * 1024) {\n      fs.rmSync(SINK + '.2', { force: true });\n      if (fs.existsSync(SINK + '.1')) fs.renameSync(SINK + '.1', SINK + '.2');\n      fs.renameSync(SINK, SINK + '.1');\n    }\n    fs.appendFileSync(SINK, JSON.stringify({ ts: new Date().toISOString(), ...rec }) + '\\n');\n  } catch { \u002F* fail-open: shadow logging never breaks the run *\u002F }\n}\n\n\u002F\u002F --- page adapter: read enabled controls + a state snapshot; act; detect errors ---\nasync function adapter(page) {\n  const consoleErrors = [];\n  const pageErrors = [];\n  page.on('console', (m) => { if (m.type() === 'error') consoleErrors.push(m.text()); });\n  page.on('pageerror', (e) => pageErrors.push(String(e)));\n  let netCount = 0;\n  page.on('request', () => { netCount++; });\n\n  const SEL = 'button:not([disabled]), [role=button]:not([aria-disabled=true]), a[href]';\n  return {\n    async enabledControls() {\n      return page.$$eval(SEL, (els) =>\n        els\n          .map((el, i) => ({ i, txt: (el.innerText || el.getAttribute('aria-label') || '').trim().slice(0, 40) }))\n          .filter((_, idx) => {\n            const el = document.querySelectorAll(\n              'button:not([disabled]), [role=button]:not([aria-disabled=true]), a[href]')[idx];\n            const r = el.getBoundingClientRect();\n            const vis = r.width > 0 && r.height > 0 && getComputedStyle(el).visibility !== 'hidden';\n            return vis;\n          }),\n      );\n    },\n    async snapshot() {\n      \u002F\u002F URL + coarse DOM fingerprint (node count + body text length) — cheap, catches real change.\n      const dom = await page.evaluate(() => ({\n        n: document.querySelectorAll('*').length,\n        t: (document.body?.innerText || '').length,\n      }));\n      return `${page.url()}|${dom.n}|${dom.t}|net:${netCount}`;\n    },\n    async clickIndex(idx) {\n      const before = { c: consoleErrors.length, p: pageErrors.length };\n      const els = await page.$$(SEL);\n      if (!els[idx]) return { skipped: true };\n      const dialogSeen = [];\n      const onDialog = (d) => { dialogSeen.push(1); d.dismiss().catch(() => {}); };\n      page.on('dialog', onDialog);\n      await els[idx].click({ timeout: 1500 }).catch(() => {});\n      await page.waitForTimeout(120);\n      page.off('dialog', onDialog);\n      return {\n        newConsole: consoleErrors.length - before.c,\n        newPage: pageErrors.length - before.p,\n        dialog: dialogSeen.length,\n      };\n    },\n    routeIsError() { return ERR_ROUTES.some((r) => page.url().includes(r)); },\n    errs() { return { console: consoleErrors.slice(-3), page: pageErrors.slice(-3) }; },\n  };\n}\n\n\u002F\u002F --- fast-check model-based commands ---\nclass ClickCmd {\n  constructor(pick) { this.pick = pick; }\n  check() { return true; }\n  async run(model, real) {\n    const ctrls = await real.enabledControls();\n    if (ctrls.length === 0) return;\n    const idx = ctrls[this.pick % ctrls.length].i;\n    const before = await real.snapshot();\n    const res = await real.clickIndex(idx);\n    if (res.skipped) return;\n    const after = await real.snapshot();\n    \u002F\u002F INV2: route\u002Fconsole\u002Fpage errors.\n    if (real.routeIsError() || res.newPage > 0 || res.newConsole > 0) {\n      throw Object.assign(new Error('ROUTE_ERROR'), { inv: 'INV2', ctrl: idx, errs: real.errs() });\n    }\n    \u002F\u002F INV1: enabled control must change something (url\u002Fdom\u002Fnet\u002Fdialog).\n    const changed = before !== after || res.dialog > 0;\n    if (!changed) {\n      throw Object.assign(new Error('DEAD_CLICK'), { inv: 'INV1', ctrl: idx, at: before });\n    }\n  }\n  toString() { return `Click(${this.pick})`; }\n}\nclass NavBackCmd {\n  check() { return true; }\n  async run(model, real) { \u002F* placeholder for history walk; kept minimal *\u002F }\n  toString() { return 'Back'; }\n}\n\nconst commands = [\n  fc.nat({ max: 12 }).map((p) => new ClickCmd(p)),\n  fc.constant(new NavBackCmd()),\n];\n\nasync function main() {\n  const browser = await chromium.launch();\n  let failed = null;\n  try {\n    await fc.assert(\n      fc.asyncProperty(fc.commands(commands, { maxCommands: MAX_STEPS }), async (cmds) => {\n        const page = await browser.newPage();\n        await page.goto(URL, { waitUntil: 'domcontentloaded' });\n        const real = await adapter(page);\n        try {\n          await fc.asyncModelRun(() => ({ model: {}, real }), cmds);\n        } finally {\n          await page.close();\n        }\n      }),\n      { numRuns: NUM_RUNS, endOnFailure: true },\n    );\n  } catch (e) {\n    failed = e;\n  } finally {\n    await browser.close();\n  }\n\n  if (!failed) {\n    console.log(`ui-invariant-walk: PASS (${NUM_RUNS} walks, no invariant violation) [mode=${MODE}]`);\n    logShadow({ result: 'pass', url: URL, runs: NUM_RUNS });\n    process.exit(0);\n  }\n\n  \u002F\u002F fast-check throws with .counterexample = the shrunk minimal sequence.\n  const cx = failed.counterexample?.[0];\n  const seq = cx ? Array.from(cx, String) : [];\n  const rec = { result: 'violation', url: URL, sequence: seq, message: String(failed).split('\\n')[0] };\n  logShadow(rec);\n  console.log(`ui-invariant-walk: INVARIANT VIOLATION [mode=${MODE}]`);\n  console.log('  minimal sequence:', JSON.stringify(seq));\n  console.log('  detail:', rec.message);\n  console.log(`  logged to ~\u002F.claude\u002Fshadow-logs\u002Fui-invariant-walk.jsonl`);\n  \u002F\u002F C5: shadow\u002Fadvise NEVER fail the build; only graduated enforce mode exits non-zero.\n  process.exit(MODE === 'enforce' ? 1 : 0);\n}\n\nmain();\n```\n\n### Run it\n\n```powershell\n# Start your app first (e.g. Reframe dev server \u002F momentstudio), then:\n$env:WALK_URL=\"http:\u002F\u002Flocalhost:5173\"; node oracle.mjs         # shadow (default, always exit 0)\n$env:WALK_MODE=\"advise\"; node oracle.mjs                        # prints summary, still exit 0\n$env:WALK_RUNS=\"50\"; $env:WALK_STEPS=\"30\"; node oracle.mjs      # deeper walk\n```\n\nTerminal-state output: `ui-invariant-walk: PASS …` or `… INVARIANT VIOLATION …` with the **shrunk**\nclick\u002Fnav sequence you can replay by hand.\n\n---\n\n## Proven mechanism (self-test)\n\nThe model-run + invariant + shrink logic was verified for real against a mock app (`walk.mjs`,\nno browser needed) — a GOOD build passes, a BUGGY build (a `ghost` dead button + an `\u002Ferr` route) is\ncaught and shrunk:\n\n```\nGOOD build  -> failed=false (expected pass)\nBUGGY build -> failed=true (expected catch)\n  counterexample: [\"Navigate(#2)\", ... ,\"Click(#5)\"]\n  error: Error: ROUTE_ERROR invariant violated after navigate(\"\u002Ferr\")\nORACLE-SELFTEST: PASS   (exit 0)\n```\n\nShip a copy of `walk.mjs` (the mock self-test) in the skill folder as a CI smoke check that the\noracle plumbing itself works without needing a live app. The mock is the two-build fixture used above.\n\n---\n\n## Optional: HypoFuzz nightly (Python) — LICENSE-GATED, do NOT wire blindly\n\nThe gem pairs this oracle with **coverage-guided deepening of the existing Python Hypothesis suite via\nHypoFuzz** (feeds the same fuzz-the-invariants idea into `@given` properties overnight).\n\n> **CAVEAT (MASTER A-Tier2):** *HypoFuzz's license shifted — VERIFY before wiring.* Historically AGPL,\n> it has moved toward a commercial\u002Fsource-available model. Do **not** add it to any repo or CI until\n> you have confirmed the *current* license permits your use.\n\n**Gate before adopting HypoFuzz:**\n\n1. `pip index versions hypofuzz` then read the live `LICENSE` on the exact version's PyPI\u002FGitHub tag.\n2. Confirm it is compatible with the target repo (AGPL\u002Fcommercial obligations, redistribution, CI use).\n3. Only if clean: `pip install hypofuzz`; run `hypothesis fuzz` over the existing property suite on a\n   **nightly** schedule (never the blocking PR gate), routing findings to the same shadow sink.\n4. If the license is **not** clean → **skip HypoFuzz entirely.** The fast-check oracle above already\n   delivers the invariant-walk value with zero license risk (`fast-check` is MIT).\n\nThe fast-check oracle is the deliverable; HypoFuzz is a strictly-optional, license-contingent add-on.\n\n---\n\n## Anti-overfit \u002F charter notes\n\n- Rides the existing test bus (fast-check + Playwright, both already used by `ui-audit`); no trained\n  critic, no vector DB, no second runtime, no new guardrail framework.\n- Ships **shadow-first** with a bounded rotating sink and a hard \"exit 0 unless human-graduated to\n  enforce\" rule (COUNCIL-VERDICT C5).\n- Invariants are **executable\u002Fexternal** evidence, exactly the kind the council said must exist before\n  anything downstream can lean on \"the verifier catches it\".\n","# ui-invariant-walk Skill\n\nA **model-based property test** that fans a *randomized* walk of Click \u002F Navigate commands across a\nrunning app and asserts UI **invariants** after every step. Where a hand-written E2E test proves one\nscripted path, this oracle explores thousands of paths and, on failure, **shrinks** to the shortest\nsequence that reproduces the violation.\n\n**Skill location:** `~\u002F.claude\u002Fskills\u002Fui-invariant-walk\u002F`\n\nTwo invariants ship by default:\n\n| ID | Invariant | Fails when |\n|----|-----------|-----------|\n| **INV1 DEAD_CLICK** | *No enabled button is a dead click.* | An **enabled + visible** control is clicked and produces **no observable state change** (no URL change, no DOM mutation, no network request, no dialog). |\n| **INV2 ROUTE_ERROR** | *No route errors.* | After any step the app is on an **error route** (`\u002Ferror`, `\u002F404`, `\u002F500`), logged a **`console.error`**, or threw an **uncaught page exception**. |\n\nBoth are cheap, app-agnostic, and catch a real class of agentic-repair regression: green unit tests +\na UI where a button silently does nothing or a route 500s. (Evidence: gem-hunt MASTER A-Tier2\nagentic-repair-2 #9 \u002F agentic-repair-1 #10 — \"invariant random-walk UI oracle\".)\n\n---\n\n## When to use\n\n- After any UI change to **Reframe** (Electron+React), **momentstudio** (web), **bilbo-web**, WebCoder.\n- As a **nightly \u002F pre-PR** advisory sweep — surface dead controls and broken routes the scripted suite never visits.\n- When a \"tests pass\" claim needs an **external, executable** UI check (see COUNCIL-VERDICT: the done-gate is spoofable; only executable\u002Fexternal evidence counts).\n\n**Not for:** design\u002Fcontrast\u002Fspacing critique → `ui-audit`; one deterministic scripted flow → plain Playwright; Python data-model properties → `python-testing` (+ HypoFuzz below).\n\n---\n\n## Shadow-mode contract (COUNCIL-VERDICT C5 — MANDATORY)\n\nThis oracle is **advisory \u002F observe-only**. It is a *heuristic random explorer*, so it enters the ladder at **SHADOW**, never blocks, never nags:\n\n- **Exit code is always 0** in shadow mode — a violation is **logged, not failed**. CI stays green; the oracle only *reports*.\n- Findings + counterexamples go to a **separate, bounded, auto-rotating** sink: `~\u002F.claude\u002Fshadow-logs\u002Fui-invariant-walk.jsonl` (rotates at 5 MB, keeps 3). **Never** the main event ledger \u002F `terminal.jsonl` (C5(iii): 40 shadow modules would DoS the F1 scorer).\n- Graduation SHADOW→ADVISE→ENFORCE is a **later, human** decision, justified only by a **measured escape\u002Ffalse-positive rate** from the shadow log — not by this skill.\n- Set `WALK_MODE=advise` to print a non-zero-styled summary to stdout (still exit 0); `WALK_MODE=enforce` (opt-in, human-graduated) exits 1 on a confirmed violation.\n\n---\n\n## Install once\n\n```powershell\ncd ~\u002F.claude\u002Fskills\u002Fui-invariant-walk\nnpm init -y\nnpm install fast-check@3 playwright\nnpx playwright install chromium\n```\n\n`fast-check@3` supplies model-based commands (`fc.commands`, `fc.asyncModelRun`) with shrinking;\nPlaywright drives the real page. (Verified locally: `fast-check@3.23.2`, node v24.16.0.)\n\n---\n\n## The oracle — `oracle.mjs`\n\nSave this alongside the skill as `oracle.mjs`. It drives a URL you pass and walks it. Swap the\nselectors block for your app if needed; defaults target generic `\u003Cbutton>` \u002F `[role=button]` \u002F `\u003Ca>`.\n\n```javascript\n\u002F\u002F oracle.mjs — invariant random-walk UI oracle (fast-check + Playwright), SHADOW mode.\nimport fc from 'fast-check';\nimport { chromium } from 'playwright';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst URL       = process.env.WALK_URL   || process.argv[2] || 'http:\u002F\u002Flocalhost:5173';\nconst MODE      = process.env.WALK_MODE  || 'shadow';        \u002F\u002F shadow | advise | enforce\nconst NUM_RUNS  = Number(process.env.WALK_RUNS  || 25);      \u002F\u002F walks\nconst MAX_STEPS = Number(process.env.WALK_STEPS || 20);      \u002F\u002F steps per walk\nconst ERR_ROUTES = (process.env.WALK_ERR_ROUTES || '\u002Ferror,\u002F404,\u002F500').split(',');\n\n\u002F\u002F --- bounded, rotating shadow sink (C5(iii)) ---\nconst SINK_DIR = path.join(os.homedir(), '.claude', 'shadow-logs');\nconst SINK = path.join(SINK_DIR, 'ui-invariant-walk.jsonl');\nfunction logShadow(rec) {\n  try {\n    fs.mkdirSync(SINK_DIR, { recursive: true });\n    if (fs.existsSync(SINK) && fs.statSync(SINK).size > 5 * 1024 * 1024) {\n      fs.rmSync(SINK + '.2', { force: true });\n      if (fs.existsSync(SINK + '.1')) fs.renameSync(SINK + '.1', SINK + '.2');\n      fs.renameSync(SINK, SINK + '.1');\n    }\n    fs.appendFileSync(SINK, JSON.stringify({ ts: new Date().toISOString(), ...rec }) + '\\n');\n  } catch { \u002F* fail-open: shadow logging never breaks the run *\u002F }\n}\n\n\u002F\u002F --- page adapter: read enabled controls + a state snapshot; act; detect errors ---\nasync function adapter(page) {\n  const consoleErrors = [];\n  const pageErrors = [];\n  page.on('console', (m) => { if (m.type() === 'error') consoleErrors.push(m.text()); });\n  page.on('pageerror', (e) => pageErrors.push(String(e)));\n  let netCount = 0;\n  page.on('request', () => { netCount++; });\n\n  const SEL = 'button:not([disabled]), [role=button]:not([aria-disabled=true]), a[href]';\n  return {\n    async enabledControls() {\n      return page.$$eval(SEL, (els) =>\n        els\n          .map((el, i) => ({ i, txt: (el.innerText || el.getAttribute('aria-label') || '').trim().slice(0, 40) }))\n          .filter((_, idx) => {\n            const el = document.querySelectorAll(\n              'button:not([disabled]), [role=button]:not([aria-disabled=true]), a[href]')[idx];\n            const r = el.getBoundingClientRect();\n            const vis = r.width > 0 && r.height > 0 && getComputedStyle(el).visibility !== 'hidden';\n            return vis;\n          }),\n      );\n    },\n    async snapshot() {\n      \u002F\u002F URL + coarse DOM fingerprint (node count + body text length) — cheap, catches real change.\n      const dom = await page.evaluate(() => ({\n        n: document.querySelectorAll('*').length,\n        t: (document.body?.innerText || '').length,\n      }));\n      return `${page.url()}|${dom.n}|${dom.t}|net:${netCount}`;\n    },\n    async clickIndex(idx) {\n      const before = { c: consoleErrors.length, p: pageErrors.length };\n      const els = await page.$$(SEL);\n      if (!els[idx]) return { skipped: true };\n      const dialogSeen = [];\n      const onDialog = (d) => { dialogSeen.push(1); d.dismiss().catch(() => {}); };\n      page.on('dialog', onDialog);\n      await els[idx].click({ timeout: 1500 }).catch(() => {});\n      await page.waitForTimeout(120);\n      page.off('dialog', onDialog);\n      return {\n        newConsole: consoleErrors.length - before.c,\n        newPage: pageErrors.length - before.p,\n        dialog: dialogSeen.length,\n      };\n    },\n    routeIsError() { return ERR_ROUTES.some((r) => page.url().includes(r)); },\n    errs() { return { console: consoleErrors.slice(-3), page: pageErrors.slice(-3) }; },\n  };\n}\n\n\u002F\u002F --- fast-check model-based commands ---\nclass ClickCmd {\n  constructor(pick) { this.pick = pick; }\n  check() { return true; }\n  async run(model, real) {\n    const ctrls = await real.enabledControls();\n    if (ctrls.length === 0) return;\n    const idx = ctrls[this.pick % ctrls.length].i;\n    const before = await real.snapshot();\n    const res = await real.clickIndex(idx);\n    if (res.skipped) return;\n    const after = await real.snapshot();\n    \u002F\u002F INV2: route\u002Fconsole\u002Fpage errors.\n    if (real.routeIsError() || res.newPage > 0 || res.newConsole > 0) {\n      throw Object.assign(new Error('ROUTE_ERROR'), { inv: 'INV2', ctrl: idx, errs: real.errs() });\n    }\n    \u002F\u002F INV1: enabled control must change something (url\u002Fdom\u002Fnet\u002Fdialog).\n    const changed = before !== after || res.dialog > 0;\n    if (!changed) {\n      throw Object.assign(new Error('DEAD_CLICK'), { inv: 'INV1', ctrl: idx, at: before });\n    }\n  }\n  toString() { return `Click(${this.pick})`; }\n}\nclass NavBackCmd {\n  check() { return true; }\n  async run(model, real) { \u002F* placeholder for history walk; kept minimal *\u002F }\n  toString() { return 'Back'; }\n}\n\nconst commands = [\n  fc.nat({ max: 12 }).map((p) => new ClickCmd(p)),\n  fc.constant(new NavBackCmd()),\n];\n\nasync function main() {\n  const browser = await chromium.launch();\n  let failed = null;\n  try {\n    await fc.assert(\n      fc.asyncProperty(fc.commands(commands, { maxCommands: MAX_STEPS }), async (cmds) => {\n        const page = await browser.newPage();\n        await page.goto(URL, { waitUntil: 'domcontentloaded' });\n        const real = await adapter(page);\n        try {\n          await fc.asyncModelRun(() => ({ model: {}, real }), cmds);\n        } finally {\n          await page.close();\n        }\n      }),\n      { numRuns: NUM_RUNS, endOnFailure: true },\n    );\n  } catch (e) {\n    failed = e;\n  } finally {\n    await browser.close();\n  }\n\n  if (!failed) {\n    console.log(`ui-invariant-walk: PASS (${NUM_RUNS} walks, no invariant violation) [mode=${MODE}]`);\n    logShadow({ result: 'pass', url: URL, runs: NUM_RUNS });\n    process.exit(0);\n  }\n\n  \u002F\u002F fast-check throws with .counterexample = the shrunk minimal sequence.\n  const cx = failed.counterexample?.[0];\n  const seq = cx ? Array.from(cx, String) : [];\n  const rec = { result: 'violation', url: URL, sequence: seq, message: String(failed).split('\\n')[0] };\n  logShadow(rec);\n  console.log(`ui-invariant-walk: INVARIANT VIOLATION [mode=${MODE}]`);\n  console.log('  minimal sequence:', JSON.stringify(seq));\n  console.log('  detail:', rec.message);\n  console.log(`  logged to ~\u002F.claude\u002Fshadow-logs\u002Fui-invariant-walk.jsonl`);\n  \u002F\u002F C5: shadow\u002Fadvise NEVER fail the build; only graduated enforce mode exits non-zero.\n  process.exit(MODE === 'enforce' ? 1 : 0);\n}\n\nmain();\n```\n\n### Run it\n\n```powershell\n# Start your app first (e.g. Reframe dev server \u002F momentstudio), then:\n$env:WALK_URL=\"http:\u002F\u002Flocalhost:5173\"; node oracle.mjs         # shadow (default, always exit 0)\n$env:WALK_MODE=\"advise\"; node oracle.mjs                        # prints summary, still exit 0\n$env:WALK_RUNS=\"50\"; $env:WALK_STEPS=\"30\"; node oracle.mjs      # deeper walk\n```\n\nTerminal-state output: `ui-invariant-walk: PASS …` or `… INVARIANT VIOLATION …` with the **shrunk**\nclick\u002Fnav sequence you can replay by hand.\n\n---\n\n## Proven mechanism (self-test)\n\nThe model-run + invariant + shrink logic was verified for real against a mock app (`walk.mjs`,\nno browser needed) — a GOOD build passes, a BUGGY build (a `ghost` dead button + an `\u002Ferr` route) is\ncaught and shrunk:\n\n```\nGOOD build  -> failed=false (expected pass)\nBUGGY build -> failed=true (expected catch)\n  counterexample: [\"Navigate(#2)\", ... ,\"Click(#5)\"]\n  error: Error: ROUTE_ERROR invariant violated after navigate(\"\u002Ferr\")\nORACLE-SELFTEST: PASS   (exit 0)\n```\n\nShip a copy of `walk.mjs` (the mock self-test) in the skill folder as a CI smoke check that the\noracle plumbing itself works without needing a live app. The mock is the two-build fixture used above.\n\n---\n\n## Optional: HypoFuzz nightly (Python) — LICENSE-GATED, do NOT wire blindly\n\nThe gem pairs this oracle with **coverage-guided deepening of the existing Python Hypothesis suite via\nHypoFuzz** (feeds the same fuzz-the-invariants idea into `@given` properties overnight).\n\n> **CAVEAT (MASTER A-Tier2):** *HypoFuzz's license shifted — VERIFY before wiring.* Historically AGPL,\n> it has moved toward a commercial\u002Fsource-available model. Do **not** add it to any repo or CI until\n> you have confirmed the *current* license permits your use.\n\n**Gate before adopting HypoFuzz:**\n\n1. `pip index versions hypofuzz` then read the live `LICENSE` on the exact version's PyPI\u002FGitHub tag.\n2. Confirm it is compatible with the target repo (AGPL\u002Fcommercial obligations, redistribution, CI use).\n3. Only if clean: `pip install hypofuzz`; run `hypothesis fuzz` over the existing property suite on a\n   **nightly** schedule (never the blocking PR gate), routing findings to the same shadow sink.\n4. If the license is **not** clean → **skip HypoFuzz entirely.** The fast-check oracle above already\n   delivers the invariant-walk value with zero license risk (`fast-check` is MIT).\n\nThe fast-check oracle is the deliverable; HypoFuzz is a strictly-optional, license-contingent add-on.\n\n---\n\n## Anti-overfit \u002F charter notes\n\n- Rides the existing test bus (fast-check + Playwright, both already used by `ui-audit`); no trained\n  critic, no vector DB, no second runtime, no new guardrail framework.\n- Ships **shadow-first** with a bounded rotating sink and a hard \"exit 0 unless human-graduated to\n  enforce\" rule (COUNCIL-VERDICT C5).\n- Invariants are **executable\u002Fexternal** evidence, exactly the kind the council said must exist before\n  anything downstream can lean on \"the verifier catches it\".\n",{"name":5,"metadata":54,"description":7},{"item":55,"origin":56,"evidence":57},"ui-invariant-walk-oracle","gem-hunt-2026-07-12","MASTER A-Tier2 (agentic-repair-2","main","a298c929b6cbd662e1fce54302ce520d5ceea1a5",".agents\u002Fskills\u002Fui-invariant-walk","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain","https:\u002F\u002Fgithub.com\u002FPrekzursil","2026-11-02 14:30:57",{"source":8,"skillName":5,"commands":65,"prompt":75,"zipAvailable":76},[66,69,72],{"tool":67,"command":68},"npx","npx skills add Prekzursil\u002Fagent-skills-toolchain --skill ui-invariant-walk",{"tool":70,"command":71},"bunx","bunx skills add Prekzursil\u002Fagent-skills-toolchain --skill ui-invariant-walk",{"tool":73,"command":74},"pnpm","pnpm dlx skills add Prekzursil\u002Fagent-skills-toolchain --skill ui-invariant-walk","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fui-invariant-walk\u002FSKILL.md",true,[],[79,101,130,154,168,184],{"skillNo":80,"name":81,"nameZh":6,"description":82,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":85,"publishedAt":49},"01M6DF7D7478R55P46REYH4QAC","clarify-requirements","Resolve genuine pre-execution ambiguity by scanning repo context, generating 1-6 grounded options, and asking 1-3 focused questions via AskUserQuestion. NOT for mid-task use (injection risk); NOT when the goal is already unambiguous; use to disambiguate vague prompts before any file edits.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fworkspace-hooks\u002Fskills\u002Fclarify-requirements\u002FSKILL.md",[],[86,87,88,89,90,91,92,93,94,95,96,97,98,99,100],"clarify requirements","clarify","requirements","risk","options","task","prompts","goal","scanning","use","context","repo","injection","execution","file",{"skillNo":102,"name":103,"nameZh":6,"description":104,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":105,"categories":106,"keywords":110,"publishedAt":49},"01M6DF79CA4BHJS66J8EZDBPWJ","openmm-md","Run FREE, open-source, Windows-native GPU molecular dynamics via the openmm-md MCP (OpenMM + CUDA on the local NVIDIA GPU). Use for protein \u002F protein-ligand MD when you want a free stack or are on Windows without a Schrodinger license. Covers the full workflow (prepare, solvate, minimize, equilibrate, production, analyze, mmgbsa) AND an honest decision guide vs Schrodinger Desmond, including how to run Desmond legitimately on Linux or a cloud GPU with a real license. This is a deliberately LESSER free alternative and does NOT replace the schrodinger MCP tools. Do NOT use it to pick a free tool when a validated FEP+, WaterMap, IFD-MD, or Prime MM-GBSA result is actually required.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fmcp\u002Fopenmm-md-mcp\u002Fskill\u002Fopenmm-md\u002FSKILL.md",[107],{"categoryNo":108,"categoryName":109,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[111,112,113,114,115,116,117,118,119,120,121,122,123,124,95,125,126,127,128,129],"openmm md","how","decision","pick","linux","analyze","tool","stack","open","NVIDIA","gpu","license","workflow","windows","free","molecular dynamics","local","do","MCP",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":140,"publishedAt":49},"01M6DF76C1MG85VEWP8VZCBNMN","visual-review","Take screenshots of web pages and UI using Playwright for visual review and iteration","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fvisual-review\u002FSKILL.md",[136,139],{"categoryNo":137,"categoryName":138,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[141,142,143,144,145,146,147,148,149,150,112,30,151,152,153],"review","visual","visual review","pages","web","false","iteration","take","check","slides","screenshot","playwright","screenshots",{"skillNo":155,"name":156,"nameZh":6,"description":157,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":158,"categories":159,"keywords":160,"publishedAt":49},"01M6DF73AQVCBZKEN0YXEX9FXP","status","Diagnostic status report — shows metaswarm installation state, project setup, and potential issues","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstatus\u002FSKILL.md",[],[156,161,162,163,164,165,166,167],"setup","report","project","installation","project setup","issues","state",{"skillNo":169,"name":170,"nameZh":6,"description":171,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":172,"categories":173,"keywords":175,"publishedAt":49},"01M6DF6X6C2XB0P6GX76P7TTYV","start","Use when starting work on any task, when the user mentions metaswarm, or when the user wants to begin tracked development work","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstart\u002FSKILL.md",[174],{"categoryNo":108,"categoryName":109,"taxonomyType":21,"taxonomyTypeDesc":22},[170,176,91,177,178,179,95,180,181,182,183],"start issue","issue","ready","work on issue","user","work","work on","development",{"skillNo":185,"name":161,"nameZh":6,"description":186,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":187,"categories":188,"keywords":189,"publishedAt":49},"01M6DF6QMJ9AJKFKHHKRT005PQ","Interactive project setup — detects your project, configures metaswarm, writes project-local files","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fsetup\u002FSKILL.md",[],[161,163,127,190,165,191],"files","interactive"]