From b161bd9d66d595c6c59c22f3474aad2a7f792248 Mon Sep 17 00:00:00 2001 From: Suleiman Shahbari Date: Sun, 5 Jul 2026 20:00:01 +0300 Subject: [PATCH] feat(ai-autopilot): add web-development and data-science domain presets (#263) --- .../open-loop-web-and-data-science-presets.md | 14 ++++ .../presets/data-science/loops/bug-fix.md | 11 +++ .../data-science/loops/major-change.md | 11 +++ .../loops/major-change.technical.md | 13 +++ .../presets/data-science/preset.md | 11 +++ .../data-science/prompts/data-root-cause.md | 22 ++++++ .../data-science/prompts/data-validation.md | 24 ++++++ .../prompts/methodology-review.md | 24 ++++++ .../data-science/prompts/regression-test.md | 21 +++++ .../prompts/reproducibility-review.md | 24 ++++++ .../data-science/skills/rules-of-ml.md | 12 +++ .../presets/web-development/loops/bug-fix.md | 11 +++ .../web-development/loops/major-change.md | 11 +++ .../loops/major-change.technical.md | 13 +++ .../presets/web-development/preset.md | 11 +++ .../prompts/accessibility-review.md | 24 ++++++ .../prompts/performance-budget.md | 24 ++++++ .../prompts/regression-test.md | 22 ++++++ .../web-development/prompts/ui-root-cause.md | 22 ++++++ .../web-development/prompts/web-security.md | 25 ++++++ .../web-development/skills/web-platform.md | 12 +++ .../src/preset/builtin-presets.test.ts | 79 +++++++++++++++++++ 22 files changed, 441 insertions(+) create mode 100644 .changeset/open-loop-web-and-data-science-presets.md create mode 100644 packages/ai-autopilot/presets/data-science/loops/bug-fix.md create mode 100644 packages/ai-autopilot/presets/data-science/loops/major-change.md create mode 100644 packages/ai-autopilot/presets/data-science/loops/major-change.technical.md create mode 100644 packages/ai-autopilot/presets/data-science/preset.md create mode 100644 packages/ai-autopilot/presets/data-science/prompts/data-root-cause.md create mode 100644 packages/ai-autopilot/presets/data-science/prompts/data-validation.md create mode 100644 packages/ai-autopilot/presets/data-science/prompts/methodology-review.md create mode 100644 packages/ai-autopilot/presets/data-science/prompts/regression-test.md create mode 100644 packages/ai-autopilot/presets/data-science/prompts/reproducibility-review.md create mode 100644 packages/ai-autopilot/presets/data-science/skills/rules-of-ml.md create mode 100644 packages/ai-autopilot/presets/web-development/loops/bug-fix.md create mode 100644 packages/ai-autopilot/presets/web-development/loops/major-change.md create mode 100644 packages/ai-autopilot/presets/web-development/loops/major-change.technical.md create mode 100644 packages/ai-autopilot/presets/web-development/preset.md create mode 100644 packages/ai-autopilot/presets/web-development/prompts/accessibility-review.md create mode 100644 packages/ai-autopilot/presets/web-development/prompts/performance-budget.md create mode 100644 packages/ai-autopilot/presets/web-development/prompts/regression-test.md create mode 100644 packages/ai-autopilot/presets/web-development/prompts/ui-root-cause.md create mode 100644 packages/ai-autopilot/presets/web-development/prompts/web-security.md create mode 100644 packages/ai-autopilot/presets/web-development/skills/web-platform.md create mode 100644 packages/ai-autopilot/src/preset/builtin-presets.test.ts diff --git a/.changeset/open-loop-web-and-data-science-presets.md b/.changeset/open-loop-web-and-data-science-presets.md new file mode 100644 index 0000000..4912965 --- /dev/null +++ b/.changeset/open-loop-web-and-data-science-presets.md @@ -0,0 +1,14 @@ +--- +'@gemstack/ai-autopilot': minor +--- + +Add two more built-in domain presets: `web-development` and `data-science`. + +Each ships as a directory of `.md` files like `software-development`, so it is +auto-discovered by `builtinDomainPresets()`, selectable via `--preset ` and +`the-framework.yml`, and drives the review phase. Both carry a Technical Control +variant (leaner major-change loop) and a bug-fix loop. Their major-change review +prompts end with the `{ blockers }` verdict footer so the review loop gates. + +- **web-development** — accessibility, performance budget, and web-security review; skill points at web.dev. +- **data-science** — reproducibility, data validation, and methodology review; skill points at Google's Rules of ML. diff --git a/packages/ai-autopilot/presets/data-science/loops/bug-fix.md b/packages/ai-autopilot/presets/data-science/loops/bug-fix.md new file mode 100644 index 0000000..06ceaa0 --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/loops/bug-fix.md @@ -0,0 +1,11 @@ +--- +name: bug-fix-loop +description: What fires after fixing a wrong result or a pipeline defect. +metadata: + on: bug-fix + run: [data-root-cause, regression-test] +--- + +When the agent fixes a wrong number or a broken pipeline step, confirm the real +cause (a leak, a bad join, a wrong dtype — not just the output that looked off), +then lock it in with a test on a known input and its expected output. diff --git a/packages/ai-autopilot/presets/data-science/loops/major-change.md b/packages/ai-autopilot/presets/data-science/loops/major-change.md new file mode 100644 index 0000000..4feb3f2 --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/loops/major-change.md @@ -0,0 +1,11 @@ +--- +name: major-change-loop +description: What fires after a substantial change to a pipeline, model, or analysis. +metadata: + on: major-change + run: [reproducibility-review, data-validation, methodology-review] +--- + +When the agent lands a substantial data change, check someone else could reproduce +it, that the data feeding it is valid, then that the method actually supports the +conclusion — in that order. diff --git a/packages/ai-autopilot/presets/data-science/loops/major-change.technical.md b/packages/ai-autopilot/presets/data-science/loops/major-change.technical.md new file mode 100644 index 0000000..0dfe7f1 --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/loops/major-change.technical.md @@ -0,0 +1,13 @@ +--- +name: major-change-loop-technical +description: The major-change loop under Technical Control — leaner, the analyst drives the depth. +metadata: + on: major-change + run: [reproducibility-review] + conditions: technical +--- + +Technical Control mode: the analyst is hands-on, so the loop only auto-runs the +reproducibility pass — the one that protects everyone downstream — and leaves data +and methodology depth to them. Overrides the base major-change loop when +`technical` is active. diff --git a/packages/ai-autopilot/presets/data-science/preset.md b/packages/ai-autopilot/presets/data-science/preset.md new file mode 100644 index 0000000..1a5290e --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/preset.md @@ -0,0 +1,11 @@ +--- +name: data-science +description: A data-science loop — reproducibility, data validation, and methodology review on every substantial change to a pipeline, model, or analysis. +metadata: + title: Data Science +--- + +Working with data and models. This preset adds the disciplines that generic code +review misses on an analysis or training pipeline: can someone reproduce it, is the +data actually valid, and does the method support the claim. Pick it for data +pipelines, notebooks, model training, and analyses — in any language. diff --git a/packages/ai-autopilot/presets/data-science/prompts/data-root-cause.md b/packages/ai-autopilot/presets/data-science/prompts/data-root-cause.md new file mode 100644 index 0000000..ce87aef --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/prompts/data-root-cause.md @@ -0,0 +1,22 @@ +--- +name: data-root-cause +description: Confirm a data fix addresses the real cause, not the wrong number. +appliesTo: ["**/*"] +metadata: + title: Data root cause + loopId: data-root-cause + passes: 1 + event: bug-fix +--- + +You are checking that a fix to a wrong result or a broken pipeline step addresses +the **real cause**, not the output that looked off. + +Work backwards from the fix: +- What was the actual defect — a bad join multiplying rows, a wrong dtype or unit, a silent NaN, an off-by-one window, a leak — and why did it produce the observed number? +- Does the fix remove that cause, or does it patch one output (a hard-coded correction, a `dropna` that hides the real gap, a clamp)? +- Could the same cause corrupt other columns, other date ranges, or a downstream table drawing from the same step? + +State the root cause in one sentence. If the fix only corrects the visible number, +say what the real fix is. If it removes the cause, confirm it and note any sibling +outputs worth rechecking. diff --git a/packages/ai-autopilot/presets/data-science/prompts/data-validation.md b/packages/ai-autopilot/presets/data-science/prompts/data-validation.md new file mode 100644 index 0000000..8edb209 --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/prompts/data-validation.md @@ -0,0 +1,24 @@ +--- +name: data-validation +description: Check the data feeding the change is valid and not leaking. +appliesTo: ["**/*"] +metadata: + title: Data validation + loopId: data-validation + passes: 1 + event: major-change +--- + +You are checking that the data feeding a change is valid — bad data quietly +produces confident wrong answers. Scope it to the inputs and transforms the change +touched. + +Look for: +- **Leakage** — target or future information reaching the features, or the test set influencing training, imputation, or feature scaling fit on the full data. +- **Split integrity** — train / validation / test are separated correctly, and grouped or time-series data does not leak across the boundary. +- **Schema and types** — expected columns, dtypes, and units, with missing values and outliers handled deliberately rather than silently coerced. +- **Distribution** — the sample actually represents the population the claim is about; class imbalance and selection bias are acknowledged. + +Name each concrete data risk and the fix. If the inputs are sound, say so and stop. + +End your reply with a fenced ```json block: `{ "blockers": ["", ...] }`. List only what must be fixed before this is production-grade; an empty array means nothing blocks. diff --git a/packages/ai-autopilot/presets/data-science/prompts/methodology-review.md b/packages/ai-autopilot/presets/data-science/prompts/methodology-review.md new file mode 100644 index 0000000..db023ea --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/prompts/methodology-review.md @@ -0,0 +1,24 @@ +--- +name: methodology-review +description: Check the method actually supports the conclusion. +appliesTo: ["**/*"] +metadata: + title: Methodology review + loopId: methodology-review + passes: 1 + event: major-change +--- + +You are checking that the method behind a change supports the conclusion it claims. +A correct implementation of the wrong method is still wrong. + +Ask: +- **Metric fit** — does the evaluation metric match the goal (accuracy on imbalanced data, a threshold chosen after seeing the test set, a proxy standing in for the real outcome)? +- **Baseline** — is the result compared against a simple baseline, or reported in a vacuum where any number looks good? +- **Overfitting** — is the reported performance on held-out data, or is the model being judged on what it trained on? +- **Statistical validity** — is the effect distinguishable from noise (sample size, variance, multiple comparisons), and are the assumptions of the test or model met? + +State whether the conclusion holds. Name each methodological flaw and what would +make the claim sound. If the method is appropriate, say so and stop. + +End your reply with a fenced ```json block: `{ "blockers": ["", ...] }`. List only what must be fixed before this is production-grade; an empty array means nothing blocks. diff --git a/packages/ai-autopilot/presets/data-science/prompts/regression-test.md b/packages/ai-autopilot/presets/data-science/prompts/regression-test.md new file mode 100644 index 0000000..6169cfc --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/prompts/regression-test.md @@ -0,0 +1,21 @@ +--- +name: regression-test +description: Ensure a data fix is locked in by a test on a known input and output. +appliesTo: ["**/*"] +metadata: + title: Regression test + loopId: regression-test + passes: 1 + event: bug-fix +--- + +You are making sure a data fix is locked in by a test — one that fails on the old +code and passes on the new. + +Check: +- Is there a test that runs the fixed step on a small, known input and asserts the expected output (a row count, an aggregate, a schema, a value)? +- Would it have caught the original bug — does it exercise the exact case that was wrong, including the edge (empty group, all-null column, boundary date)? +- Is it deterministic and fast enough to run in the pipeline, with the fixture committed rather than pulled from a live source? + +If the test is missing or weak, describe the exact input and expected output it +should assert. If it is already present and meaningful, confirm it and stop. diff --git a/packages/ai-autopilot/presets/data-science/prompts/reproducibility-review.md b/packages/ai-autopilot/presets/data-science/prompts/reproducibility-review.md new file mode 100644 index 0000000..8ef6bd1 --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/prompts/reproducibility-review.md @@ -0,0 +1,24 @@ +--- +name: reproducibility-review +description: Check someone else could reproduce this result from the repo alone. +appliesTo: ["**/*"] +metadata: + title: Reproducibility review + loopId: reproducibility-review + passes: 1 + event: major-change +--- + +You are checking that a data-science change is reproducible — that a colleague with +the repo and documented access could rerun it and get the same result. + +Look for: +- **Determinism** — random seeds set where results depend on them; no reliance on unpinned ordering or wall-clock. +- **Pinned environment** — dependencies and versions locked, not "whatever is installed". +- **Data provenance** — the data source is named and reachable (path, query, or fetch step), not a stray local file that only exists on one machine. +- **Runnable end to end** — the pipeline runs from a documented entry point, not a notebook with cells executed out of order or hidden manual steps. + +Report each concrete gap and the fix. If the work reproduces cleanly, say so and +stop. + +End your reply with a fenced ```json block: `{ "blockers": ["", ...] }`. List only what must be fixed before this is production-grade; an empty array means nothing blocks. diff --git a/packages/ai-autopilot/presets/data-science/skills/rules-of-ml.md b/packages/ai-autopilot/presets/data-science/skills/rules-of-ml.md new file mode 100644 index 0000000..b8b69d7 --- /dev/null +++ b/packages/ai-autopilot/presets/data-science/skills/rules-of-ml.md @@ -0,0 +1,12 @@ +--- +name: rules-of-ml +description: Google's Rules of Machine Learning — practical guidance for ML engineering. +metadata: + title: Rules of Machine Learning + url: https://developers.google.com/machine-learning/guides/rules-of-ml +--- + +A field guide to doing machine learning well: start simple, get the pipeline and +metrics right before the model, watch for training/serving skew and leakage, and +know when added complexity pays off. Consult it when framing data and modeling +review. diff --git a/packages/ai-autopilot/presets/web-development/loops/bug-fix.md b/packages/ai-autopilot/presets/web-development/loops/bug-fix.md new file mode 100644 index 0000000..c3d029e --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/loops/bug-fix.md @@ -0,0 +1,11 @@ +--- +name: bug-fix-loop +description: What fires after a bug fix in the UI. +metadata: + on: bug-fix + run: [ui-root-cause, regression-test] +--- + +When the agent fixes a UI bug, confirm the real cause (a state, a race, a wrong +breakpoint — not just the pixel that looked wrong), then lock it in with a +regression test that would have caught it. diff --git a/packages/ai-autopilot/presets/web-development/loops/major-change.md b/packages/ai-autopilot/presets/web-development/loops/major-change.md new file mode 100644 index 0000000..d9d79f0 --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/loops/major-change.md @@ -0,0 +1,11 @@ +--- +name: major-change-loop +description: What fires after a substantial change to the UI or its routes. +metadata: + on: major-change + run: [accessibility-review, performance-budget, web-security] +--- + +When the agent lands a substantial web change, check it is usable by everyone, +stays inside the performance budget, then look for security regressions in the +routes and markup it touched — in that order. diff --git a/packages/ai-autopilot/presets/web-development/loops/major-change.technical.md b/packages/ai-autopilot/presets/web-development/loops/major-change.technical.md new file mode 100644 index 0000000..45d3cd2 --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/loops/major-change.technical.md @@ -0,0 +1,13 @@ +--- +name: major-change-loop-technical +description: The major-change loop under Technical Control — leaner, the developer drives the depth. +metadata: + on: major-change + run: [accessibility-review] + conditions: technical +--- + +Technical Control mode: the developer is hands-on, so the loop only auto-runs the +accessibility pass — the one most often skipped by hand — and leaves performance +and security depth to them. Overrides the base major-change loop when `technical` +is active. diff --git a/packages/ai-autopilot/presets/web-development/preset.md b/packages/ai-autopilot/presets/web-development/preset.md new file mode 100644 index 0000000..1889857 --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/preset.md @@ -0,0 +1,11 @@ +--- +name: web-development +description: A web-app engineering loop — accessibility, performance budget, and web-security review on every substantial change to the UI or its routes. +metadata: + title: Web Development +--- + +Building for the browser. This preset adds the disciplines that generic +engineering review misses on a web app: accessibility, a performance budget, and +the security regressions that live in routes, forms, and rendered markup. Pick it +when the thing you are shipping runs in a browser, whatever the framework. diff --git a/packages/ai-autopilot/presets/web-development/prompts/accessibility-review.md b/packages/ai-autopilot/presets/web-development/prompts/accessibility-review.md new file mode 100644 index 0000000..e654e27 --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/prompts/accessibility-review.md @@ -0,0 +1,24 @@ +--- +name: accessibility-review +description: Check the change is usable with a keyboard, a screen reader, and low vision. +appliesTo: ["**/*"] +metadata: + title: Accessibility review + loopId: accessibility-review + passes: 1 + event: major-change +--- + +You are checking that a web change is usable by everyone, not just a mouse user on +a large screen. Scope it to the markup and interactions the change touched. + +Look for: +- **Semantics** — real elements for the job (`button`, `a`, `label`, headings in order), not a `div` with a click handler. +- **Keyboard** — every interactive control is reachable and operable by keyboard, focus is visible, and focus is managed on open/close of dialogs and menus. +- **Screen reader** — names and roles are present (labels, `alt`, `aria-*` only where a native element cannot carry it), and state changes are announced. +- **Low vision** — text contrast meets WCAG AA, nothing conveys meaning by color alone, and the layout survives 200% zoom. + +Report each concrete barrier with the element and the fix. If the change is +accessible, say so plainly and stop. + +End your reply with a fenced ```json block: `{ "blockers": ["", ...] }`. List only what must be fixed before this is production-grade; an empty array means nothing blocks. diff --git a/packages/ai-autopilot/presets/web-development/prompts/performance-budget.md b/packages/ai-autopilot/presets/web-development/prompts/performance-budget.md new file mode 100644 index 0000000..b7e85dc --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/prompts/performance-budget.md @@ -0,0 +1,24 @@ +--- +name: performance-budget +description: Check the change stays inside the web performance budget. +appliesTo: ["**/*"] +metadata: + title: Performance budget + loopId: performance-budget + passes: 1 + event: major-change +--- + +You are checking that a web change does not quietly blow the performance budget. +Scope it to what the change adds to the page and its network cost. + +Ask: +- **Bytes** — new dependencies, large imports pulled into the client bundle, or a heavy library added for one small use. +- **Core Web Vitals** — does the change hurt LCP (a big hero image or blocking font), CLS (content that shifts as it loads), or INP (a handler that blocks the main thread)? +- **Images and media** — sized, lazy where below the fold, and served in a modern format rather than a multi-megabyte original. +- **Requests** — waterfalls, unbatched calls, or data fetched on the client that could be fetched on the server. + +Name each concrete regression with its cost and the cheaper alternative. If the +change is within budget, say so and stop. + +End your reply with a fenced ```json block: `{ "blockers": ["", ...] }`. List only what must be fixed before this is production-grade; an empty array means nothing blocks. diff --git a/packages/ai-autopilot/presets/web-development/prompts/regression-test.md b/packages/ai-autopilot/presets/web-development/prompts/regression-test.md new file mode 100644 index 0000000..cc4a5b8 --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/prompts/regression-test.md @@ -0,0 +1,22 @@ +--- +name: regression-test +description: Ensure a UI fix is locked in by a test that fails without it. +appliesTo: ["**/*"] +metadata: + title: Regression test + loopId: regression-test + passes: 1 + event: bug-fix +--- + +You are making sure a UI bug fix is locked in by a regression test — one that fails +on the old code and passes on the new. + +Check: +- Is there a test that drives the actual interaction (a click, a keypress, a resize) and asserts the corrected behavior? +- Does it assert what the user sees or can do, not just that a component rendered without throwing? +- Is it at the right level — a component or end-to-end test for behavior a unit test cannot reach — and named so a future reader knows which bug it guards? + +If the regression test is missing or weak, describe the exact interaction and +assertion it should cover. If it is already present and meaningful, confirm it and +stop. diff --git a/packages/ai-autopilot/presets/web-development/prompts/ui-root-cause.md b/packages/ai-autopilot/presets/web-development/prompts/ui-root-cause.md new file mode 100644 index 0000000..821f2c9 --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/prompts/ui-root-cause.md @@ -0,0 +1,22 @@ +--- +name: ui-root-cause +description: Confirm a UI fix addresses the real cause, not the visible symptom. +appliesTo: ["**/*"] +metadata: + title: UI root cause + loopId: ui-root-cause + passes: 1 + event: bug-fix +--- + +You are checking that a UI bug fix addresses the **real cause**, not the pixel that +looked wrong. + +Work backwards from the fix: +- What was the actual defect — a wrong state transition, a race between render and data, a bad breakpoint, a stale effect dependency — and why did it show up the way it did? +- Does the fix remove that cause, or does it paper over it (a hard-coded value, a `setTimeout`, a `!important`, a magic z-index)? +- Could the same cause surface on another screen size, another route, or another component sharing the state? + +State the root cause in one sentence. If the fix only treats the symptom, say what +the real fix is. If it correctly removes the cause, confirm it and note any sibling +views worth the same treatment. diff --git a/packages/ai-autopilot/presets/web-development/prompts/web-security.md b/packages/ai-autopilot/presets/web-development/prompts/web-security.md new file mode 100644 index 0000000..2536905 --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/prompts/web-security.md @@ -0,0 +1,25 @@ +--- +name: web-security +description: Look for browser-facing security regressions introduced by the change. +appliesTo: ["**/*"] +metadata: + title: Web security + loopId: web-security + passes: 1 + event: major-change +--- + +You are checking a web change for browser-facing security regressions. Scope the +review to the routes, forms, and markup the change touched; do not audit the whole +app. + +Look for: +- **XSS** — untrusted data rendered without escaping, `dangerouslySetInnerHTML` / `v-html` / `innerHTML` on user or network content. +- **Authz on routes** — a new page, action, or API route that skips an auth or ownership check its neighbors make. +- **CSRF and cookies** — state-changing requests without protection, cookies missing `HttpOnly` / `Secure` / `SameSite`. +- **Secrets and headers** — API keys or tokens shipped to the client, missing or weakened CSP, `target="_blank"` without `rel="noopener"`. + +Report each concrete risk with the file, why it is exploitable, and the fix. If the +change introduces no new exposure, say so and stop. + +End your reply with a fenced ```json block: `{ "blockers": ["", ...] }`. List only what must be fixed before this is production-grade; an empty array means nothing blocks. diff --git a/packages/ai-autopilot/presets/web-development/skills/web-platform.md b/packages/ai-autopilot/presets/web-development/skills/web-platform.md new file mode 100644 index 0000000..baacf95 --- /dev/null +++ b/packages/ai-autopilot/presets/web-development/skills/web-platform.md @@ -0,0 +1,12 @@ +--- +name: web-platform +description: web.dev — the web platform's guidance on accessibility, performance, and security. +metadata: + title: Web Platform Practices + url: https://web.dev/ +--- + +The reference for what good looks like on the web: accessibility patterns, Core +Web Vitals and the performance budget, and the security guidance behind CSP, +cross-site protections, and safe rendering. Consult it when framing web review and +authoring work. diff --git a/packages/ai-autopilot/src/preset/builtin-presets.test.ts b/packages/ai-autopilot/src/preset/builtin-presets.test.ts new file mode 100644 index 0000000..3f19848 --- /dev/null +++ b/packages/ai-autopilot/src/preset/builtin-presets.test.ts @@ -0,0 +1,79 @@ +import { describe, it } from 'node:test' +import assert from 'node:assert/strict' +import { builtinDomainPresets } from './load.js' +import { selectPreset } from './compose.js' + +// The two web/data presets added alongside software-development. They are pure +// .md content, auto-discovered by builtinDomainPresets(); these guard their shape. +const CASES = [ + { + name: 'web-development', + title: 'Web Development', + skillUrl: 'https://web.dev/', + majorRun: ['accessibility-review', 'performance-budget', 'web-security'], + technicalRun: ['accessibility-review'], + }, + { + name: 'data-science', + title: 'Data Science', + skillUrl: 'https://developers.google.com/machine-learning/guides/rules-of-ml', + majorRun: ['reproducibility-review', 'data-validation', 'methodology-review'], + technicalRun: ['reproducibility-review'], + }, +] + +const majorLoop = (p: { loops: readonly { on: readonly string[]; run: readonly string[] }[] }) => + p.loops.find(l => l.on.includes('major-change'))! + +for (const c of CASES) { + describe(`${c.name} (shipped built-in)`, () => { + it('loads the {loops, prompts, skills} bundle from the shipped directory', async () => { + const preset = selectPreset(await builtinDomainPresets(), c.name) + assert.ok(preset, `${c.name} is discovered`) + assert.equal(preset!.title, c.title) + assert.ok(preset!.description.length > 0) + assert.equal(preset!.loops.length, 2) // major-change + bug-fix + assert.ok(preset!.prompts.length >= 5) + assert.equal(preset!.skills.length, 1) + assert.equal(preset!.skills[0]!.url, c.skillUrl) + }) + + it('targets non-web loop events (major-change, bug-fix)', async () => { + const preset = selectPreset(await builtinDomainPresets(), c.name)! + const kinds = preset.loops.flatMap(l => [...l.on]).sort() + assert.deepEqual(kinds, ['bug-fix', 'major-change']) + }) + + it('every id a loop dispatches resolves to a shipped, non-empty prompt body', async () => { + const preset = selectPreset(await builtinDomainPresets(), c.name)! + const byId = new Map(preset.prompts.map(p => [p.id, p])) + for (const loop of preset.loops) { + for (const id of loop.run) { + const prompt = byId.get(id) + assert.ok(prompt, `loop prompt "${id}" has a shipped body`) + assert.ok(prompt!.instructions.length > 0, `"${id}" body is non-empty`) + } + } + }) + + it('major-change review prompts carry the { blockers } verdict footer so the loop gates', async () => { + const preset = selectPreset(await builtinDomainPresets(), c.name)! + const byId = new Map(preset.prompts.map(p => [p.id, p])) + for (const id of majorLoop(preset).run) { + assert.match( + byId.get(id)!.instructions, + /"blockers"/, + `"${id}" ends with a blockers verdict footer`, + ) + } + }) + + it('Technical Control mode overrides the major-change loop with the leaner variant', async () => { + const base = selectPreset(await builtinDomainPresets(), c.name)! + const technical = selectPreset(await builtinDomainPresets({ modes: ['technical'] }), c.name)! + assert.deepEqual([...majorLoop(base).run], c.majorRun) + assert.deepEqual([...majorLoop(technical).run], c.technicalRun) // the variant wins + assert.equal(technical.loops.length, 2) // replaces the base, not adds + }) + }) +}