claudecode-omc 5.6.8 → 5.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.local/settings/settings.json +8 -0
- package/.local/skills/prompt-optimizer/SKILL.md +262 -19
- package/.omc-curation/ecc-selection.json +80 -0
- package/.omc-curation/governance.json +116 -0
- package/.omc-curation/sources.lock.json +30 -0
- package/README.md +78 -4
- package/bundled/manifest.json +6 -5
- package/bundled/upstream/anthropic-skills/.omc-source/bundle.json +18 -0
- package/bundled/upstream/anthropic-skills/.omc-source/provenance.json +399 -0
- package/bundled/upstream/anthropic-skills/skills/claude-api/SKILL.md +18 -17
- package/bundled/upstream/anthropic-skills/skills/claude-api/curl/examples.md +9 -9
- package/bundled/upstream/anthropic-skills/skills/claude-api/curl/managed-agents.md +4 -4
- package/bundled/upstream/anthropic-skills/skills/claude-api/go/managed-agents/README.md +2 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/java/claude-api.md +2 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/java/managed-agents/README.md +2 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/php/claude-api.md +10 -10
- package/bundled/upstream/anthropic-skills/skills/claude-api/php/managed-agents/README.md +2 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/README.md +16 -16
- package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/batches.md +3 -3
- package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/files-api.md +3 -3
- package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/streaming.md +7 -7
- package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/tool-use.md +19 -19
- package/bundled/upstream/anthropic-skills/skills/claude-api/python/managed-agents/README.md +3 -3
- package/bundled/upstream/anthropic-skills/skills/claude-api/ruby/claude-api.md +4 -4
- package/bundled/upstream/anthropic-skills/skills/claude-api/ruby/managed-agents/README.md +2 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/error-codes.md +5 -5
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/live-sources.md +3 -1
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-api-reference.md +10 -4
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-core.md +19 -1
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-environments.md +6 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-multiagent.md +1 -1
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-onboarding.md +3 -3
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-overview.md +3 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +173 -0
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-tools.md +10 -4
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/model-migration.md +113 -13
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/models.md +14 -11
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/prompt-caching.md +2 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/shared/tool-use-concepts.md +4 -4
- package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/README.md +15 -15
- package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/batches.md +2 -2
- package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/files-api.md +1 -1
- package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/streaming.md +5 -5
- package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/tool-use.md +15 -15
- package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/managed-agents/README.md +3 -3
- package/bundled/upstream/ecc/.omc-source/bundle.json +2 -1
- package/bundled/upstream/ecc/.omc-source/last-plan-apply.json +108 -24
- package/bundled/upstream/ecc/.omc-source/manifests/.claude-plugin/marketplace.json +3 -3
- package/bundled/upstream/ecc/.omc-source/provenance.json +563 -0
- package/bundled/upstream/ecc/agents/marketing-agent.md +159 -0
- package/bundled/upstream/ecc/agents/react-build-resolver.md +215 -0
- package/bundled/upstream/ecc/agents/react-reviewer.md +167 -0
- package/bundled/upstream/ecc/agents/typescript-reviewer.md +3 -0
- package/bundled/upstream/ecc/commands/harness-audit.md +17 -10
- package/bundled/upstream/ecc/commands/marketing-campaign.md +129 -0
- package/bundled/upstream/ecc/commands/react-build.md +187 -0
- package/bundled/upstream/ecc/commands/react-review.md +170 -0
- package/bundled/upstream/ecc/commands/react-test.md +265 -0
- package/bundled/upstream/ecc/skills/benchmark-optimization-loop/SKILL.md +69 -0
- package/bundled/upstream/ecc/skills/blender-motion-state-inspection/SKILL.md +164 -0
- package/bundled/upstream/ecc/skills/canary-watch/SKILL.md +9 -1
- package/bundled/upstream/ecc/skills/continuous-learning-v2/hooks/observe.sh +31 -9
- package/bundled/upstream/ecc/skills/continuous-learning-v2/scripts/detect-project.sh +38 -4
- package/bundled/upstream/ecc/skills/continuous-learning-v2/scripts/instinct-cli.py +319 -12
- package/bundled/upstream/ecc/skills/data-throughput-accelerator/SKILL.md +72 -0
- package/bundled/upstream/ecc/skills/dynamic-workflow-mode/SKILL.md +123 -0
- package/bundled/upstream/ecc/skills/frontend-a11y/SKILL.md +446 -0
- package/bundled/upstream/ecc/skills/ito-basket-compare/SKILL.md +63 -0
- package/bundled/upstream/ecc/skills/ito-data-atlas-agent/SKILL.md +63 -0
- package/bundled/upstream/ecc/skills/ito-market-intelligence/SKILL.md +60 -0
- package/bundled/upstream/ecc/skills/ito-trade-planner/SKILL.md +67 -0
- package/bundled/upstream/ecc/skills/latency-critical-systems/SKILL.md +73 -0
- package/bundled/upstream/ecc/skills/marketing-campaign/SKILL.md +113 -0
- package/bundled/upstream/ecc/skills/nextjs-turbopack/SKILL.md +13 -0
- package/bundled/upstream/ecc/skills/parallel-execution-optimizer/SKILL.md +72 -0
- package/bundled/upstream/ecc/skills/prediction-market-oracle-research/SKILL.md +63 -0
- package/bundled/upstream/ecc/skills/prediction-market-risk-review/SKILL.md +60 -0
- package/bundled/upstream/ecc/skills/react-patterns/SKILL.md +341 -0
- package/bundled/upstream/ecc/skills/react-performance/SKILL.md +574 -0
- package/bundled/upstream/ecc/skills/react-testing/SKILL.md +423 -0
- package/bundled/upstream/ecc/skills/recsys-pipeline-architect/SKILL.md +114 -0
- package/bundled/upstream/ecc/skills/recursive-decision-ledger/SKILL.md +79 -0
- package/bundled/upstream/ecc/skills/social-publisher/SKILL.md +115 -0
- package/bundled/upstream/ecc/skills/team-agent-orchestration/SKILL.md +110 -0
- package/bundled/upstream/ecc/skills/uncloud/SKILL.md +343 -0
- package/bundled/upstream/ecc/skills/windows-desktop-e2e/SKILL.md +99 -0
- package/bundled/upstream/impeccable/.omc-source/bundle.json +20 -0
- package/bundled/upstream/impeccable/.omc-source/provenance.json +105 -0
- package/bundled/upstream/impeccable/agents/impeccable-manual-edit-applier.md +97 -0
- package/bundled/upstream/impeccable/skills/impeccable/SKILL.md +168 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/adapt.md +311 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/animate.md +201 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/audit.md +133 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/bolder.md +113 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/brand.md +108 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/clarify.md +288 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/codex.md +105 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/colorize.md +257 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/craft.md +123 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/critique.md +767 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/delight.md +302 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/distill.md +111 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/document.md +429 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/extract.md +69 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/harden.md +347 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/hooks.md +88 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/init.md +172 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/interaction-design.md +189 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/layout.md +161 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/live.md +718 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/onboard.md +234 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/optimize.md +258 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/overdrive.md +130 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/polish.md +241 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/product.md +60 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/quieter.md +99 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/shape.md +165 -0
- package/bundled/upstream/impeccable/skills/impeccable/reference/typeset.md +279 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/command-metadata.json +94 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/context-signals.mjs +225 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/context.mjs +280 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/critique-storage.mjs +242 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detect-csp.mjs +198 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detect.mjs +21 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/browser/injected/index.mjs +1735 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/cli/main.mjs +244 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +4907 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/detect-antipatterns.mjs +43 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +252 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +552 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +1013 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +208 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/engines/visual/screenshot-contrast.mjs +189 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/findings.mjs +12 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/node/file-system.mjs +198 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/profile/profiler.mjs +166 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/registry/antipatterns.mjs +419 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/rules/checks.mjs +2671 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/shared/color.mjs +124 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/shared/constants.mjs +101 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/detector/shared/page.mjs +7 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/hook-admin.mjs +574 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/hook-before-edit.mjs +473 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/hook-lib.mjs +1286 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/hook.mjs +61 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/lib/design-parser.mjs +835 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/lib/impeccable-paths.mjs +126 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/lib/is-generated.mjs +69 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/browser-script-parts.mjs +49 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/completion.mjs +19 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/event-validation.mjs +137 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/insert-ui.mjs +458 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/manual-apply.mjs +939 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/manual-edit-routes.mjs +357 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/manual-edits-buffer.mjs +152 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/session-store.mjs +289 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/svelte-component.mjs +826 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/sveltekit-adapter.mjs +274 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/ui-core.mjs +180 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live/vocabulary.mjs +36 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-accept.mjs +812 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-browser-dom.js +146 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-browser-session.js +123 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-browser.js +11086 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-commit-manual-edits.mjs +1241 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-complete.mjs +75 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-copy-edit-agent.mjs +683 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-discard-manual-edits.mjs +51 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-inject.mjs +583 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-insert.mjs +272 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-manual-edit-evidence.mjs +363 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-poll.mjs +379 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-resume.mjs +94 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-server.mjs +1134 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-status.mjs +61 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live-wrap.mjs +894 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/live.mjs +246 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/palette.mjs +633 -0
- package/bundled/upstream/impeccable/skills/impeccable/scripts/pin.mjs +214 -0
- package/bundled/upstream/oh-my-claudecode/.omc-source/bundle.json +2 -1
- package/bundled/upstream/oh-my-claudecode/.omc-source/provenance.json +116 -0
- package/bundled/upstream/oh-my-claudecode/skills/autopilot/SKILL.md +7 -0
- package/bundled/upstream/oh-my-claudecode/skills/cancel/SKILL.md +1 -0
- package/bundled/upstream/oh-my-claudecode/skills/deep-interview/SKILL.md +39 -5
- package/bundled/upstream/oh-my-claudecode/skills/hud/SKILL.md +1 -0
- package/bundled/upstream/oh-my-claudecode/skills/local-build-reminder/SKILL.md +78 -0
- package/bundled/upstream/oh-my-claudecode/skills/omc-doctor/SKILL.md +1 -1
- package/bundled/upstream/oh-my-claudecode/skills/omc-setup/SKILL.md +26 -10
- package/bundled/upstream/oh-my-claudecode/skills/omc-setup/phases/01-install-claude-md.md +3 -3
- package/bundled/upstream/oh-my-claudecode/skills/omc-setup/phases/02-configure.md +6 -4
- package/bundled/upstream/oh-my-claudecode/skills/omc-setup/phases/03-integrations.md +1 -1
- package/bundled/upstream/oh-my-claudecode/skills/omc-setup/phases/04-welcome.md +2 -2
- package/bundled/upstream/oh-my-claudecode/skills/omc-teams/SKILL.md +6 -6
- package/bundled/upstream/oh-my-claudecode/skills/plan/SKILL.md +44 -32
- package/bundled/upstream/oh-my-claudecode/skills/ralph/SKILL.md +45 -21
- package/bundled/upstream/oh-my-claudecode/skills/ralplan/SKILL.md +1 -1
- package/bundled/upstream/oh-my-claudecode/skills/self-improve/SKILL.md +7 -0
- package/bundled/upstream/oh-my-claudecode/skills/self-improve/scripts/resolve-paths.mjs +39 -15
- package/bundled/upstream/oh-my-claudecode/skills/team/SKILL.md +132 -90
- package/bundled/upstream/oh-my-claudecode/skills/ultragoal/SKILL.md +93 -0
- package/bundled/upstream/oh-my-claudecode/skills/ultraqa/SKILL.md +28 -13
- package/bundled/upstream/oh-my-claudecode/skills/ultrawork/SKILL.md +7 -0
- package/bundled/upstream/superpowers/.omc-source/bundle.json +2 -1
- package/bundled/upstream/superpowers/.omc-source/provenance.json +63 -0
- package/package.json +2 -1
- package/src/catalog/source-catalog.js +10 -4
- package/src/cli/index.js +4 -0
- package/src/cli/plan.js +14 -2
- package/src/cli/setup.js +52 -13
- package/src/cli/skill.js +1 -1
- package/src/cli/source.js +271 -14
- package/src/config/sources.js +82 -1
- package/src/merge/content-patch.js +88 -0
- package/templates/merge-config.json +1 -8
- package/bundled/upstream/ecc/skills/strategic-compact/suggest-compact.sh +0 -54
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-testing
|
|
3
|
+
description: React component testing with React Testing Library, Vitest/Jest, MSW for network mocking, accessibility assertions with axe, and the decision boundary between component tests and Playwright/Cypress end-to-end runs. Use when writing or fixing tests for React components, hooks, or pages.
|
|
4
|
+
origin: ECC
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Testing
|
|
8
|
+
|
|
9
|
+
Comprehensive React testing patterns for behavior-focused component tests, custom hook tests, accessibility assertions, and network-level mocking.
|
|
10
|
+
|
|
11
|
+
## When to Activate
|
|
12
|
+
|
|
13
|
+
- Writing tests for React components, custom hooks, or pages
|
|
14
|
+
- Adding test coverage to legacy untested components
|
|
15
|
+
- Migrating from Enzyme or class-component-era patterns to React Testing Library
|
|
16
|
+
- Setting up Vitest or Jest for a new React project
|
|
17
|
+
- Mocking HTTP requests in tests
|
|
18
|
+
- Asserting accessibility violations
|
|
19
|
+
- Deciding which tests belong in RTL vs Playwright Component Testing vs full E2E
|
|
20
|
+
|
|
21
|
+
## Core Principle
|
|
22
|
+
|
|
23
|
+
Test what the user sees and does, not implementation details.
|
|
24
|
+
|
|
25
|
+
A test should:
|
|
26
|
+
|
|
27
|
+
- Render the component with the same providers it has in production
|
|
28
|
+
- Interact with it via accessible queries (role, label) and `userEvent`
|
|
29
|
+
- Assert visible output and observable side effects (callback fired, request sent)
|
|
30
|
+
|
|
31
|
+
A test should NOT:
|
|
32
|
+
|
|
33
|
+
- Inspect component state, props passed to children, or which hooks were called
|
|
34
|
+
- Mock React itself or framework hooks
|
|
35
|
+
- Assert on the number of renders or DOM structure beyond what affects users
|
|
36
|
+
|
|
37
|
+
## Library Choice
|
|
38
|
+
|
|
39
|
+
| Runner | When | Note |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| **Vitest** | Vite, Remix, modern setups | Faster, native ESM, Jest-compatible API |
|
|
42
|
+
| **Jest** | Next.js, CRA, established repos | Default for many React projects |
|
|
43
|
+
| **Playwright Component Testing** | Real browser engine needed | Use when JSDOM lacks the required feature |
|
|
44
|
+
| **Cypress Component Testing** | Real browser, Cypress already in use | Alternative to Playwright CT |
|
|
45
|
+
|
|
46
|
+
Pick one. Do not run RTL + Vitest AND Playwright CT in the same repo unless you have a clear lane separation.
|
|
47
|
+
|
|
48
|
+
## Query Priority
|
|
49
|
+
|
|
50
|
+
React Testing Library exposes queries in three tiers — use top-down:
|
|
51
|
+
|
|
52
|
+
1. **Accessible to everyone**: `getByRole`, `getByLabelText`, `getByPlaceholderText`, `getByText`, `getByDisplayValue`
|
|
53
|
+
2. **Semantic**: `getByAltText`, `getByTitle`
|
|
54
|
+
3. **Test IDs (escape hatch)**: `getByTestId`
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
// Best
|
|
58
|
+
screen.getByRole("button", { name: /save/i });
|
|
59
|
+
|
|
60
|
+
// OK for inputs
|
|
61
|
+
screen.getByLabelText("Email");
|
|
62
|
+
|
|
63
|
+
// Last resort
|
|
64
|
+
screen.getByTestId("save-btn");
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Variants:
|
|
68
|
+
|
|
69
|
+
- `getBy*` — throws if no match
|
|
70
|
+
- `queryBy*` — returns `null` (use for "assert absence")
|
|
71
|
+
- `findBy*` — async, returns a Promise (use for elements that appear after async work)
|
|
72
|
+
|
|
73
|
+
## User Interaction with `userEvent`
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import userEvent from "@testing-library/user-event";
|
|
77
|
+
|
|
78
|
+
test("submits the form", async () => {
|
|
79
|
+
const user = userEvent.setup();
|
|
80
|
+
const onSubmit = vi.fn();
|
|
81
|
+
render(<UserForm onSubmit={onSubmit} />);
|
|
82
|
+
|
|
83
|
+
await user.type(screen.getByLabelText("Email"), "user@example.com");
|
|
84
|
+
await user.click(screen.getByRole("button", { name: /save/i }));
|
|
85
|
+
|
|
86
|
+
expect(onSubmit).toHaveBeenCalledWith({ email: "user@example.com" });
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- Always `await` userEvent calls
|
|
91
|
+
- Call `userEvent.setup()` once per test, reuse the returned `user`
|
|
92
|
+
- `userEvent` simulates a real browser sequence; `fireEvent` dispatches a single synthetic event — prefer `userEvent`
|
|
93
|
+
|
|
94
|
+
## Async Patterns
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
// Element that appears after async work
|
|
98
|
+
expect(await screen.findByText("Loaded")).toBeInTheDocument();
|
|
99
|
+
|
|
100
|
+
// Side effect assertion
|
|
101
|
+
await waitFor(() => expect(saveSpy).toHaveBeenCalled());
|
|
102
|
+
|
|
103
|
+
// Element that should disappear
|
|
104
|
+
await waitForElementToBeRemoved(() => screen.queryByText("Loading"));
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Never `setTimeout` + assertion — flaky. Use the matchers above.
|
|
108
|
+
|
|
109
|
+
## Network Mocking with MSW
|
|
110
|
+
|
|
111
|
+
Mock Service Worker mocks at the network layer. The component, hooks, and fetch library all behave exactly as in production.
|
|
112
|
+
|
|
113
|
+
### Setup
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// test/setup.ts
|
|
117
|
+
import { setupServer } from "msw/node";
|
|
118
|
+
import { http, HttpResponse } from "msw";
|
|
119
|
+
|
|
120
|
+
export const handlers = [
|
|
121
|
+
http.get("/api/users/:id", ({ params }) =>
|
|
122
|
+
HttpResponse.json({ id: params.id, name: "Alice" }),
|
|
123
|
+
),
|
|
124
|
+
http.post("/api/users", async ({ request }) => {
|
|
125
|
+
const body = await request.json();
|
|
126
|
+
return HttpResponse.json({ id: "new-id", ...body }, { status: 201 });
|
|
127
|
+
}),
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
export const server = setupServer(...handlers);
|
|
131
|
+
|
|
132
|
+
beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
|
|
133
|
+
afterEach(() => server.resetHandlers());
|
|
134
|
+
afterAll(() => server.close());
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Configure `onUnhandledRequest: "error"` so any unmocked request fails the test loudly — silent passes are worse than red.
|
|
138
|
+
|
|
139
|
+
### Per-test override
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
test("renders error on 500", async () => {
|
|
143
|
+
server.use(
|
|
144
|
+
http.get("/api/users/:id", () => new HttpResponse(null, { status: 500 })),
|
|
145
|
+
);
|
|
146
|
+
render(<UserPage id="1" />);
|
|
147
|
+
expect(await screen.findByText(/something went wrong/i)).toBeInTheDocument();
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Provider Wrapping
|
|
152
|
+
|
|
153
|
+
Wrap providers once in a `test-utils.tsx`:
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
// test-utils.tsx
|
|
157
|
+
import { render, RenderOptions } from "@testing-library/react";
|
|
158
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
159
|
+
|
|
160
|
+
export function renderWithProviders(
|
|
161
|
+
ui: React.ReactElement,
|
|
162
|
+
options?: RenderOptions,
|
|
163
|
+
) {
|
|
164
|
+
const queryClient = new QueryClient({
|
|
165
|
+
defaultOptions: { queries: { retry: false } },
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return render(
|
|
169
|
+
<QueryClientProvider client={queryClient}>
|
|
170
|
+
<ThemeProvider theme={lightTheme}>
|
|
171
|
+
<MemoryRouter>{ui}</MemoryRouter>
|
|
172
|
+
</ThemeProvider>
|
|
173
|
+
</QueryClientProvider>,
|
|
174
|
+
options,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export * from "@testing-library/react";
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Then `import { renderWithProviders, screen } from "test-utils"` in every test file.
|
|
182
|
+
|
|
183
|
+
## Custom Hook Testing
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { renderHook, act } from "@testing-library/react";
|
|
187
|
+
|
|
188
|
+
test("useCounter increments and decrements", () => {
|
|
189
|
+
const { result } = renderHook(() => useCounter(0));
|
|
190
|
+
|
|
191
|
+
expect(result.current.count).toBe(0);
|
|
192
|
+
|
|
193
|
+
act(() => result.current.increment());
|
|
194
|
+
expect(result.current.count).toBe(1);
|
|
195
|
+
|
|
196
|
+
act(() => result.current.decrement());
|
|
197
|
+
expect(result.current.count).toBe(0);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("useCounter accepts initial value", () => {
|
|
201
|
+
const { result } = renderHook(() => useCounter(10));
|
|
202
|
+
expect(result.current.count).toBe(10);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("useUser fetches user data", async () => {
|
|
206
|
+
// Instantiate QueryClient ONCE per test outside the wrapper so it survives re-renders.
|
|
207
|
+
// Creating it inside the wrapper closure resets cache state on every render, producing flaky tests.
|
|
208
|
+
const queryClient = new QueryClient({
|
|
209
|
+
defaultOptions: { queries: { retry: false } },
|
|
210
|
+
});
|
|
211
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
212
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const { result } = renderHook(() => useUser("1"), { wrapper });
|
|
216
|
+
|
|
217
|
+
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
218
|
+
expect(result.current.data).toEqual({ id: "1", name: "Alice" });
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- Wrap state-changing calls in `act`
|
|
223
|
+
- Test through the hook's public API only
|
|
224
|
+
- For hooks that use context, pass a `wrapper`
|
|
225
|
+
|
|
226
|
+
## Accessibility Assertions
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
import { axe, toHaveNoViolations } from "jest-axe"; // or vitest-axe
|
|
230
|
+
expect.extend(toHaveNoViolations);
|
|
231
|
+
|
|
232
|
+
test("UserCard has no a11y violations", async () => {
|
|
233
|
+
const { container } = render(<UserCard user={mockUser} />);
|
|
234
|
+
expect(await axe(container)).toHaveNoViolations();
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Run axe in component tests for every interactive component. Catches:
|
|
239
|
+
|
|
240
|
+
- Missing labels on form inputs
|
|
241
|
+
- Invalid ARIA usage
|
|
242
|
+
- Poor color contrast (limited — JSDOM has no real CSS engine, so this works for inline styles only; visual contrast belongs in Playwright)
|
|
243
|
+
- Missing alt text on images
|
|
244
|
+
- Heading order violations
|
|
245
|
+
|
|
246
|
+
Cross-link: [skills/accessibility/SKILL.md](../accessibility/SKILL.md) for the broader a11y testing playbook.
|
|
247
|
+
|
|
248
|
+
## When NOT to Use Snapshot Tests
|
|
249
|
+
|
|
250
|
+
Snapshots of rendered output:
|
|
251
|
+
|
|
252
|
+
- Break on every styling change
|
|
253
|
+
- Get rubber-stamped during review
|
|
254
|
+
- Test implementation detail (DOM structure), not behavior
|
|
255
|
+
|
|
256
|
+
Acceptable snapshot uses:
|
|
257
|
+
|
|
258
|
+
- Pure data serialization functions (`formatInvoice(invoice)` -> stable string)
|
|
259
|
+
- Generated config files (e.g., webpack config output)
|
|
260
|
+
|
|
261
|
+
For visual regression on components, use Playwright/Cypress screenshots or Percy/Chromatic — actual visual diffs, not DOM strings.
|
|
262
|
+
|
|
263
|
+
## When to Reach for Playwright / Cypress
|
|
264
|
+
|
|
265
|
+
JSDOM (used by Vitest/Jest) cannot:
|
|
266
|
+
|
|
267
|
+
- Render real layout (flexbox, grid, viewport queries)
|
|
268
|
+
- Run native browser animation, CSS transitions
|
|
269
|
+
- Test scrolling behavior, drag-and-drop, paste from clipboard
|
|
270
|
+
- Handle iframes, popups, downloads, cross-origin flows
|
|
271
|
+
- Run real network in a controlled environment with full DevTools support
|
|
272
|
+
|
|
273
|
+
For any of those, use Playwright Component Testing (component test in real browser) or full E2E. See [e2e-testing skill](../e2e-testing/SKILL.md).
|
|
274
|
+
|
|
275
|
+
Decision boundary:
|
|
276
|
+
|
|
277
|
+
- A hook, a presentational component, a form with logic -> RTL
|
|
278
|
+
- A component whose layout matters or that uses browser APIs not in JSDOM -> Playwright CT
|
|
279
|
+
- A full user flow across multiple pages -> Playwright/Cypress E2E
|
|
280
|
+
|
|
281
|
+
## Coverage Targets
|
|
282
|
+
|
|
283
|
+
| Layer | Target |
|
|
284
|
+
|---|---|
|
|
285
|
+
| Pure utilities | >=90% |
|
|
286
|
+
| Custom hooks | >=85% |
|
|
287
|
+
| Presentational components | >=80% — behavior, not lines |
|
|
288
|
+
| Container components | >=70% — golden paths + error states |
|
|
289
|
+
| Pages | E2E covered separately; smoke test minimum |
|
|
290
|
+
|
|
291
|
+
Configure via `vitest.config.ts` / `jest.config.js`:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
// vitest.config.ts
|
|
295
|
+
test: {
|
|
296
|
+
coverage: {
|
|
297
|
+
provider: "v8",
|
|
298
|
+
reporter: ["text", "html", "lcov"],
|
|
299
|
+
thresholds: {
|
|
300
|
+
lines: 80,
|
|
301
|
+
functions: 80,
|
|
302
|
+
branches: 70,
|
|
303
|
+
statements: 80,
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Anti-Patterns
|
|
310
|
+
|
|
311
|
+
- `container.querySelector("...")` — bypasses accessibility queries, lets tests pass when real users would fail
|
|
312
|
+
- Asserting on number of renders — implementation detail
|
|
313
|
+
- `jest.mock("react", ...)` — never mock React. Refactor the component instead
|
|
314
|
+
- Mocking child components by default — tests the integration, not isolation. Mock only when the child has heavy side effects
|
|
315
|
+
- Ignoring `act()` warnings — they signal real bugs (state update after unmount, missing async wrapping)
|
|
316
|
+
- Sharing mutable state across tests — flakes when test order changes
|
|
317
|
+
- Tests that pass with `it.skip()` removed — your test does not actually assert what you think
|
|
318
|
+
|
|
319
|
+
## TDD Workflow
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
RED -> Write failing test for the next requirement
|
|
323
|
+
GREEN -> Write minimal component code to pass
|
|
324
|
+
REFACTOR -> Improve the component, tests stay green
|
|
325
|
+
REPEAT -> Next requirement
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
For new components:
|
|
329
|
+
|
|
330
|
+
1. Define the component's prop type and signature
|
|
331
|
+
2. Write the first test for the simplest case
|
|
332
|
+
3. Verify it fails for the right reason
|
|
333
|
+
4. Implement just enough to pass
|
|
334
|
+
5. Add the next test case
|
|
335
|
+
6. Refactor when the third similar test reveals a pattern
|
|
336
|
+
|
|
337
|
+
## Test Commands
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
# Vitest
|
|
341
|
+
vitest # watch
|
|
342
|
+
vitest run # one-shot
|
|
343
|
+
vitest run --coverage # with coverage
|
|
344
|
+
vitest run path/to/file.test.tsx # single file
|
|
345
|
+
|
|
346
|
+
# Jest
|
|
347
|
+
jest --watch
|
|
348
|
+
jest --coverage
|
|
349
|
+
jest path/to/file.test.tsx
|
|
350
|
+
|
|
351
|
+
# CI mode
|
|
352
|
+
CI=true vitest run --coverage
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Related
|
|
356
|
+
|
|
357
|
+
- Rules: [rules/react/testing.md](../../rules/react/testing.md)
|
|
358
|
+
- Skills: [react-patterns](../react-patterns/SKILL.md), [accessibility](../accessibility/SKILL.md), [e2e-testing](../e2e-testing/SKILL.md), [tdd-workflow](../tdd-workflow/SKILL.md)
|
|
359
|
+
- Agents: `react-reviewer` (reviews test quality during code review), `tdd-guide` (enforces TDD process)
|
|
360
|
+
- Commands: `/react-test`, `/react-review`
|
|
361
|
+
|
|
362
|
+
## Examples
|
|
363
|
+
|
|
364
|
+
### Form submission with MSW and userEvent
|
|
365
|
+
|
|
366
|
+
```tsx
|
|
367
|
+
test("submits user form and shows success", async () => {
|
|
368
|
+
server.use(
|
|
369
|
+
http.post("/api/users", () =>
|
|
370
|
+
HttpResponse.json({ id: "1", name: "Alice" }, { status: 201 }),
|
|
371
|
+
),
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const user = userEvent.setup();
|
|
375
|
+
renderWithProviders(<UserForm />);
|
|
376
|
+
|
|
377
|
+
await user.type(screen.getByLabelText("Name"), "Alice");
|
|
378
|
+
await user.type(screen.getByLabelText("Email"), "alice@example.com");
|
|
379
|
+
await user.click(screen.getByRole("button", { name: /save/i }));
|
|
380
|
+
|
|
381
|
+
expect(await screen.findByText(/saved successfully/i)).toBeInTheDocument();
|
|
382
|
+
});
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Testing an error boundary
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
function Broken() {
|
|
389
|
+
throw new Error("boom");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
test("error boundary renders fallback", () => {
|
|
393
|
+
// Suppress React's console.error noise for the expected throw, then restore so
|
|
394
|
+
// the spy does not leak across tests and hide real errors elsewhere.
|
|
395
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
396
|
+
try {
|
|
397
|
+
render(
|
|
398
|
+
<ErrorBoundary fallback={<div>Something went wrong</div>}>
|
|
399
|
+
<Broken />
|
|
400
|
+
</ErrorBoundary>,
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
expect(screen.getByText("Something went wrong")).toBeInTheDocument();
|
|
404
|
+
} finally {
|
|
405
|
+
errorSpy.mockRestore();
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Testing a Suspense boundary
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
test("shows loading then content", async () => {
|
|
414
|
+
renderWithProviders(
|
|
415
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
416
|
+
<UserDetail id="1" />
|
|
417
|
+
</Suspense>,
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
expect(screen.getByText("Loading...")).toBeInTheDocument();
|
|
421
|
+
expect(await screen.findByText("Alice")).toBeInTheDocument();
|
|
422
|
+
});
|
|
423
|
+
```
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: recsys-pipeline-architect
|
|
3
|
+
description: Design composable recommendation, ranking, and feed pipelines using the six-stage Source→Hydrator→Filter→Scorer→Selector→SideEffect framework popularized by xAI's open-sourced For You algorithm. Use this skill whenever the user is building any system that picks "the top K items for a (user, context)" — social feeds, content CMSs, RAG rerankers, task prioritizers, notification triage, search reranking, ad ranking.
|
|
4
|
+
origin: community
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# recsys-pipeline-architect
|
|
8
|
+
|
|
9
|
+
A spec-and-scaffold skill for building composable recommendation, ranking, and feed pipelines. It encodes the **six-stage pattern** — Source → Hydrator → Filter → Scorer → Selector → SideEffect — popularized by xAI's open-sourced [For You algorithm](https://github.com/xai-org/x-algorithm) (Apache 2.0). This skill is an independent reimplementation of the pattern (MIT) — no code copied from the original.
|
|
10
|
+
|
|
11
|
+
Upstream: <https://github.com/mturac/recsys-pipeline-architect>
|
|
12
|
+
|
|
13
|
+
## When to Use
|
|
14
|
+
|
|
15
|
+
- User wants to build any system that picks "the top K items for a user/context"
|
|
16
|
+
- User asks "how should I rank X" or describes a feed/personalization problem
|
|
17
|
+
- User has a scoring function and needs the pipeline plumbing around it
|
|
18
|
+
- User wants to migrate from a single relevance score to multi-action prediction with tunable weights
|
|
19
|
+
- User is wrapping an LLM/ML scorer and needs filters, hydrators, side-effects, and a runnable scaffold in their stack (TypeScript / Go / Python)
|
|
20
|
+
- Triggers: "recommendation system", "feed algorithm", "ranking pipeline", "for you feed", "candidate pipeline", "content recommender", "pipeline architecture for recsys", "RAG retrieval reranker"
|
|
21
|
+
|
|
22
|
+
## When NOT to Use
|
|
23
|
+
|
|
24
|
+
- Model architecture work (transformer design, two-tower retrieval, embedding training) — this skill is plumbing *around* the model, not the model itself
|
|
25
|
+
- Pure ML training pipelines — the scoring function is the user's responsibility
|
|
26
|
+
- Operating a deployed pipeline (monitoring, autoscaling) — out of scope
|
|
27
|
+
|
|
28
|
+
## The six-stage framework
|
|
29
|
+
|
|
30
|
+
| # | Stage | Job | Parallel? |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| 1 | **Source** | Fetch candidates from one or more origins | Yes — multiple sources run in parallel |
|
|
33
|
+
| 2 | **Hydrator** | Enrich each candidate with metadata needed for filtering and scoring | Yes — independent hydrators run in parallel |
|
|
34
|
+
| 3 | **Filter** | Drop candidates that should never be shown (blocked, expired, duplicate, ineligible) | Sequential — each filter sees fewer items |
|
|
35
|
+
| 4 | **Scorer** | Assign each surviving candidate one or more scores | Sequential — later scorers see earlier scores |
|
|
36
|
+
| 5 | **Selector** | Sort by final score, return top K | Single op |
|
|
37
|
+
| 6 | **SideEffect** | Cache served IDs, log impressions, emit events, update counters | Async — must never block the response |
|
|
38
|
+
|
|
39
|
+
### Why this exact order
|
|
40
|
+
|
|
41
|
+
- Sources before hydration: know what candidates exist before paying to enrich them
|
|
42
|
+
- Hydration before filtering: many filters need metadata the source did not provide
|
|
43
|
+
- Filtering before scoring: scoring is the expensive stage; drop the ineligible first
|
|
44
|
+
- Scorer chain (not single scorer): real systems compose ML scoring + diversity reranking + business rules
|
|
45
|
+
- Selector after scoring: keeps scoring deterministic and cacheable
|
|
46
|
+
- SideEffects last and async: side effects must never block the user response
|
|
47
|
+
|
|
48
|
+
## Workflow when invoked
|
|
49
|
+
|
|
50
|
+
Walk the user through these eight steps:
|
|
51
|
+
|
|
52
|
+
1. **Clarify the use case** (one round, three questions): items being ranked? input context? language/runtime?
|
|
53
|
+
2. **Identify the candidate sources**: usually in-network (followed/owned/subscribed) + out-of-network (ML retrieval / trending / similar-to-liked)
|
|
54
|
+
3. **List required hydrations**: for each filter and scorer, what data does it need that the source did not provide?
|
|
55
|
+
4. **List the filters**: duplicate, self, age, block/mute, previously-served, eligibility. Order matters — cheap before expensive.
|
|
56
|
+
5. **Design the scorer chain**: primary (ML) → combiner (multi-action with weights) → diversity → business rules
|
|
57
|
+
6. **Selector**: sort descending by final score, take top K (or stratified mix for in-network/out-of-network)
|
|
58
|
+
7. **SideEffects**: cache served IDs, emit impression events, update counters, log analytics — all fire-and-forget
|
|
59
|
+
8. **Generate the scaffold** in the user's stack
|
|
60
|
+
|
|
61
|
+
## Key trade-offs to surface (don't default silently)
|
|
62
|
+
|
|
63
|
+
### 1. Single score vs multi-action prediction
|
|
64
|
+
|
|
65
|
+
- **Single score**: train one model to predict relevance. To change behavior → retrain.
|
|
66
|
+
- **Multi-action**: predict `P(action)` for many actions (read, like, share, skip, report), combine with weights at serving time. To change behavior → change weights. No retraining.
|
|
67
|
+
|
|
68
|
+
The X For You system uses multi-action with both positive and negative weights. Recommend multi-action when the user expects to tune frequently.
|
|
69
|
+
|
|
70
|
+
### 2. Candidate isolation in scoring
|
|
71
|
+
|
|
72
|
+
- **Isolated**: each candidate scored independently. Deterministic, cacheable.
|
|
73
|
+
- **Joint**: candidates attend to each other during scoring (e.g., transformer over batch). More expressive but non-deterministic across batches.
|
|
74
|
+
|
|
75
|
+
Default to isolation. Joint only when there's a specific reason (e.g., explicit batch-aware diversity).
|
|
76
|
+
|
|
77
|
+
### 3. Online vs offline
|
|
78
|
+
|
|
79
|
+
- **Request-time (online)**: pipeline runs on each request. Latency budget: 100–300ms. Default.
|
|
80
|
+
- **Pre-computed (offline batch)**: pipeline runs periodically, results cached. Lower latency, lower freshness.
|
|
81
|
+
- **Hybrid**: candidate retrieval offline, ranking online.
|
|
82
|
+
|
|
83
|
+
## Hard rules
|
|
84
|
+
|
|
85
|
+
1. **Do not invent benchmark numbers.** "How much faster?" → "depends on workload, run it yourself."
|
|
86
|
+
2. **Attribution discipline.** When the pattern is referenced, attribute as "popularized by xAI's open-sourced For You algorithm" / `github.com/xai-org/x-algorithm` (Apache 2.0).
|
|
87
|
+
3. **No trademark use.** Do not name the user's artifact "X-like" or use "For You" branding. Pattern is free; brand is not. Suggested naming: "candidate pipeline", "feed pipeline", "ranking pipeline", "recsys pipeline".
|
|
88
|
+
4. **Surface trade-offs.** Multi-action vs single, isolation vs joint, online vs offline — never default silently.
|
|
89
|
+
5. **The generated scaffold must run.** No pseudocode passing as code.
|
|
90
|
+
6. **Filter order matters.** Cheap before expensive. Universal before user-specific.
|
|
91
|
+
7. **Side effects never block.** Wrap in fire-and-forget patterns (goroutines / promises without await / asyncio tasks).
|
|
92
|
+
|
|
93
|
+
## Anti-Patterns
|
|
94
|
+
|
|
95
|
+
- Scoring before filtering (wastes compute on candidates that will be dropped anyway)
|
|
96
|
+
- Synchronous side effects (cache writes / impression emits blocking the response)
|
|
97
|
+
- A single "relevance" score when the product needs to tune for multiple objectives (engagement vs safety vs diversity vs ads)
|
|
98
|
+
- Joint scoring as default (non-deterministic, harder to cache, doesn't compose with reranking stages)
|
|
99
|
+
- Generating pseudocode "for illustration" — the scaffold must actually run
|
|
100
|
+
|
|
101
|
+
## Upstream contents
|
|
102
|
+
|
|
103
|
+
The upstream repository at <https://github.com/mturac/recsys-pipeline-architect> ships:
|
|
104
|
+
|
|
105
|
+
- Full `SKILL.md` with the complete 8-step workflow
|
|
106
|
+
- 5 load-on-demand reference docs: interfaces in 4 languages (TS/Go/Python/Rust), multi-action scoring pattern, candidate isolation, filter cookbook (12 patterns), scorer cookbook (weighted sum, MMR, diversity penalty, position debiasing)
|
|
107
|
+
- 3 runnable example scaffolds, every one green on its test suite:
|
|
108
|
+
- Strapi v5 plugin (TypeScript / Jest — 3/3 pass)
|
|
109
|
+
- Zentra-compatible pipeline (Go with generics — 3/3 pass)
|
|
110
|
+
- PMAI task prioritizer (Python / FastAPI / pytest — 3/3 pass)
|
|
111
|
+
- v0.1.0 release tagged
|
|
112
|
+
- MIT license; pattern attributed to xAI X For You algorithm (Apache 2.0)
|
|
113
|
+
|
|
114
|
+
Install via skills.sh: `npx skills add mturac/recsys-pipeline-architect`
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: recursive-decision-ledger
|
|
3
|
+
description: Use when the user asks for repeated rollouts, marked decision processes, high-dimensional search, stochastic optimization, local-optima exploration, ensemble comparison, or recursive reasoning with a visible evidence trail.
|
|
4
|
+
origin: ECC
|
|
5
|
+
tools: Read, Write, Edit, Bash, Grep, Glob
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Recursive Decision Ledger
|
|
9
|
+
|
|
10
|
+
Use this skill when the user is trying to force deeper computation through
|
|
11
|
+
repeated rollouts or "Prime Gauss" style recursive prompting. Preserve the useful
|
|
12
|
+
part: repeated trials, prior memory, fresh information, and explicit marks.
|
|
13
|
+
Remove the unsafe part: pretending the loop proves certainty.
|
|
14
|
+
|
|
15
|
+
## Ledger Contract
|
|
16
|
+
|
|
17
|
+
Every rollout should record:
|
|
18
|
+
|
|
19
|
+
- rollout id and timestamp;
|
|
20
|
+
- prior accepted winner and prior watchlist;
|
|
21
|
+
- fresh information ingested;
|
|
22
|
+
- search space size;
|
|
23
|
+
- model families or heuristics used;
|
|
24
|
+
- trial count and effective trial count;
|
|
25
|
+
- top candidates;
|
|
26
|
+
- decision marks;
|
|
27
|
+
- coherence marks against the prior ledger;
|
|
28
|
+
- promotion gate result.
|
|
29
|
+
|
|
30
|
+
Prefer JSONL for append-only ledgers and Markdown for human summaries.
|
|
31
|
+
|
|
32
|
+
## Rollout Loop
|
|
33
|
+
|
|
34
|
+
1. Load the prior ledger.
|
|
35
|
+
2. Capture new information at time-step zero.
|
|
36
|
+
3. Run the bounded search.
|
|
37
|
+
4. Mark each candidate: accept, watch, reject, decay watch, or needs replay.
|
|
38
|
+
5. Compare winners against prior winners and latest marked rollout.
|
|
39
|
+
6. Downgrade candidates when drift, tail risk, stale data, or failed replay
|
|
40
|
+
invalidates the previous mark.
|
|
41
|
+
7. Append artifacts before summarizing.
|
|
42
|
+
|
|
43
|
+
## Coherence Mark
|
|
44
|
+
|
|
45
|
+
Include a compact coherence mark:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
Ensemble matches prior winner: true
|
|
49
|
+
Recursive matches prior winner: false
|
|
50
|
+
Latest rollout match: true
|
|
51
|
+
Live promotion allowed: false
|
|
52
|
+
Reason: replay and freshness gates not satisfied
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Promotion Rules
|
|
56
|
+
|
|
57
|
+
For trading, capital allocation, production deploys, migrations, or destructive
|
|
58
|
+
ops, recursive confidence is not approval.
|
|
59
|
+
|
|
60
|
+
Default to paper, dry-run, read-only, preview, or staged mode unless the user
|
|
61
|
+
explicitly approves the live action and the repo/service gate supports it.
|
|
62
|
+
|
|
63
|
+
Promote only when:
|
|
64
|
+
|
|
65
|
+
- the candidate beats the prior accepted winner on the chosen metric;
|
|
66
|
+
- correctness and replay checks pass;
|
|
67
|
+
- risk limits are explicit;
|
|
68
|
+
- the evidence is durable;
|
|
69
|
+
- the user has approved the live step when needed.
|
|
70
|
+
|
|
71
|
+
## Summary Shape
|
|
72
|
+
|
|
73
|
+
Lead with the decision, not the drama:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
Rollout 15 complete. The prior winner still holds, but edge deteriorated 17%.
|
|
77
|
+
Status: watch, not live. Next gate: 20 replay fills with fresh orderbook age
|
|
78
|
+
below threshold.
|
|
79
|
+
```
|