{"owner":"langflow-ai","repo":"langflow","hasSkills":true,"totalSkillsCount":44,"totalTokensCount":38958,"categories":["anthropic-skill","root-instruction","claude-rule","project-spec","cursor-rule","subagent-persona"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/frontend-query-mutation/SKILL.md",".agents/skills/frontend-i18n/SKILL.md",".agents/skills/frontend-code-review/SKILL.md",".agents/skills/backend-code-review/SKILL.md",".agents/skills/frontend-testing/SKILL.md",".agents/skills/ibm-a11y-level1-audit/SKILL.md",".agents/skills/ibm-a11y-route-scan/SKILL.md",".agents/skills/component-refactoring/SKILL.md",".agents/skills/ibm-a11y-testing-guide/SKILL.md","AGENTS.md",".agents/skills/e2e-testing/SKILL.md",".agents/skills/ibm-a11y-pr-remediation/SKILL.md","CLAUDE.md","DESIGN.md",".cursor/rules/docs_development.mdc","docs/agents/ANTI-PATTERNS.md","docs/agents/ARCHITECTURE.md","docs/agents/COMPONENTS.md","docs/agents/CONTRACTS.md","docs/agents/PHILOSOPHY.md","docs/agents/TESTING.md",".agents/skills/backend-code-review/references/architecture-rule.md",".agents/skills/backend-code-review/references/db-schema-rule.md",".agents/skills/backend-code-review/references/repositories-rule.md",".agents/skills/backend-code-review/references/sqlalchemy-rule.md",".agents/skills/component-refactoring/references/complexity-patterns.md",".agents/skills/component-refactoring/references/component-splitting.md",".agents/skills/component-refactoring/references/hook-extraction.md",".agents/skills/e2e-testing/references/fixtures.md",".agents/skills/e2e-testing/references/helpers.md",".agents/skills/e2e-testing/references/selectors.md",".agents/skills/frontend-code-review/references/business-logic.md",".agents/skills/frontend-code-review/references/code-quality.md",".agents/skills/frontend-code-review/references/performance.md",".agents/skills/frontend-query-mutation/references/query-patterns.md",".agents/skills/frontend-query-mutation/references/runtime-rules.md",".agents/skills/frontend-testing/references/async-testing.md",".agents/skills/frontend-testing/references/checklist.md",".agents/skills/frontend-testing/references/common-patterns.md",".agents/skills/frontend-testing/references/domain-components.md",".agents/skills/frontend-testing/references/mocking.md",".agents/skills/frontend-testing/references/workflow.md",".agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md",".agents/skills/ibm-a11y-level1-audit/references/report-template.md"],"skills":{".agents/skills/frontend-query-mutation/SKILL.md":"---\nname: frontend-query-mutation\ndescription: Guide for implementing Langflow frontend query and mutation patterns with Axios and TanStack React Query v5. Trigger when creating or updating API hooks in controllers/API/queries, consuming UseRequestProcessor in components, deciding whether to use useQuery or useMutation, handling conditional queries, cache invalidation, mutation error handling, or migrating legacy API calls to the query hook pattern.\n---\n\n# Frontend Query & Mutation\n\n## Intent\n\n- Keep API hooks in `controllers/API/queries/{domain}/` as the organized source of truth.\n- Use `UseRequestProcessor` for consistent retry, invalidation, and error handling.\n- Use Axios `api` instance for all HTTP calls (never raw `fetch` for REST).\n- Use `performStreamingRequest()` for streaming operations (build, chat).\n- Keep cache invalidation in mutation `onSettled` callbacks.\n\n## Architecture Overview\n\nLangflow uses **Axios + TanStack React Query v5** for data fetching. There is no oRPC or contract layer. The architecture is:\n\n```\nComponent\n  -> API hook (controllers/API/queries/{domain}/use-{verb}-{resource}.ts)\n    -> UseRequestProcessor (controllers/API/services/request-processor.ts)\n      -> useQuery / useMutation (TanStack React Query)\n        -> queryFn / mutationFn\n          -> api.get/post/patch/delete (Axios instance from controllers/API/api.tsx)\n```\n\n### Key Files\n\n| File | Purpose |\n|------|---------|\n| `controllers/API/api.tsx` | Axios instance, `ApiInterceptor` component, `performStreamingRequest()` |\n| `controllers/API/services/request-processor.ts` | `UseRequestProcessor` hook wrapping `useQuery`/`useMutation` with retry defaults |\n| `controllers/API/helpers/constants.ts` | URL constants (`URLs`) and `getURL()` helper |\n| `controllers/API/queries/{domain}/` | Domain-organized query and mutation hooks |\n| `types/api/index.ts` | `useQueryFunctionType`, `useMutationFunctionType` type helpers |\n| `stores/` | Zustand stores for client-side state |\n\n## Workflow\n\n1. Identify the change surface.\n   - Read `references/query-patterns.md` for hook structure, naming, URL patterns, and query/mutation call-site shape.\n   - Read `references/runtime-rules.md` for conditional queries, invalidation, error handling, and streaming.\n   - Read both references when a task spans hook creation and runtime behavior.\n2. Implement the hook following existing patterns.\n   - Use `UseRequestProcessor` for `query()` and `mutate()` wrappers.\n   - Use the `api` Axios instance for all HTTP calls.\n   - Use `getURL()` for constructing API paths.\n   - Type the hook with `useQueryFunctionType` or `useMutationFunctionType`.\n3. Preserve Langflow conventions.\n   - Query keys are arrays with the hook name: `[\"useGetFlows\"]`.\n   - Mutations invalidate related queries in `onSettled`.\n   - Zustand store updates happen in the query/mutation function, not in components.\n   - `UseRequestProcessor` provides default retry (5 for queries, 3 for mutations) with exponential backoff.\n\n## Files Commonly Touched\n\n- `controllers/API/queries/{domain}/use-get-*.ts` (query hooks)\n- `controllers/API/queries/{domain}/use-post-*.ts` (create mutations)\n- `controllers/API/queries/{domain}/use-patch-*.ts` (update mutations)\n- `controllers/API/queries/{domain}/use-delete-*.ts` (delete mutations)\n- `controllers/API/helpers/constants.ts` (URL constants)\n- `types/api/index.ts` (API type definitions)\n- `stores/` (Zustand store updates triggered by queries)\n\n## References\n\n- Use `references/query-patterns.md` for hook structure, naming conventions, directory layout, and anti-patterns.\n- Use `references/runtime-rules.md` for conditional queries, invalidation, `mutate` versus `mutateAsync`, error handling, and streaming requests.\n\nTreat this skill as the single query and mutation entry point for Langflow frontend work. Keep detailed rules in the reference files instead of duplicating them in project docs.\n",".agents/skills/frontend-i18n/SKILL.md":"---\nname: frontend-i18n\ndescription: Add, change, or review user-facing text in the Langflow frontend using the i18n system (i18next / react-i18next). Use whenever a change adds or edits UI strings — labels, buttons, tooltips, modals, toasts, error messages, empty states — or when reviewing a diff that contains user-facing text. Every user-facing string must go through the translation system and every new key must exist in ALL locale files. Do NOT use for backend strings, log messages, or code identifiers.\n---\n\n# Frontend i18n\n\nThe Langflow frontend is internationalized with **i18next + react-i18next**. Locales live in `src/frontend/src/locales/` — currently `en`, `de`, `es`, `fr`, `ja`, `pt`, `zh-Hans`. A hardcoded user-facing string, or a key missing from one locale, ships broken UI for part of the user base.\n\n## The two rules\n\n1. **Every user-facing string goes through `t(...)`** — never hardcoded JSX text for labels, buttons, tooltips, modals, toasts, errors, placeholders, `aria-label`s, or empty states.\n2. **Every new key is added to ALL locale files in the same PR** (`en.json`, `de.json`, `es.json`, `fr.json`, `ja.json`, `pt.json`, `zh-Hans.json`). `fallbackLng: \"en\"` means a missing key silently shows English — it won't crash, which is exactly why reviews must catch it.\n\n## How it works here\n\n- Config: `src/frontend/src/i18n.ts` — a custom i18next instance. `en` is bundled statically; other languages are **lazy-loaded** by `loadLanguage()` via dynamic import. Language preference comes from `localStorage.getItem(\"languagePreference\")`, normalized (e.g. `zh-CN` → `zh-Hans`, unknown → `en`).\n- Keys are **flat, dot-namespaced by feature**: `deleteModal.title`, `errors.fileTooLarge`, `crash.restartButton`. Follow the existing namespace of the area you're touching; create a new prefix only for a genuinely new surface.\n- Interpolation uses `{{variable}}` in the string and an options object in the call.\n\n## Adding a string (the pattern)\n\n```tsx\nimport { useTranslation } from \"react-i18next\";\n\nconst { t } = useTranslation();\n\n<span>{t(\"deleteModal.title\")}</span>\n<p>{t(\"errors.fileTooLarge\", { maxSizeMB: \"10MB\" })}</p>\n```\n\nIn `locales/en.json` (and **every** other locale file):\n\n```json\n\"errors.fileTooLarge\": \"The file size is too large. Please select a file smaller than {{maxSizeMB}}.\"\n```\n\nFor the non-English locales, write a real translation when confident; if not, add the key with the English value and flag it in the PR — a present-but-untranslated key is visible and searchable, a missing key is invisible.\n\n## Rules that prevent real bugs\n\n- **Don't concatenate translated fragments** (`t(\"a\") + name + t(\"b\")`) — word order differs across languages. Use one key with interpolation: `t(\"greeting\", { name })`.\n- **Don't put JSX/HTML inside translation strings**; compose in JSX around `t()` calls (see how `crash.descriptionBefore` / `crash.githubIssues` / `crash.descriptionAfter` split around a link).\n- **Plurals**: use i18next plural forms (`_one` / `_other` suffixes), not `count === 1 ? ... : ...`.\n- **Keys are code**: renaming or deleting a key means updating **all seven** locale files — an orphan key in one file is dead weight; a rename missed in one file is a regression.\n- Dates/numbers/currency: format with `Intl.*` using the active locale, never hardcoded formats.\n\n## Review checklist (apply to any diff with UI text)\n\n- [ ] No hardcoded user-facing string in JSX (including `placeholder`, `title`, `aria-label`, toast/error text).\n- [ ] Every new key exists in **all** locale files (grep the key across `src/frontend/src/locales/*.json`).\n- [ ] No concatenation of translated fragments; interpolation used instead.\n- [ ] Removed/renamed keys cleaned up in all locales (no orphans).\n- [ ] Key follows the existing dot-namespace of the feature area.\n\nQuick check for a key across locales:\n\n```bash\nfor f in src/frontend/src/locales/*.json; do grep -L '\"my.new.key\"' \"$f\"; done\n```\n\n(prints the locale files where the key is **missing** — should print nothing)\n",".agents/skills/frontend-code-review/SKILL.md":"---\nname: frontend-code-review\ndescription: \"Review frontend code (.tsx, .ts, .js files) for quality, performance, and correctness against Langflow's frontend conventions. Supports pending-change reviews and file-targeted reviews.\"\n---\n\n# Frontend Code Review\n\n## When to use this skill\n\nUse this skill whenever the user asks to **review, analyze, or improve** frontend code (`.tsx`, `.ts`, `.js` files) under the `src/frontend/` directory. Supports the following review modes:\n\n1. **Pending-change review** -- inspect staged or working-tree files slated for commit and flag checklist violations before submission.\n2. **File-targeted review** -- review the specific file(s) the user names and report the relevant checklist findings.\n\nDo NOT use this skill when:\n\n- The request is about backend code (`.py` files under `src/backend/`).\n- The user is not asking for a review/analysis/improvement of frontend code.\n- The scope is outside `src/frontend/` (unless the user explicitly asks to review frontend-related changes elsewhere).\n\n## How to use this skill\n\nFollow these steps when using this skill:\n\n1. **Identify the review mode** (pending-change vs file-targeted) based on the user's input. Keep the scope tight: review only what the user provided or explicitly referenced.\n2. Follow the rules defined in the **Checklist** to perform the review. If no checklist rule matches, apply **General Review Rules** as a fallback.\n3. Compose the final output strictly following the **Required Output Format**.\n\nNotes when using this skill:\n\n- Always include actionable fixes or suggestions (including possible code snippets).\n- Use `File:Line` references when a file path and line numbers are available; otherwise, use the most specific identifier you can.\n- The Langflow frontend uses React 19, TypeScript 5.4, Vite 7 with SWC, Zustand for state management, TanStack React Query for server state, @xyflow/react v12 for graph visualization, Radix UI + shadcn-ui components, Tailwind CSS v3, and Biome for linting/formatting.\n\n## Checklist\n\n- **Code quality**: For any reviewed file, follow [references/code-quality.md](references/code-quality.md) to check styling conventions, TypeScript usage, Biome compliance, and component patterns.\n- **Performance**: If the review scope involves React components, hooks, Zustand stores, React Query usage, or @xyflow/react node rendering, follow [references/performance.md](references/performance.md) to check for re-render issues, memoization, and data flow patterns.\n- **Business logic**: If the review scope involves custom nodes (GenericNode), flow state, API calls, the component system, global variables, or the inspection panel, follow [references/business-logic.md](references/business-logic.md) to check for Langflow-specific correctness.\n- **i18n**: If the diff adds or changes user-facing text (labels, buttons, tooltips, modals, toasts, errors, placeholders, aria-labels), follow the [frontend-i18n skill](../frontend-i18n/SKILL.md): no hardcoded UI strings — everything through `t(...)` — and every new key present in **all** locale files (`src/frontend/src/locales/*.json`).\n\n## General Review Rules\n\n### 1. Security Review\n\nCheck for:\n- XSS vulnerabilities (dangerouslySetInnerHTML, unescaped user input)\n- Sensitive data exposure in client-side code\n- Insecure direct object references in API calls\n- Hardcoded secrets, tokens, or API keys\n\n### 2. Accessibility Review\n\nCheck for:\n- Missing aria labels on interactive elements\n- Keyboard navigation support\n- Proper use of semantic HTML elements\n- Color contrast issues in custom styling\n\n### 3. Code Quality Review\n\nCheck for:\n- Code duplication (DRY violations — extract at 3+ identical usages)\n- Functions/components doing too much (SRP violations — if you need \"and\" to describe it, split it)\n- Deep nesting or complex conditionals (prefer early returns and guard clauses)\n- Magic numbers/strings without named constants\n- Poor naming: generic names (`data`, `result`, `temp`), missing verb prefixes on functions, missing `is`/`has`/`can`/`should` prefixes on booleans\n- Missing error handling or error boundaries\n- Incomplete TypeScript type coverage (no `any`, no `as any` casts)\n- Comments that explain WHAT instead of WHY\n- Commented-out code (use version control)\n- Boolean parameters that switch component behavior (use two components instead)\n- Mutable patterns where `const` or immutable alternatives exist\n- Production files exceeding ~500 lines (red flag at 600+)\n- `console.log` in production code (Biome flags this)\n\n### 4. Testing Impact Review\n\nCheck for:\n- Changes to data-testid attributes that may break E2E tests\n- Modified component interfaces that require test updates\n- New interactive elements missing data-testid attributes\n\n### 5. Pre-Commit Verification\n\nFor pending-change reviews, verify:\n- `npm run format` (Biome formatter) — zero diffs\n- `npm run lint` (Biome linter) — zero errors\n- `npm test` (Jest) — zero failures\n\n## Required Output Format\n\nWhen this skill is invoked, the response must exactly follow one of the two templates:\n\n### Template A (any findings)\n\n```markdown\n# Code Review\n\nFound <N> urgent issues that need to be fixed:\n\n## 1. <brief description of issue>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n\n### Suggested fix\n\n<brief description of suggested fix with code example>\n\n---\n\n... (repeat for each urgent issue) ...\n\nFound <M> suggestions for improvement:\n\n## 1. <brief description of suggestion>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n\n### Suggested fix\n\n<brief description of suggested fix with code example>\n\n---\n\n... (repeat for each suggestion) ...\n```\n\n- If there are no urgent issues, omit that section. If there are no suggestions, omit that section.\n- If the issue count exceeds 10, summarize as \"10+ urgent issues\" or \"10+ suggestions\" and output only the first 10 items.\n- Do not compress the blank lines between sections; keep them as-is for readability.\n- If Template A is used (there are issues to fix) and at least one issue requires code changes, append a brief follow-up question after the structured output asking whether the user wants the suggested fixes applied. For example: \"Would you like me to apply the suggested fixes to address these issues?\"\n\n### Template B (no issues)\n\n```markdown\n## Code Review\n\nNo issues found.\n```\n",".agents/skills/backend-code-review/SKILL.md":"---\nname: backend-code-review\ndescription: Review backend code for quality, security, maintainability, and best practices based on established checklist rules. Use when the user requests a review, analysis, or improvement of backend files (e.g., `.py`) under the `src/backend/` directory. Do NOT use for frontend files (e.g., `.tsx`, `.ts`, `.js`). Supports pending-change review, code snippets review, and file-focused review.\n---\n\n# Backend Code Review\n\n## When to use this skill\n\nUse this skill whenever the user asks to **review, analyze, or improve** backend code (e.g., `.py`) under the `src/backend/` directory. Supports the following review modes:\n\n- **Pending-change review**: when the user asks to review current changes (inspect staged/working-tree files slated for commit to get the changes).\n- **Code snippets review**: when the user pastes code snippets (e.g., a function/class/module excerpt) into the chat and asks for a review.\n- **File-focused review**: when the user points to specific files and asks for a review of those files (one file or a small, explicit set of files, e.g., `src/backend/base/langflow/api/v1/flows.py`).\n\nDo NOT use this skill when:\n\n- The request is about frontend code or UI (e.g., `.tsx`, `.ts`, `.js`, `src/frontend/`).\n- The user is not asking for a review/analysis/improvement of backend code.\n- The scope is not under `src/backend/` (unless the user explicitly asks to review backend-related changes outside `src/backend/`).\n\n## How to use this skill\n\nFollow these steps when using this skill:\n\n1. **Identify the review mode** (pending-change vs snippet vs file-focused) based on the user's input. Keep the scope tight: review only what the user provided or explicitly referenced.\n2. Follow the rules defined in **Checklist** to perform the review. If no Checklist rule matches, apply **General Review Rules** as a fallback to perform the best-effort review.\n3. Compose the final output strictly following the **Required Output Format**.\n\nNotes when using this skill:\n- Always include actionable fixes or suggestions (including possible code snippets).\n- Use best-effort `File:Line` references when a file path and line numbers are available; otherwise, use the most specific identifier you can.\n\n## Checklist\n\n- db schema design: if the review scope includes code/files under `src/backend/base/langflow/services/database/models/` or Alembic migrations under `src/backend/base/langflow/alembic/versions/`, follow [references/db-schema-rule.md](references/db-schema-rule.md) to perform the review\n- architecture: if the review scope involves route/service/model layering, dependency direction, or moving responsibilities across modules, follow [references/architecture-rule.md](references/architecture-rule.md) to perform the review\n- service abstraction: if the review scope contains table/model operations (e.g., `select(...)`, `session.execute(...)`, joins, CRUD) and is not already inside a service under `src/backend/base/langflow/services/`, follow [references/repositories-rule.md](references/repositories-rule.md) to perform the review\n- sqlalchemy patterns: if the review scope involves SQLAlchemy/SQLModel session/query usage, db transaction/crud usage, `session_scope()` usage, or raw SQL usage, follow [references/sqlalchemy-rule.md](references/sqlalchemy-rule.md) to perform the review\n\n## General Review Rules\n\n### 1. Security Review\n\nCheck for:\n- SQL injection vulnerabilities (especially raw `text()` queries with string interpolation). Consequence: attacker can read/modify/delete any data in the database.\n- Server-Side Request Forgery (SSRF) in component HTTP calls. Consequence: attacker uses the server to scan internal networks or access cloud metadata endpoints.\n- Command injection (especially in subprocess or shell-executing components). Consequence: attacker gains shell access to the server.\n- Insecure deserialization (pickle, yaml.load without SafeLoader). Consequence: arbitrary code execution on the server.\n- Hardcoded secrets/credentials. Consequence: secrets leak via git history and are impossible to fully revoke.\n- Improper authentication/authorization (missing `CurrentActiveUser` dependency). Consequence: unauthenticated users can access protected endpoints.\n- Insecure direct object references (missing `user_id` scoping on queries). Consequence: user A can read/modify user B's flows, variables, API keys.\n- Path traversal in file storage operations. Consequence: attacker reads arbitrary server files (e.g., `/etc/passwd`, `.env`).\n\n### 2. Performance Review\n\nCheck for:\n- N+1 queries (especially in loops calling `session.execute()`). Consequence: 100 flows = 101 DB queries instead of 2; page load goes from 50ms to 5s.\n- Missing database indexes on frequently queried columns. Consequence: full table scans on large datasets; queries degrade from O(log n) to O(n).\n- Memory leaks (unbounded caches, retained references in long-lived services). Consequence: server OOM after hours of operation; pods restart in production.\n- Blocking operations in async code (`time.sleep()`, synchronous I/O, CPU-bound work without `run_in_executor`). Consequence: entire event loop stalls; all concurrent requests hang until the blocking call completes.\n- Missing caching opportunities for expensive computations. Consequence: repeated computation of the same result on every request.\n- Large result sets loaded entirely into memory without pagination. Consequence: memory spike + slow response when user has 10K+ flows.\n\n### 3. Code Quality Review\n\nCheck for:\n- Code forward compatibility with Python 3.10-3.13\n- Code duplication (DRY violations — extract when the *exact same business rule* is duplicated in 3+ places)\n- Functions doing too much (SRP violations — if you need \"and\" to describe it, split it)\n- Deep nesting / complex conditionals (prefer early returns and guard clauses)\n- Magic numbers/strings (extract to named constants or enums)\n- Poor naming: unclear abbreviations, misleading names, generic names (`data`, `result`, `obj`, `temp`). Functions should use verbs (`get`, `create`, `validate`). Booleans should use prefixes (`is_`, `has_`, `can_`, `should_`).\n- Missing error handling (bare `except`, swallowed exceptions, silent failures)\n- Incomplete type coverage (use strong typing, avoid `Any` where a concrete type is known)\n- Use Python 3.10+ union syntax (`X | Y` not `Union[X, Y]`, `X | None` not `Optional[X]`)\n- Use `TYPE_CHECKING` guard for imports only needed for type annotations (prevents circular imports)\n- Use `Annotated[Type, Depends(...)]` with project aliases (`CurrentActiveUser`, `DbSession`, `DbSessionReadOnly`) for FastAPI DI\n- Google-style docstrings (enforced by Ruff): `Args:`, `Returns:`, `Raises:` sections for public functions\n- Violations of SOLID principles\n- YAGNI violations (code that anticipates future needs without a present requirement)\n- Line length exceeding 120 characters (project Ruff config)\n- Comments that explain WHAT instead of WHY (comments should only explain reasoning, not restate code)\n- Commented-out code (use version control instead)\n- Boolean parameters that switch function behavior (split into two named functions instead)\n- Mutable shared state where immutable alternatives exist (prefer returning new objects over mutation)\n\n### 4. File Structure Review\n\nCheck for:\n- Production files exceeding ~500 lines of code (excluding imports, types, and docstrings). Files above 600 lines are a red flag and should be split by responsibility. Why: Files above 500 lines have statistically higher defect rates and take longer to review. They signal multiple responsibilities (SRP violation). In Langflow, services like `DatabaseService` that grow beyond this limit should have their CRUD operations extracted to dedicated modules.\n- Test files exceeding ~1000 lines. Split by logical grouping if exceeded.\n- No more than 5 functions with different responsibilities in a single file (per AGENTS-example.md).\n- Each file has a single reason to exist and a single reason to change (SRP).\n- No generic file names: `utils.py`, `helpers.py`, `misc.py`, `common.py` as standalone files. Why: A file named `utils.py` becomes a dumping ground for unrelated functions. Within months it has 50+ functions covering formatting, validation, parsing, and HTTP calls — violating SRP. Each function group should be in a file named after its responsibility (`formatting.py`, `validation.py`).\n\n### 5. Testing Review\n\nCheck for:\n- Missing test coverage for new code paths\n- Tests that don't test behavior (testing implementation details)\n- Flaky test patterns (time-dependent, order-dependent, external-service-dependent)\n- Proper use of `pytest.mark.asyncio` for async tests\n- Excessive mocking (prefer real integrations per project conventions)\n- Coverage target: 80% (minimum acceptable: 75%)\n- Test anti-patterns: The Liar (passes but doesn't verify claimed behavior), The Mirror (asserts exactly what code does), The Giant (50+ lines setup), The Mockery (tests only mock setup), The Inspector (coupled to implementation), The Chain Gang (depends on execution order), The Flaky (inconsistent results)\n\n**Happy path tests are the foundation but are NOT enough.** Tests MUST also challenge the code to find real defects:\n\n- **Unexpected inputs**: `None`, `\"\"`, `[]`, `{}`, `0`, `-1`, `UUID(\"00000000-0000-0000-0000-000000000000\")`\n- **Boundary values**: max length strings, exactly at the limit, one past the limit, zero items, max items\n- **Malformed data**: missing required fields, extra unexpected fields, wrong types, invalid formats\n- **Error states**: what happens when the database is down? When an external API returns 500? When the user doesn't exist?\n- **What should NOT happen**: verify that user A CANNOT access user B's flows. Verify that a deleted flow returns 404. Verify that invalid `endpoint_name` is rejected with 422.\n- **Error messages and types**: not just that it fails, but that it fails with the RIGHT exception and the RIGHT message\n- **Concurrency**: what happens when two requests try to update the same flow simultaneously?\n\n**Write tests based on REQUIREMENTS/SPEC, not on what the source code currently does.** This is how you catch bugs where the code diverges from expected behavior.\n\n**When a test fails:** first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.\n\n### 6. Observability Review\n\nCheck for:\n- Use the async logger from `lfx.log.logger` with `a`-prefixed methods (`adebug`, `ainfo`, `awarning`, `aerror`, `aexception`). Never use `print()` or stdlib `logging`.\n- Log at key decision points and boundaries, not inside tight loops\n- Include: operation name, relevant IDs, outcome (success/failure), duration if relevant\n- Correct log levels: ERROR (broken, needs attention), WARN (degraded but recoverable), INFO (significant events), DEBUG (diagnostic, off in prod)\n- **ZERO PII TOLERANCE**: Never log email addresses, user names, phone numbers, tokens, passwords. Only approved identifiers: `user_id`, `flow_id`, `session_id`\n- No `print()` statements — these go to production logs\n- Use `{e!s}` for string representation of exceptions in log messages\n\n### 7. Pre-Commit Verification\n\nFor pending-change reviews, verify the author has run:\n- `make format_backend` (Ruff formatter) — inconsistent formatting creates noisy diffs that hide real changes in code review. Format first, review second.\n- `make lint` (MyPy type checking) — type errors caught at lint time are 10x cheaper to fix than runtime crashes in production. Langflow services use duck typing via `Service` base class; MyPy catches mismatches early.\n- `make unit_tests` (pytest) — a failing test means the change breaks existing behavior. Never merge with failing tests; investigate whether the code or the test is wrong.\n\n## Required Output Format\n\nWhen this skill is invoked, the response must exactly follow one of the two templates:\n\n### Template A (any findings)\n\n```markdown\n# Code Review Summary\n\nFound <X> critical issues need to be fixed:\n\n## 🔴 Critical (Must Fix)\n\n### 1. <brief description of the issue>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<detailed explanation and references of the issue>\n\n#### Suggested Fix\n\n1. <brief description of suggested fix>\n2. <code example> (optional, omit if not applicable)\n\n---\n... (repeat for each critical issue) ...\n\nFound <Y> suggestions for improvement:\n\n## 🟡 Suggestions (Should Consider)\n\n### 1. <brief description of the suggestion>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<detailed explanation and references of the suggestion>\n\n#### Suggested Fix\n\n1. <brief description of suggested fix>\n2. <code example> (optional, omit if not applicable)\n\n---\n... (repeat for each suggestion) ...\n\nFound <Z> optional nits:\n\n## 🟢 Nits (Optional)\n### 1. <brief description of the nit>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<explanation and references of the optional nit>\n\n#### Suggested Fix\n\n- <minor suggestions>\n\n---\n... (repeat for each nits) ...\n\n## ✅ What's Good\n\n- <Positive feedback on good patterns>\n```\n\n- If there are no critical issues or suggestions or optional nits or good points, just omit that section.\n- If the issue number is more than 10, summarize as \"Found 10+ critical issues/suggestions/optional nits\" and only output the first 10 items.\n- Don't compress the blank lines between sections; keep them as-is for readability.\n- If there is any issue that requires code changes, append a brief follow-up question to ask whether the user wants to apply the fix(es) after the structured output. For example: \"Would you like me to use the Suggested fix(es) to address these issues?\"\n\n### Template B (no issues)\n\n```markdown\n## Code Review Summary\n✅ No issues found.\n```\n",".agents/skills/frontend-testing/SKILL.md":"# Frontend Testing Skill - Langflow\n\n## When to Apply\n\nActivate this skill when:\n- Writing new unit or integration tests for React components, hooks, utilities, or Zustand stores\n- Reviewing existing tests for correctness, coverage, or best practices\n- Improving test coverage for under-tested modules\n- Debugging flaky or failing tests\n- Refactoring test code for maintainability\n\n## Tech Stack\n\n| Technology | Version | Purpose |\n|---|---|---|\n| Jest | 30.x | Test runner and assertion framework |\n| ts-jest | 29.x | TypeScript transform for Jest |\n| React Testing Library | 16.x | Component rendering and DOM queries |\n| @testing-library/user-event | 14.x | Realistic user interaction simulation |\n| @testing-library/jest-dom | 6.x | Extended DOM matchers |\n| jsdom | (via jest-environment-jsdom 30.x) | Browser environment simulation |\n| React | 19.x | UI framework |\n| TypeScript | 5.4 | Type safety |\n| Zustand | 4.x | State management |\n| React Router DOM | 6.x | Client-side routing |\n| @tanstack/react-query | 5.x | Server state management |\n| Axios | 1.x | HTTP client |\n\n## Project Configuration\n\n- **Jest config**: `src/frontend/jest.config.js`\n- **Setup files**: `src/frontend/jest.setup.js` (globals/mocks) and `src/frontend/src/setupTests.ts` (DOM matchers, ResizeObserver, IntersectionObserver, matchMedia)\n- **Path alias**: `@/` maps to `<rootDir>/src/`\n- **Test match patterns**: `src/**/__tests__/**/*.{test,spec}.{ts,tsx}` and `src/**/*.{test,spec}.{ts,tsx}`\n- **Transform**: Custom `transform-import-meta.js` handles `import.meta` for Jest compatibility\n- **Global mocks** (in `jest.setup.js`): `@radix-ui/react-form`, `react-markdown`, `remark-gfm`, `remark-math`, `rehype-mathjax/browser`, `lucide-react/dynamicIconImports`, `@/components/common/genericIconComponent`, `@/icons/BotMessageSquare`, `@/stores/darkStore`, `localStorage`, `sessionStorage`, `crypto`\n\n## Key Commands\n\n```bash\n# Run all tests\nnpm test\n\n# Run a specific test file\nnpm test -- path/to/file.test.tsx\n\n# Run tests matching a pattern\nnpm test -- --testPathPattern=\"alertStore\"\n\n# Run tests in watch mode\nnpm run test:watch\n\n# Run tests with coverage\nnpm run test:coverage\n\n# Run a single test file with coverage\nnpm test -- --coverage --collectCoverageFrom='src/path/to/source.ts' path/to/__tests__/source.test.ts\n```\n\n## File Naming and Location\n\nTest files follow one of two patterns:\n\n1. **Dedicated `__tests__` directory** (preferred for components and modules):\n   ```\n   src/components/core/my-component/\n   ├── my-component.tsx\n   └── __tests__/\n       └── my-component.test.tsx\n   ```\n\n2. **Co-located test file** (acceptable for utilities and simple modules):\n   ```\n   src/utils/\n   ├── myUtil.ts\n   └── myUtil.test.ts\n   ```\n\nNaming convention: `ComponentName.test.tsx` for components, `hook-name.test.ts` for hooks, `util-name.test.ts` for utilities.\n\n**Do NOT use `.spec.tsx`** -- while technically matched, the project convention is `.test.tsx`.\n\n## Test Structure Template\n\n```tsx\nimport { render, screen } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport MyComponent from \"../MyComponent\";\n\n// Mock dependencies (use jest.mock, NOT vi.mock)\njest.mock(\"@/controllers/API/api\", () => ({\n  get: jest.fn(),\n  post: jest.fn(),\n}));\n\ndescribe(\"MyComponent\", () => {\n  beforeEach(() => {\n    jest.clearAllMocks();\n  });\n\n  describe(\"rendering\", () => {\n    it(\"should render the component with default props\", () => {\n      // Arrange\n      render(<MyComponent />);\n\n      // Act - (none for render test)\n\n      // Assert\n      expect(screen.getByRole(\"button\", { name: /submit/i })).toBeInTheDocument();\n    });\n  });\n\n  describe(\"user interactions\", () => {\n    it(\"should call onSubmit when the form is submitted\", async () => {\n      // Arrange\n      const user = userEvent.setup();\n      const onSubmit = jest.fn();\n      render(<MyComponent onSubmit={onSubmit} />);\n\n      // Act\n      await user.click(screen.getByRole(\"button\", { name: /submit/i }));\n\n      // Assert\n      expect(onSubmit).toHaveBeenCalledTimes(1);\n    });\n  });\n});\n```\n\n## Incremental Testing Workflow\n\nWhen testing a directory with multiple files, follow this order:\n\n1. **Identify all source files** in the target directory\n2. **Order by complexity** (simplest first):\n   - Pure utility functions (no React, no side effects)\n   - Constants and configuration objects\n   - Custom hooks (no UI rendering)\n   - Simple presentational components (no state, no side effects)\n   - Stateful components with local state\n   - Components using Zustand stores\n   - Components with API calls or complex async behavior\n   - Integration-level components that compose many children\n3. **For each file**:\n   a. Read the source file completely\n   b. Identify all exported functions, components, and types\n   c. Write tests covering all branches and edge cases\n   d. Run the tests and fix any failures\n   e. Check coverage and add tests for uncovered lines\n   f. Move to the next file\n4. **Run full directory coverage** at the end to verify\n\n## Complexity-Based Test Ordering\n\nWithin a single test file, order test cases from simplest to most complex:\n\n1. Default rendering / initial state\n2. Props variations and conditional rendering\n3. User interactions (clicks, typing, form submission)\n4. Async operations (API calls, timers)\n5. Error states and edge cases\n6. Integration with stores or context\n7. Cleanup and unmount behavior\n\n## Core Principles\n\n### Arrange-Act-Assert (AAA)\nEvery test should have a clear three-phase structure. Use blank lines to separate each phase for readability.\n\n### Black-Box Testing\nTest the component from the user's perspective. Query by role, label, text, or `data-testid` -- never by CSS class, internal state variable, or implementation detail.\n\n### Single Behavior Per Test\nEach `it()` block should verify exactly one behavior. If you need to write \"and\" in the test name, split it into two tests.\n\n### Semantic Test Names\nUse descriptive names that explain the expected behavior:\n- Good: `\"should disable the submit button when the form is invalid\"`\n- Bad: `\"button test\"` or `\"test 1\"`\n\nFormat: `\"should [expected behavior] when [condition]\"`\n\n## Required Test Scenarios\n\nFor every component, cover at minimum:\n\n### Rendering\n- Default render with no optional props\n- Render with all optional props provided\n- Conditional rendering branches (if/else in JSX)\n\n### Props and State\n- Each prop variation that changes rendered output\n- Default prop values\n- State transitions triggered by user actions\n\n### User Interactions\n- Click handlers\n- Form input and submission\n- Keyboard navigation (if applicable)\n- Hover/focus states (if applicable)\n\n### Challenge Tests (MANDATORY — not optional)\n\n**Happy path tests alone are NOT enough.** They only confirm the code works when everything is perfect. Real bugs hide in the cracks. You MUST write tests that actively TRY TO BREAK the code:\n\n**Unexpected inputs:**\n- `null`, `undefined`, `\"\"`, `[]`, `{}`, `0`, `-1`, `NaN`, `Infinity`\n- What happens when a required prop is missing?\n- What happens when data from the API comes back with missing fields?\n\n**Boundary values:**\n- Max length strings (paste 10,000 chars in an input)\n- Exactly at the limit, one past the limit\n- Zero items, one item, maximum items\n- First page, last page, out-of-range page\n\n**Malformed data:**\n- API returns `{ data: null }` instead of `{ data: [] }`\n- JSON with extra unexpected fields\n- Dates in wrong format, numbers as strings\n\n**Error states:**\n- Network failure (API rejects with 500)\n- Authentication expired mid-action (401)\n- Resource not found (404)\n- Permission denied (403)\n- Timeout\n\n**What should NOT happen:**\n- Verify that deleting a flow does NOT delete flows from other users\n- Verify that a read-only user CANNOT trigger write mutations\n- Verify that XSS payloads in user input are sanitized\n\n**Rapid/concurrent actions:**\n- Double-click on submit button\n- Rapid repeated API calls\n- Unmount component while async operation is in flight\n\n**Write tests based on REQUIREMENTS, not on what the source code does.** This is how you catch bugs where the code diverges from expected behavior.\n\n**When a test fails:** first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.\n\n### Async Behavior\n- Loading states\n- Success states\n- Error states\n- Timeout/retry behavior\n\n## Coverage Goals\n\nPer source file:\n- **Function coverage**: 100%\n- **Branch coverage**: > 95%\n- **Line coverage**: > 95%\n- **Statement coverage**: > 95%\n\nRun coverage for a specific file:\n```bash\nnpm test -- --coverage --collectCoverageFrom='src/path/to/file.ts' src/path/to/__tests__/file.test.ts\n```\n\n## Important Rules\n\n1. **Never use Vitest APIs**: Use `jest.fn()`, `jest.mock()`, `jest.spyOn()`, `jest.mocked()` -- never `vi.*` equivalents.\n2. **Never mock base UI components** from `@/components/ui/` -- render them as-is.\n3. **Check `jest.setup.js` before mocking**: Many modules are already globally mocked (darkStore, genericIconComponent, react-markdown, radix-form, etc.). Do not re-mock them.\n4. **Use `@testing-library/user-event`** over `fireEvent` for user interactions.\n5. **Wrap state updates in `act()`** when testing Zustand stores or React state changes.\n6. **Clean up after each test**: Use `beforeEach(() => jest.clearAllMocks())` and `afterEach` for timers.\n7. **Always write both happy path AND adversarial tests** (null, undefined, empty values, boundary conditions, error states).\n8. **Minimum coverage: 75%** (target 80%). Below 75% the task is not complete.\n\n## Forbidden Test Anti-Patterns\n\n| Pattern | Problem | How to Detect |\n|---------|---------|---------------|\n| **The Liar** | Test passes but doesn't verify the behavior it claims to test | Assertions don't match the test name |\n| **The Mirror** | Test reads source code and asserts exactly what the code does — finds zero bugs | Test would never fail even if logic changes |\n| **The Giant** | 50+ lines of setup, multiple acts, dozens of assertions | Should be 5+ separate tests |\n| **The Mockery** | So many mocks that the test only tests the mock setup | Count mocks — if > 3 deep, rethink |\n| **The Inspector** | Coupled to implementation details, breaks on any refactor | Tests internal state instead of behavior |\n| **The Chain Gang** | Tests depend on execution order or share mutable state | Tests fail when run in isolation |\n| **The Flaky** | Sometimes passes, sometimes fails with no code changes | Non-deterministic assertions or timing issues |\n\n## References\n\n- [Incremental Testing Workflow](references/workflow.md) - Step-by-step process for testing directories\n- [Mocking Guide](references/mocking.md) - Patterns for mocking APIs, stores, routers, and context\n- [Async Testing](references/async-testing.md) - Patterns for async operations, timers, and waitFor\n- [Common Patterns](references/common-patterns.md) - Query priority, events, forms, modals, data-driven tests\n- [Test Checklist](references/checklist.md) - Pre-submission verification checklist\n- [Domain Components](references/domain-components.md) - Langflow-specific component testing patterns\n- [Component Template](assets/component-test.template.tsx) - Starter template for component tests\n- [Hook Template](assets/hook-test.template.ts) - Starter template for hook tests\n- [Utility Template](assets/utility-test.template.ts) - Starter template for utility tests\n",".agents/skills/ibm-a11y-level1-audit/SKILL.md":"---\nname: ibm-a11y-level1-audit\ndescription: Perform a scoped IBM Equal Access Level 1 compliance audit of a chosen Langflow frontend surface (routes, components, or a PR) and produce a findings report mapped to WCAG/IBM Level 1 criteria. Default behavior is audit and report only — fixes are applied only when the user explicitly asks for remediation in the same request. Use when the user asks for an IBM Level 1 audit, a Level 1 compliance report, or to find/report Level 1 WCAG issues on a specific surface. For scanning a batch of routes without a report, see ibm-a11y-route-scan. For scanning and fixing an entire PR/branch end-to-end by default, see ibm-a11y-pr-remediation.\n---\n\n# IBM Accessibility Level 1 Audit\n\nAudit → report → (fix only if asked) → verify. Scope is **IBM Equal Access Toolkit v7.3 Level 1 only**. Do not expand into Level 2/3 unless the user asks.\n\nThis skill's default deliverable is a **report**, not a diff. If the user wants a full scan-and-fix pass across an entire PR/branch, use `ibm-a11y-pr-remediation` instead — it is fix-by-default. This skill fixes only when the user explicitly asks for remediation of the audited surface in the same request.\n\n## Related skills\n\n| Skill | Use for |\n|-------|---------|\n| [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) | How to write/run axe + IBM scans, baselines, component gotchas |\n| [ibm-a11y-route-scan](../ibm-a11y-route-scan/SKILL.md) | Python route scanner + Markdown/HTML report generation |\n| [ibm-a11y-pr-remediation](../ibm-a11y-pr-remediation/SKILL.md) | Fix-by-default scan of an entire PR/branch |\n| [frontend-i18n](../frontend-i18n/SKILL.md) | Any new/changed accessible names or UI strings |\n\n## Sources of truth\n\n1. **Level 1 criteria (engineering guide):** [references/ibm-level1-criteria.md](references/ibm-level1-criteria.md) — full checklist, deferred L2/L3 list, common failures, implementation patterns.\n2. **Langflow captured IBM Level 1 filter:** `src/frontend/tests/a11y/ibm-able-level-1-requirements.md` — 21 requirements from the IBM Able UI Level 1 filter. Prefer the engineering guide when the two disagree on pace; still map findings to both IDs when useful.\n3. **Verification hosts:** `src/frontend/tests/a11y/` — Playwright specs, baselines, README. Confirm this path exists before claiming coverage.\n\n## Workflow\n\nCopy and track:\n\n```\nLevel 1 Audit Progress:\n- [ ] 1. Scope the surface\n- [ ] 2. Scan (IBM + axe as applicable)\n- [ ] 3. Map findings to Level 1 criteria\n- [ ] 4. Write the report\n- [ ] 5. Fix violations (only if the user asked for remediation)\n- [ ] 6. Verify with tests/a11y (only if step 5 ran)\n- [ ] 7. Re-scan and update report status (only if step 5 ran)\n```\n\n### 1. Scope the surface\n\nIdentify what to audit from the user request:\n\n- Specific route(s), page(s), or component(s)\n- Pending git changes under `src/frontend`\n- Default: static routes + any stateful specs that touch the changed surface\n\nRead `src/frontend/tests/a11y/README.md` and list existing specs that already cover the surface.\n\n### 2. Scan\n\nRun **both** engines when the surface is interactive UI (see `ibm-a11y-testing-guide`):\n\n```bash\n# Playwright IBM scans (live DOM / stateful)\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# Optional: HTML triage report\nnpm run a11y:html-report --silent\n# → coverage/accessibility-reports/index.html\n\n# Ad-hoc route batch (default-loaded page only) — see ibm-a11y-route-scan for details\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y.json \\\n  --markdown /tmp/langflow-a11y.md \\\n  --html /tmp/langflow-a11y.html \\\n  --timeout-ms 45000\n```\n\nFor component-only changes, also run Jest axe where a `__tests__/*.a11y.test.tsx` exists.\n\nDo not invent findings. Prefer scanner output + manual Level 1 checks scanners miss (keyboard trap both directions, focus restore, reflow at 320px, color-not-only cues).\n\n### 3. Map findings to Level 1\n\nFor each issue, assign:\n\n- **WCAG / IBM ID** from [references/ibm-level1-criteria.md](references/ibm-level1-criteria.md) (e.g. `2.1.1`, `4.1.2`)\n- **IBM ruleId** when from Equal Access (`aria_accessiblename_exists`, `element_tabbable_role_valid`, …)\n- **Severity:** `violation` | `potentialviolation` | `manual`\n- **In scope?** Drop or defer anything listed under **Deferred to Level 2 & Level 3** in the criteria doc unless the user expands scope.\n\n### 4. Write the report\n\nUse [references/report-template.md](references/report-template.md). Deliver the report in the chat (and write a file only if the user asks for a path).\n\nRequired sections: Summary, Scope, Findings table, Fixes applied / proposed, Verification, Remaining risk / baselines.\n\n**Stop here by default.** List proposed fixes in the report without editing files, unless the user's request explicitly also asked for remediation (e.g. \"audit and fix\", \"fix what you find\"). If they only asked for an audit or report, do not proceed to step 5.\n\n### 5. Fix violations (only when explicitly requested)\n\nOnly continue past the report if the user's request explicitly asked for fixes alongside the audit. Otherwise stop after step 4.\n\n- Prefer semantic HTML over ARIA.\n- Follow Langflow patterns in `ibm-a11y-testing-guide` (AG Grid, Radix `asChild`, focus restore, icon-only `aria-label`).\n- Route new UI strings / `aria-label`s through `frontend-i18n`.\n- Do **not** silently disable scans. Use IBM baselines under `src/frontend/tests/a11y/baselines/` only for documented framework debt (see `ibm-a11y-testing-guide`).\n- Keep fixes minimal; do not refactor unrelated UI.\n\n### 6. Verify with `tests/a11y`\n\nConfirm path: `src/frontend/tests/a11y/`.\n\n| Surface | Spec to run / update |\n|---------|----------------------|\n| Static routes | `static-routes.a11y.spec.ts` (+ `scripts/a11y/a11y_routes.json` if new route) |\n| Auth | `auth-pages.a11y.spec.ts` |\n| Core pages | `core-pages.a11y.spec.ts` |\n| Data-rich (files, API keys, globals) | `files.a11y.spec.ts`, `api-keys.a11y.spec.ts`, `global-variables.a11y.spec.ts` |\n| Other data-rich | `data-rich-routes.a11y.spec.ts` |\n| Baselines | `baselines/*.json` |\n\nCommands:\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<relevant>.a11y.spec.ts --project=chromium --workers=5\n```\n\nIf coverage is missing for a fixed state, add a scan (and keyboard test when custom keyboard behavior was fixed) following `files.a11y.spec.ts` / `api-keys.a11y.spec.ts` patterns.\n\n### 7. Close the loop\n\nRe-run the same scans. Update the report: each finding → `fixed` | `baselined` | `open`. State commands run and whether IBM assert mode passed.\n\n## Manual Level 1 spot checks\n\nScanners miss some Level 1 tasks — spot-check when relevant:\n\n- **2.1.1 / 2.1.2:** Tab and Shift+Tab through the surface; Escape closes overlays; no trap.\n- **2.4.3 / 2.4.7:** Focus order matches visual order; focus ring visible.\n- **1.4.10:** 320px width / ~400% zoom — no essential horizontal scroll.\n- **1.4.1:** Status/errors not color-only.\n- **3.3.1 / 3.3.2:** Errors named in text and tied to fields; inputs labeled.\n\n## Best practices (data grids + modals)\n\nWhen auditing **settings tables** (especially `/settings/global-variables`) and similar AG Grid + modal flows, treat these as Level 1 best practices (2.1.1 / 2.4.3) — not edge-case gotchas.\n\n### Selectable-row keyboard map\n\nReference implementation: `GlobalVariablesPage` + `tests/a11y/global-variables.a11y.spec.ts`.\n\n| Key | Behavior |\n|-----|----------|\n| **Space** | Toggle the row selection checkbox (do **not** open edit) |\n| **Enter** | Open the Update Variable modal for the focused row |\n\nImplementation notes:\n\n- Handle in page-level `onCellKeyDown` only (do not change shared `TableComponent` defaults for other grids unless the product asks for the same map).\n- Add `suppressKeyboardEvent` on that page’s column defs for Enter/Space so AG Grid’s built-in Space selection does not fight the custom handler.\n- Sync React selection state after `node.setSelected` so toolbar delete enablement updates (`TableOptions.hasSelection` is read at render time).\n\n### Modal open/close — retain last position (focus restore)\n\n- Opening edit from a row/cell must remember the focused cell (`rowIndex` + `colId`).\n- Closing the edit modal (Escape, Cancel, or successful save) must restore focus to that same cell via `api.setFocusedCell` + DOM `.focus()`, using a few `requestAnimationFrame`s to outlast Radix dialog focus cleanup.\n- Create modal opened from **Add New** should restore to that trigger (Radix default when a real `DialogTrigger` exists).\n- Verify with a Playwright keyboard test: open from a cell → Escape → `document.activeElement` is still that cell (or its `col-id`), then Enter can open again without a manual mouse re-focus.\n\nFor AG Grid pagination/tab traps, Radix `asChild`, and popover Esc restore details, see [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) (Gotchas vs Best practices sections).\n\n## Out of scope (unless asked)\n\n- IBM Level 2/3 criteria listed as deferred in the criteria reference\n- Section 508 software-only rows (web UI covered via 4.1.2)\n- Media captions (1.2.x) when the surface has no audio/video\n- Fixing violations without an explicit remediation request (report only by default)\n",".agents/skills/ibm-a11y-route-scan/SKILL.md":"---\nname: ibm-a11y-route-scan\ndescription: Batch-scan Langflow frontend routes for accessibility issues using the Python IBM Equal Access scanner (scripts/a11y/a11y_scan.py) and produce JSON/Markdown/HTML reports. Scans the default-loaded state of each route only (use explicit state files for modals). Use when asked to scan one or more routes, produce an accessibility report for pages, or batch-check static routes. Reports findings only — does not fix code, run Playwright/axe tests, or perform a formal Level 1 audit; see ibm-a11y-testing-guide, ibm-a11y-level1-audit, and ibm-a11y-pr-remediation for those.\n---\n\n# IBM Route Scanner\n\nUse this skill when asked to scan Langflow frontend pages for accessibility issues with the local Python scanner. This skill only reports — it does not modify code. For test-writing guidance, formal audits, or PR-wide fixes, see `ibm-a11y-testing-guide`, `ibm-a11y-level1-audit`, and `ibm-a11y-pr-remediation`.\n\n## Scanner\n\nUse the Python script:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-report.json \\\n  --markdown /tmp/langflow-a11y-report.md \\\n  --html /tmp/langflow-a11y-report.html \\\n  --timeout-ms 45000\n```\n\nScript options:\n\n- `--url`: base app URL, usually `http://localhost:3000`.\n- `--routes-file`: route manifest JSON file. Prefer `scripts/a11y/a11y_routes.json`.\n- `--route-group`: manifest group to scan. Default: `static`.\n- `--routes`: comma-separated route paths to scan.\n- `--route`: one route path; can be repeated instead of `--routes`.\n- `--levels`: comma-separated issue levels. Default: `violation`.\n- `--out`: JSON report path.\n- `--markdown`: optional Markdown report path.\n- `--html`: optional self-contained HTML report path.\n- `--timeout-ms`: per-route timeout.\n- `--quiet-ms`: network quiet window before scanning. Default: `1000`.\n- `--states-file`: JSON file with explicit modal/state actions.\n- `--headed`: show browser while scanning.\n\n## Route Selection\n\nUse `scripts/a11y/a11y_routes.json` as the source of truth for route selection.\n\nThe normal CI/local batch is the manifest `static` group. Prefer that unless the user asks for custom, dynamic, or gated routes.\n\nCommon manifest-backed command:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-static.json \\\n  --markdown /tmp/langflow-a11y-static.md \\\n  --html /tmp/langflow-a11y-static.html\n```\n\nDynamic routes need real IDs before scanning:\n\n- `/flow/:id/`\n- `/flow/:id/view`\n- `/playground/:id/`\n- `/assets/knowledge-bases/:sourceId/chunks`\n\nFor dynamic routes, get IDs from the loaded app, API responses, or existing test data before replacing placeholders.\n\n## Examples\n\nScan one route:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --route /flows \\\n  --out /tmp/langflow-a11y-flows.json\n```\n\nScan multiple routes:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-report.json \\\n  --markdown /tmp/langflow-a11y-report.md \\\n  --html /tmp/langflow-a11y-report.html\n```\n\nScan more than violations:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --levels violation,potentialviolation,recommendation \\\n  --out /tmp/langflow-a11y-expanded.json\n```\n\nScan route plus modal states:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --states-file /tmp/langflow-a11y-states.json \\\n  --out /tmp/langflow-a11y-modal-report.json \\\n  --markdown /tmp/langflow-a11y-modal-report.md \\\n  --html /tmp/langflow-a11y-modal-report.html \\\n  --timeout-ms 45000\n```\n\nState file shape:\n\n```json\n[\n  {\n    \"route\": \"/settings/global-variables\",\n    \"states\": [\n      {\n        \"name\": \"new-global-variable-modal\",\n        \"open\": [\n          { \"click\": \"[data-testid='api-key-button-store']\" },\n          { \"waitFor\": \"[role='dialog']\" }\n        ],\n        \"close\": [\n          { \"press\": \"Escape\" },\n          { \"waitForHidden\": \"[role='dialog']\" }\n        ]\n      }\n    ]\n  }\n]\n```\n\nSupported state actions:\n\n- `{ \"click\": \"<css selector>\" }`\n- `{ \"clickText\": \"<visible text>\" }`\n- `{ \"clickRole\": { \"role\": \"button\", \"name\": \"Create\" } }`\n- `{ \"fill\": { \"selector\": \"<css selector>\", \"value\": \"text\" } }`\n- `{ \"press\": \"Escape\" }`\n- `{ \"press\": { \"selector\": \"<css selector>\", \"key\": \"Enter\" } }`\n- `{ \"waitFor\": \"<css selector>\" }`\n- `{ \"waitForHidden\": \"<css selector>\" }`\n- `{ \"waitForText\": \"<visible text>\" }`\n- `{ \"wait\": 500 }`\n- `{ \"scroll\": { \"selector\": \"<css selector>\", \"to\": \"bottom\" } }` — `to` is `top` or\n  `bottom`; use `\"by\": <pixels>` for a relative scroll, and omit `selector` to scroll the\n  page. Needed for virtualised grids whose tabbable rows only disappear after scrolling.\n\n## Report\n\nThe scanner always writes JSON. It can also write Markdown and HTML for presentation.\n\nUse JSON for exact data. Use Markdown for PR comments or issues. Use HTML when the user wants a browsable report.\n\nSummarize:\n\n- report path\n- Markdown/HTML report paths, when generated\n- total issue count\n- per-route issue count\n- per-route API request count\n- per-route request failure count\n- top rule IDs\n\nUse report fields directly:\n\n- `totalIssues`\n- `results[].route`\n- `results[].state`\n- `results[].phase`\n- `results[].apiRequests`\n- `results[].requestFailures`\n- `results[].diagnostics`\n- `results[].issues[].ruleId`\n\n## Rules\n\n- Use only scanner output for findings.\n- Do not invent route names. Read `routes.tsx`.\n- Do not auto-click arbitrary buttons to find modals. Use explicit state actions.\n- Avoid destructive modal actions unless the user explicitly asks and data is safe.\n- If a route has zero API requests, mention that scan quality may be limited.\n- This skill reports only; do not edit files. If the user also wants fixes applied, hand off to `ibm-a11y-pr-remediation` or `ibm-a11y-level1-audit`.\n",".agents/skills/component-refactoring/SKILL.md":"---\nname: component-refactoring\ndescription: Refactor high-complexity React components in Langflow frontend. Use when manual complexity assessment shows complexity > 50 or lineCount > 300, when the user asks for code splitting, hook extraction, or complexity reduction; avoid for simple/well-structured components, third-party wrappers, or when the user explicitly wants testing without refactoring.\n---\n\n# Langflow Component Refactoring Skill\n\nRefactor high-complexity React components in the Langflow frontend codebase with the patterns and workflow below.\n\n> **Complexity Threshold**: Components with complexity > 50 (measured manually by counting conditionals, nesting levels, and lines) should be refactored before testing.\n\n## Quick Reference\n\n### Commands (run from `src/frontend/`)\n\n```bash\ncd src/frontend\n\n# Lint with Biome\nnpm run lint\n\n# Type checking\nnpm run type-check\n\n# Run tests\nnpm test\n```\n\n### Manual Complexity Assessment\n\nSince Langflow does not have automated complexity analysis tools, assess components manually:\n\n1. **Count conditionals**: Each `if/else`, `switch/case`, ternary, `&&`/`||` chain adds +1.\n2. **Count nesting levels**: Each level of nesting within conditionals or loops adds +1.\n3. **Count total lines**: Target < 300 lines per component file.\n4. **Count state hooks**: More than 5 `useState` calls suggests hook extraction.\n5. **Count effects**: More than 3 `useEffect` calls suggests effect consolidation.\n\n### Complexity Score Interpretation\n\n| Score | Level | Action |\n|-------|-------|--------|\n| 0-25 | Simple | Ready for testing |\n| 26-50 | Medium | Consider minor refactoring |\n| 51-75 | Complex | **Refactor before testing** |\n| 76-100 | Very Complex | **Must refactor** |\n\n## Core Refactoring Patterns\n\n### Pattern 1: Extract Custom Hooks\n\n**When**: Component has complex state management, multiple `useState`/`useEffect`, or business logic mixed with UI.\n\n**Langflow Convention**: Place hooks in a `hooks/` subdirectory or alongside the component as `use-<feature>.ts`. Langflow uses kebab-case filenames with `use-` prefix.\n\n```typescript\n// Before: Complex state logic in component\nconst FlowPage: FC = () => {\n  const [nodes, setNodes] = useState<Node[]>([])\n  const [edges, setEdges] = useState<Edge[]>([])\n  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)\n\n  // 50+ lines of state management logic...\n\n  return <div>...</div>\n}\n\n// After: Extract to custom hook\n// hooks/use-flow-state.ts\nexport const useFlowState = (flowId: string) => {\n  const [nodes, setNodes] = useState<Node[]>([])\n  const [edges, setEdges] = useState<Edge[]>([])\n  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)\n\n  // Related state management logic here\n\n  return { nodes, setNodes, edges, setEdges, buildStatus, setBuildStatus }\n}\n\n// Component becomes cleaner\nconst FlowPage: FC = () => {\n  const { nodes, setNodes, edges, setEdges } = useFlowState(flowId)\n  return <div>...</div>\n}\n```\n\n**Langflow Examples**:\n- `src/frontend/src/hooks/use-add-component.ts`\n- `src/frontend/src/hooks/use-unsaved-changes.ts`\n- `src/frontend/src/hooks/use-refresh-model-inputs.ts`\n\n### Pattern 2: Extract Sub-Components\n\n**When**: Single component has multiple UI sections, conditional rendering blocks, or repeated patterns.\n\n**Langflow Convention**: Place sub-components in subdirectories or as separate files in the same directory. UI primitives go in `components/ui/`, domain components in `components/core/`, reusable components in `components/common/`.\n\n```typescript\n// Before: Monolithic JSX with multiple sections\nconst GenericNode = () => {\n  return (\n    <div>\n      {/* 100 lines of header UI */}\n      {/* 100 lines of parameter fields */}\n      {/* 100 lines of output handles */}\n    </div>\n  )\n}\n\n// After: Split into focused components\n// CustomNodes/GenericNode/\n//   generic-node.tsx       (orchestration only — kebab-case, descriptive name)\n//   components/\n//     node-header.tsx\n//     node-parameters.tsx\n//     node-outputs.tsx\n\nconst GenericNode = () => {\n  return (\n    <div>\n      <NodeHeader nodeData={data} />\n      <NodeParameters fields={fields} />\n      <NodeOutputs outputs={outputs} />\n    </div>\n  )\n}\n```\n\n**Langflow Examples**:\n- `src/frontend/src/CustomNodes/GenericNode/components/`\n- `src/frontend/src/components/core/`\n- `src/frontend/src/components/ui/`\n\n### Pattern 3: Simplify Conditional Logic\n\n**When**: Deep nesting (> 3 levels), complex ternaries, or multiple `if/else` chains.\n\n```typescript\n// Before: Deeply nested conditionals\nconst getFieldComponent = (field: InputFieldType) => {\n  if (field.type === \"str\") {\n    if (field.multiline) {\n      return <TextAreaComponent />\n    } else if (field.password) {\n      return <PasswordInput />\n    } else if (field.options?.length) {\n      return <Dropdown options={field.options} />\n    } else {\n      return <InputComponent />\n    }\n  } else if (field.type === \"int\") {\n    return <IntComponent />\n  } else if (field.type === \"float\") {\n    return <FloatComponent />\n  }\n  return null\n}\n\n// After: Use lookup tables + early returns\nconst FIELD_COMPONENT_MAP: Record<string, FC<FieldProps>> = {\n  int: IntComponent,\n  float: FloatComponent,\n  bool: ToggleComponent,\n  code: CodeAreaComponent,\n}\n\nconst STR_VARIANT_MAP: Record<string, FC<FieldProps>> = {\n  multiline: TextAreaComponent,\n  password: PasswordInput,\n  options: Dropdown,\n}\n\nconst getFieldComponent = (field: InputFieldType) => {\n  if (field.type !== \"str\") {\n    const Component = FIELD_COMPONENT_MAP[field.type]\n    return Component ? <Component {...field} /> : null\n  }\n\n  const variant = field.multiline ? \"multiline\"\n    : field.password ? \"password\"\n    : field.options?.length ? \"options\"\n    : \"default\"\n\n  const Component = STR_VARIANT_MAP[variant] ?? InputComponent\n  return <Component {...field} />\n}\n```\n\n### Pattern 4: Extract API/Data Logic\n\n**When**: Component directly handles API calls, data transformation, or complex async operations.\n\n**Langflow Convention**:\n- This skill is for component decomposition, not query/mutation design.\n- When refactoring data fetching, use `frontend-query-mutation` for query patterns, `UseRequestProcessor`, cache invalidation, and mutation error handling.\n- Do not create thin passthrough `useQuery` wrappers during refactoring; only extract a custom hook when it truly orchestrates multiple queries/mutations or shared derived state.\n- API hooks live in `controllers/API/queries/{domain}/`.\n\n**Langflow Examples**:\n- `src/frontend/src/controllers/API/queries/flows/use-post-add-flow.ts`\n- `src/frontend/src/controllers/API/queries/variables/use-get-global-variables.ts`\n- `src/frontend/src/controllers/API/queries/folders/use-get-folders.ts`\n\n### Pattern 5: Extract Modal/Dialog Management\n\n**When**: Component manages multiple modals with complex open/close states.\n\n**Langflow Convention**: Modals should be extracted with their state management.\n\n```typescript\n// Before: Multiple modal states in component\nconst FlowToolbar = () => {\n  const [showExportModal, setShowExportModal] = useState(false)\n  const [showShareModal, setShowShareModal] = useState(false)\n  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)\n  const [showApiModal, setShowApiModal] = useState(false)\n  // 5+ more modal states...\n}\n\n// After: Extract to modal management hook\ntype ModalType = \"export\" | \"share\" | \"delete\" | \"api\" | null\n\nconst useFlowToolbarModals = () => {\n  const [activeModal, setActiveModal] = useState<ModalType>(null)\n\n  const openModal = useCallback((type: ModalType) => setActiveModal(type), [])\n  const closeModal = useCallback(() => setActiveModal(null), [])\n\n  return {\n    activeModal,\n    openModal,\n    closeModal,\n    isOpen: (type: ModalType) => activeModal === type,\n  }\n}\n```\n\n### Pattern 6: Extract Form Logic\n\n**When**: Complex form validation, submission handling, or field transformation.\n\n**Langflow Convention**: Extract form state and validation into hooks.\n\n```typescript\n// Extract form validation and submission\nconst useFlowSettingsForm = (initialValues: FlowSettings) => {\n  const [values, setValues] = useState(initialValues)\n  const [errors, setErrors] = useState<Record<string, string>>({})\n\n  const validate = useCallback(() => {\n    const newErrors: Record<string, string> = {}\n    if (!values.name?.trim()) newErrors.name = \"Name is required\"\n    if (values.endpoint_name && !/^[a-z0-9-]+$/.test(values.endpoint_name)) {\n      newErrors.endpoint_name = \"Must be lowercase alphanumeric with hyphens\"\n    }\n    setErrors(newErrors)\n    return Object.keys(newErrors).length === 0\n  }, [values])\n\n  const handleChange = useCallback((field: string, value: any) => {\n    setValues((prev) => ({ ...prev, [field]: value }))\n  }, [])\n\n  return { values, errors, validate, handleChange }\n}\n```\n\n## Langflow-Specific Refactoring Guidelines\n\n### 1. Zustand Store Selectors\n\n**When**: Component reads many values from a Zustand store, causing unnecessary re-renders.\n\n```typescript\n// Before: Selecting too many values\nconst Component = () => {\n  const flowStore = useFlowStore()\n  // Component re-renders on ANY store change\n}\n\n// After: Use individual selectors\nconst Component = () => {\n  const nodes = useFlowStore((state) => state.nodes)\n  const edges = useFlowStore((state) => state.edges)\n  // Component only re-renders when nodes or edges change\n}\n```\n\n**Langflow Reference**: All stores in `src/frontend/src/stores/` follow this selector pattern.\n\n### 2. Custom Node Components\n\n**When**: Refactoring flow node components (`CustomNodes/GenericNode/`).\n\n**Conventions**:\n- Keep node logic in custom hooks\n- Extract parameter rendering to separate components\n- Use the existing `components/` subdirectory for sub-components\n\n```\nCustomNodes/GenericNode/\n  generic-node.tsx             # Node registration and main render (kebab-case, NOT index.tsx)\n  components/\n    handle-render.tsx           # Handle rendering\n    node-description.tsx        # Node description display\n    node-input-field.tsx        # Input field rendering\n    node-name.tsx               # Node name display\n    node-output-field.tsx       # Output field rendering\n    node-status.tsx             # Build status display\n```\n\n### 3. Flow Canvas Components\n\n**When**: Refactoring components related to the flow editor canvas.\n\n**Conventions**:\n- `@xyflow/react` v12 is the canvas library\n- Keep canvas event handlers separate from UI rendering\n- Use `useFlowStore` for flow state management\n- Use `useFlowsManagerStore` for multi-flow management\n\n### 4. API Query Hook Components\n\n**When**: Refactoring components that consume API data.\n\n**Conventions**:\n- Use existing query hooks from `controllers/API/queries/`\n- Access `UseRequestProcessor` for new queries/mutations\n- Use query key arrays like `[\"useGetGlobalVariables\"]` for cache management\n- Cache invalidation belongs in mutation `onSettled` callbacks\n\n## Refactoring Workflow\n\n### Step 1: Assess Complexity\n\nManually count:\n- Total conditionals (if/else, switch, ternary, &&/||)\n- Maximum nesting depth\n- Total lines of code\n- Number of useState/useEffect hooks\n- Number of distinct UI sections\n\n### Step 2: Plan\n\nCreate a refactoring plan based on detected features:\n\n| Detected Feature | Refactoring Action |\n|------------------|-------------------|\n| 5+ `useState` hooks with related state | Extract custom hook |\n| API calls in component body | Extract to query hook |\n| 3+ event handlers with logic | Extract event handlers to hook |\n| 300+ lines | Split into sub-components |\n| Deep conditional nesting (>3) | Simplify conditional logic |\n| Multiple modal states | Extract modal management |\n\n### Step 3: Execute Incrementally\n\n1. **Extract one piece at a time**\n2. **Run lint, type-check, and tests after each extraction**\n3. **Verify functionality before next step**\n\n```\nFor each extraction:\n  1. Extract code\n  2. Run: npm run lint\n  3. Run: npm run type-check\n  4. Run: npm test\n  5. Test functionality manually\n  6. PASS? -> Next extraction\n     FAIL? -> Fix before continuing\n```\n\n### Step 4: Verify\n\nAfter refactoring, re-assess complexity manually:\n\n- Target complexity < 50\n- Target line count < 300\n- Target max nesting depth <= 3\n- Target max function length < 30 lines\n\n## Common Mistakes to Avoid\n\n### Over-Engineering\n\n```typescript\n// Too many tiny hooks\nconst useButtonText = () => useState(\"Click\")\nconst useButtonDisabled = () => useState(false)\nconst useButtonLoading = () => useState(false)\n\n// Cohesive hook with related state\nconst useButtonState = () => {\n  const [text, setText] = useState(\"Click\")\n  const [disabled, setDisabled] = useState(false)\n  const [loading, setLoading] = useState(false)\n  return { text, setText, disabled, setDisabled, loading, setLoading }\n}\n```\n\n### Breaking Existing Patterns\n\n- Follow existing directory structures in `components/ui/`, `components/core/`, `components/common/`\n- Maintain naming conventions (kebab-case files, PascalCase components)\n- Preserve export patterns for compatibility\n- Keep Zustand store selector patterns consistent\n\n### Premature Abstraction\n\n- Only extract when there is clear complexity benefit\n- Do not create abstractions for single-use code\n- Keep refactored code in the same domain area\n\n### Bypassing UseRequestProcessor\n\n- Do not call `useQuery` or `useMutation` directly for API calls\n- Always use the `UseRequestProcessor` pattern for consistency with retry and invalidation logic\n- See `frontend-query-mutation` skill for API hook patterns\n\n## References\n\n### Langflow Codebase Examples\n\n- **Hook extraction**: `src/frontend/src/hooks/`\n- **Component splitting**: `src/frontend/src/CustomNodes/GenericNode/components/`\n- **UI components**: `src/frontend/src/components/ui/`\n- **Core components**: `src/frontend/src/components/core/`\n- **Common components**: `src/frontend/src/components/common/`\n- **API query hooks**: `src/frontend/src/controllers/API/queries/`\n- **Zustand stores**: `src/frontend/src/stores/`\n\n### Related Skills\n\n- `frontend-query-mutation` - For API query and mutation patterns\n- `frontend-testing` - For testing refactored components\n",".agents/skills/ibm-a11y-testing-guide/SKILL.md":"---\nname: ibm-a11y-testing-guide\ndescription: Reference guide for writing and running Langflow frontend accessibility tests with axe (Jest) and IBM Equal Access (Playwright `page.runA11yScan`). Covers which engine/test layer to pick, the POUR checklist, axe-vs-IBM rule gaps, Radix/AG-Grid component gotchas, and IBM baselines. Use when writing or reviewing a11y tests, debugging a specific axe/IBM violation, or deciding which test layer fits a UI surface. Does not run scans, audit a whole surface, or fix a PR end-to-end — see ibm-a11y-route-scan, ibm-a11y-level1-audit, and ibm-a11y-pr-remediation for those.\n---\n\n# IBM/axe Accessibility Testing Guide\n\nReference material for **how** to test a given UI surface. This skill does not decide what to scan or drive a fix loop by itself — use it while writing tests, debugging a violation, or implementing a fix identified by `ibm-a11y-route-scan`, `ibm-a11y-level1-audit`, or `ibm-a11y-pr-remediation`.\n\n## When To Use\n\nUse this skill for frontend work under `src/frontend` when the change affects:\n\n- UI components, primitives, pages, routes, dialogs, drawers, popovers, dropdowns, tabs, accordions, tables, forms, navigation, or canvas controls.\n- ARIA attributes, labels, roles, focus management, tab order, keyboard interaction, disabled states, loading states, or error states.\n- A request to write, fix, or review an axe/IBM accessibility test.\n\nUse alongside:\n\n- `frontend-testing` for Jest/React Testing Library patterns.\n- `e2e-testing` for Playwright patterns.\n- `frontend-i18n` when adding or changing user-facing labels, accessible names, `aria-label`, tooltips, or visible strings.\n- `ibm-a11y-route-scan` to batch-scan routes with the Python scanner and produce Markdown/HTML reports.\n- `ibm-a11y-level1-audit` for a scoped IBM Level 1 compliance audit and report.\n- `ibm-a11y-pr-remediation` to scan and fix an entire PR/branch end-to-end.\n\n## Goal\n\nDo not stop at \"run axe on default render.\" Inspect the changed UI and cover every meaningful user-visible state, for both keyboard and screen-reader users, that can expose accessibility bugs.\n\n## The Bar: two engines, both must pass\n\nAutomated a11y is not one tool. Run BOTH — they catch different classes of bug:\n\n- **axe-core** — Jest `axe()` (`@/utils/a11y-test`), jsdom-only. Fast; strong on contrast, labels, roles, ARIA basics.\n- **IBM Equal Access** — the stricter engine on ARIA structure and keyboard semantics; catches real WCAG Level-1 issues axe silently passes. Two entry points, **same engine** (`aChecker`):\n  - Playwright **`page.runA11yScan(label)`** — runs `aChecker.getCompliance` in-browser on the **live DOM**, so it scans open modals / menus / selected / editing states. This is IBM, **not** axe (despite the name).\n  - `scripts/a11y/a11y_scan.py` (see `ibm-a11y-route-scan`) — route scanner for the default-loaded page only.\n\nA page that is green on axe is NOT done. When the change touches a table/tree/listbox/menu/composite widget, IBM is mandatory. Prefer `page.runA11yScan` for stateful surfaces (it sees the DOM after your interactions); use the Python scanner for a quick default-load route check.\n\nVerify commands:\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# The Python scanner's playwright deps are NOT in the default `uv sync`.\n# One-time: `uv run --with playwright playwright install chromium`\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes /settings/<route> \\\n  --out /tmp/a11y.json --markdown /tmp/a11y.md --timeout-ms 45000\n```\n\n`RUN_A11Y=true` runs the scan; add `RUN_A11Y_ASSERT=true` to actually fail on new violations (without it the scan is informational). Both engines must report zero. After changing a shared component, re-scan every page that uses it, not just the one you touched.\n\n## First Pass\n\n1. Read the changed files and nearby tests.\n2. Identify the UI surface:\n   - Primitive/component only\n   - Composed component\n   - Page/route\n   - Stateful workflow\n   - Shared helper that changes labels, roles, tab order, or focus behavior\n3. List interactive surfaces:\n   - Buttons, links, inputs, checkboxes, switches, radios, selects, comboboxes\n   - Dropdown menus, popovers, dialogs, drawers, tooltips\n   - Tabs, accordions, tables/treegrids, row actions, pagination\n   - Keyboard-only interactions and focus traps\n4. List states worth scanning:\n   - Default, populated, empty, loading, error/validation visible\n   - Disabled/read-only, selected/expanded/open\n   - Modal/dropdown/popover open\n   - Mobile viewport when layout changes\n\n## What To Check (POUR checklist)\n\n### Perceivable\n- Non-text content has a text alternative; decorative images are hidden from AT.\n- Text contrast ≥ 4.5:1; non-text (icons, borders, focus rings) ≥ 3:1.\n- Layout reflows at 320px / 400% zoom without horizontal scroll or loss of content.\n\n### Operable (keyboard is the big one)\n- Every control is reachable AND operable by keyboard (Enter/Space activate; arrows where expected).\n- Focus is always visible.\n- Focus order is logical and matches visual order.\n- **No keyboard trap — test Shift+Tab too, not just Tab.** Focus can always move out of a widget both ways.\n- Disabled controls are not in the tab order.\n- Composite widgets (grid/tree/listbox/menu/tablist) are ONE logical tab stop with a roving `tabindex`; arrows move within.\n- After async transitions (route change, panel open/close) focus lands somewhere useful, never silently on `<body>`.\n\n### Understandable\n- Inputs have programmatic labels; visible label matches accessible name.\n- Errors are identified in text and associated with their field; give a suggestion when possible.\n- The same control is identified consistently across the app.\n\n### Robust (name / role / value)\n- Interactive elements expose a correct role, name, and state.\n- Valid ARIA parent/child (e.g. `rowgroup` owns `row`, `list` owns `listitem`).\n- No tabbable element with a non-widget role (`presentation`/`none`).\n- `aria-hidden=\"true\"` never wraps a focusable element.\n\n## axe vs IBM: known gaps\n\nThings axe passes but IBM flags (all real WCAG Level 1):\n\n| IBM rule | WCAG | Typical cause |\n| --- | --- | --- |\n| `element_tabbable_role_valid` | 4.1.2 | tabbable element with `role=\"presentation\"` / no widget role (focus sentinels, wrappers) |\n| `aria_child_valid` | 1.3.1 | `rowgroup`/`list`/`tablist` owning no valid child role |\n| `aria_child_tabbable` | 2.1.1 / 4.1.2 | composite widget with no tabbable descendant (needs roving `tabindex`) |\n| `aria_hidden_focus_misuse` | 4.1.2 | focusable element inside `aria-hidden=\"true\"` |\n| `aria_accessiblename_exists` | 4.1.2 | element with a widget role but no accessible name — e.g. an AG Grid `columnheader` with neither `field` nor `headerName` (icon-only action column) |\n| `aria_content_in_landmark` | 1.3.1 | content outside any landmark — e.g. a Radix menu/popover portaled to `<body>`. `role=\"menu\"` is NOT landmark-exempt; only `aria-modal` dialogs are. Often unfixable app-wide → baseline it (see below) |\n\nRule of thumb: touch a table/tree/listbox/menu → scan with IBM.\n\n## Component Gotchas\n\n- **Data grids (AG Grid — the shared `TableComponent`)**: the grid's own tab guards + pagination are the fragile part.\n  - Disabled paging buttons must be `tabindex=\"-1\"`, but NEVER `inert`/`disabled` — that breaks AG Grid's tab guards and traps reverse (Shift+Tab) entry (WCAG 2.1.2).\n  - AG Grid still *programmatically* focuses disabled paging on tab-out; handle with its `tabToNextCell` hook plus a container-scoped `focusin` redirect (defer the redirect to `requestAnimationFrame` — focus changes inside a `focusin` handler are ignored, and AG Grid restores the last-focused cell on the next tick).\n  - Empty rowgroups need `role=\"presentation\"`; the grid needs one roving-tabindex row.\n  - **Icon-only / action column header**: AG Grid derives the `columnheader` accessible name from `field`; a column with neither `field` nor `headerName` is nameless (`aria_accessiblename_exists`, 4.1.2). Give it a `headerName` and hide it visually with a `headerClass` (sr-only clip on `.ag-header-cell-text`) so the header stays blank but named.\n  - **Interactive control inside a cell** (menu trigger, button): AG Grid navigates **cell-by-cell** and never tab-focuses the inner control, so it's keyboard-inoperable. Activate it from the grid's `onCellKeyDown` on Enter/Space (give the column a `colId` to target it). A **Radix trigger opens on keydown/pointerdown, not a synthetic `.click()`** — focus the trigger and **re-dispatch the key** (`new KeyboardEvent(\"keydown\", {key, bubbles:true})`), guarding against re-entry (skip when `target` is already the button). `TableComponent` spreads `{...props}` to AG Grid, so pass `onCellKeyDown` from the page — no shared-component change.\n  - **Focus visible on borderless grids** (`.ag-no-border`): the theme suppresses the cell focus outline (`outline:none`), hiding keyboard focus (2.4.7). Restore it with a `:focus-visible` ring scoped to `.ag-no-border .ag-cell:focus-visible` (+ header/row). `:focus-visible` **distinguishes modality on AG cells** — mouse click resolves to `:focus`, keyboard nav to `:focus-visible` — so the ring shows for keyboard only and mouse stays quiet. CAVEAT when verifying: probe with a **clean, first-interaction** page; a session that already used the keyboard makes `:focus-visible` report `true` for a later mouse click (heuristic contamination).\n  - Set `ensureDomOrder: true` so DOM order matches visual order for tab navigation.\n  - `TableComponent` is shared across settings/traces — fix once in the grid patch, then re-scan every grid page. Its pagination/tab-out rework lives in LE-1720 / PR #13953; don't duplicate it per-page.\n- **Modals/drawers**: title + description, focus trapped inside while open, focus restored to the trigger on close, Escape closes.\n- **Dropdowns/popovers**: reachable by keyboard, Escape closes, focus returns to the trigger.\n- **Radix (shadcn) primitives** — three recurring traps:\n  - **`Trigger asChild` double tab stop**: a `DialogTrigger`/`DropdownMenuTrigger` wrapping a real `<button>` **without `asChild`** makes Radix render its *own* `<button>` around it → nested buttons → two consecutive tab stops (2.4.3). Always pass `asChild` when the child is already an interactive element. (Watch the trigger wrapper's own `asChild={cond}` default — `DeleteConfirmationModal` needs an explicit `asChild`.)\n  - **Focus-restore race**: on close, Radix restores focus to its trigger **once, asynchronously**. If the menu action programmatically moves focus elsewhere (e.g. `startEditingCell` opens an inline editor), Radix steals it back. Re-assert focus on your target across a few `requestAnimationFrame`s to outlast the one-time restore. For rename-style flows, `.select()` the input too.\n  - **Portaled menu landmark**: `DropdownMenu`/`Popover` content portals to `<body>`, tripping `aria_content_in_landmark`. Portaling into `<main>` is not an option when `<main>` is `overflow-hidden` (clips the menu). Treat as app-wide debt → **baseline it** (below); the menu's real a11y (role, named trigger, keyboard open/close, focus restore) is covered by a keyboard test instead.\n- **Disabled controls**: keep them out of the tab order. Only use `aria-disabled` (instead of native `disabled`) when the control must stay discoverable, and keep it non-operable.\n- **Icon-only buttons**: real `<button>` with an `aria-label`.\n- **Custom clickable divs/cells**: prefer a semantic element; if a cell/card must open something it needs Enter/Space handling, not just `onClick`.\n\n## Best practices (settings grids + modals)\n\nApply these when building or reviewing AG Grid tables that open an edit modal (reference: `GlobalVariablesPage`, `tests/a11y/global-variables.a11y.spec.ts`):\n\n- **Keyboard map on selectable rows:** **Space** toggles the row checkbox; **Enter** opens the row’s edit/detail UI. Do not use Space to open the modal.\n- **Scope the map on the page:** implement with page-level `onCellKeyDown` and per-column `suppressKeyboardEvent` for Enter/Space so AG Grid’s default Space handling does not compete. Prefer not changing shared `TableComponent` defaults unless the same map is product-wide.\n- **Keep selection UI in sync:** after `node.setSelected`, update React selection state so toolbar actions (e.g. delete) enable/disable correctly (`TableOptions.hasSelection` is read at render time).\n- **Retain place on modal close (2.4.3):** remember the focused cell (`rowIndex` + `colId`) when opening edit; on close (Escape, Cancel, or save) restore with `api.setFocusedCell` + DOM `.focus()`, using a few `requestAnimationFrame`s to outlast Radix dialog cleanup. Controlled edit dialogs without a Radix trigger need this explicitly. Create flows that use a real `DialogTrigger` (e.g. Add New) should restore to that trigger.\n- **Verify with keyboard tests:** open from a cell → Escape → focus is still on that cell; Enter can open again without a mouse re-click.\n\n## Choose Test Layer\n\n### Jest + axe\n\nUse Jest when the changed surface is a primitive or component that renders in jsdom without full app routing.\n\n```tsx\nimport { render, screen } from \"@testing-library/react\";\nimport { axe } from \"@/utils/a11y-test\";\n\nit(\"should_have_no_axe_violations\", async () => {\n  const { container } = render(<Component />);\n  expect(await axe(container)).toHaveNoViolations();\n});\n```\n\nNotes: `a11y-test.ts` disables `color-contrast` (jsdom can't measure layout). Prefer role/label/text queries. Assert accessible names explicitly for icon-only / visually-hidden / generated labels. Put tests at `__tests__/<name>.a11y.test.tsx`.\n\n```bash\ncd src/frontend\nnpx jest path/to/<name>.a11y.test.tsx --runInBand\n```\n\n### Playwright + `page.runA11yScan`\n\nUse Playwright when the surface needs real browser layout, routing, app state, mocking, modals, tables, focus order, keyboard behavior, or full-page interactions.\n\n```ts\nimport { expect, test } from \"../fixtures\";\nimport { awaitBootstrapTest } from \"../utils/await-bootstrap-test\";\n\ntest(\"scans populated state\", { tag: [\"@release\", \"@api\"] }, async ({ page }) => {\n  await awaitBootstrapTest(page, { skipModal: true });\n  await page.goto(\"/settings/example\");\n  await expect(page.getByText(\"Example\")).toBeVisible();\n  await page.runA11yScan(\"settings-example-populated\");\n});\n```\n\nRules: put specs under `tests/a11y/<feature>.a11y.spec.ts`; import `test`/`expect` from `../fixtures`; tag every test `@release` plus a domain tag (`@workspace`/`@api`/`@database`/`@components`/`@starter-projects`); stable scan names (lowercase, feature-first, state-specific); mock API for deterministic states; disable animations for focus assertions; use explicit interactions (no random clicking of destructive controls). If the route belongs in static coverage, update `scripts/a11y/a11y_routes.json`.\n\n```bash\ncd src/frontend\nRUN_A11Y=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\nnpm run a11y:html-report --silent\n```\n\n## IBM baselines (accepting known debt)\n\nSome IBM violations are real but framework-level and can't be fixed per-page (e.g. `aria_content_in_landmark` on a Radix menu portal). Track them with an IBM baseline so the scan stays green while the debt is recorded — do NOT silently drop the scan or the assertion.\n\nHow it works (`fixtures.ts` + `.achecker.yml`):\n\n- `baselineFolder: tests/a11y/baselines`. The baseline file name is the **scan label**: `{project}__{label}.json`, e.g. `chromium__assets-files-actions-menu.json` (dashes preserved; the first scan in a test has no index suffix).\n- On each run IBM's `filterReport` loads that file and marks any result matching a baseline entry by **`path.dom` + `ruleId` + `reasonId`** as `ignored: true`. `countNewA11yViolations` counts `violation && !ignored`, so baselined issues don't fail.\n\nCreate one:\n\n1. Run the scan once — it writes the full report to `coverage/accessibility-reports/{label}.json`.\n2. Copy the offending result(s) into `tests/a11y/baselines/{label}.json` as `{ \"results\": [ { \"ruleId\", \"reasonId\", \"path\": { \"dom\": … } } ] }`. A **minimal** baseline (only the entries to ignore) is clearest; add a `description` field noting why and how to resurface it.\n3. Re-run → the scan passes. Commit the baseline so the debt is tracked; **deleting it resurfaces the violation**. Point at the baseline from a comment in the spec.\n\nMatching is exact-DOM-path, so baselines can go stale if unrelated `<body>`-level portals (toasts) shift the portal index — scan the state with no other overlays open.\n\n## Route/Page Coverage Matrix\n\nFor route/page changes, cover the smallest matrix representing real user states:\n\n- Loaded/default, empty data, populated data\n- Primary modal open, dropdown/popover open\n- Validation or error visible\n- Selected/expanded table row or bulk-action state\n- Mobile viewport when responsive layout changes\n\nDon't force states the page can't enter; briefly explain any skipped state.\n\n## Data-Rich Route Pattern\n\nUse `tests/a11y/api-keys.a11y.spec.ts` and `tests/a11y/files.a11y.spec.ts` as the models for route-level work: mock API data; scan populated table; scan empty table; open create modal and scan; submit to the generated-result modal and scan; open a text-cell modal and scan; select a row and scan the selected state; set a mobile viewport and scan. Add keyboard-interaction tests (not scans) for tab-out, reverse re-entry, Enter-to-open, focus restore, and focus-visible where the surface has custom keyboard behavior. This is the expected bar for data-rich routes.\n\n**Driving grid row states**: client-rendered states (upload progress %, upload-failed/error, disabled rows) are usually driven by fields the cell renderer reads off the row (`params.data.*`). If the list query returns the response untransformed, just inject the field into the mocked rows (e.g. `{ ...file, progress: -1 }`) to scan the error/progress state — no need to simulate the real flow.\n\n## Report Back\n\nWhen done, state:\n\n- Test files added or updated, and states scanned.\n- Commands run — including whether BOTH axe and IBM were run and both reported zero.\n- Any states not covered and why.\n- Any remaining a11y risk or accepted limitation.\n","AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to AI coding agents when working with code in this repository.\n\n## Project Overview\n\nLangflow is a visual workflow builder for AI-powered agents. It has a Python/FastAPI backend, React/TypeScript frontend, and a lightweight executor CLI (lfx).\n\n## Prerequisites\n\n- **Python:** 3.10-3.14\n- **uv:** >=0.4 (Python package manager)\n- **Node.js:** >=20.19.0 (v22.12 LTS recommended)\n- **npm:** v10.9+\n- **make:** For build coordination\n\n## Common Commands\n\n### Development Setup\n```bash\nmake init              # Install all dependencies + pre-commit hooks\nmake run_cli           # Build and run Langflow (http://localhost:7860)\nmake run_clic          # Clean build and run (use when frontend issues occur)\n```\n\n### Development Mode (Hot Reload)\n```bash\nmake backend           # FastAPI on port 7860 (terminal 1)\nmake frontend          # Vite dev server on port 3000 (terminal 2)\n```\n\nFor component development, enable dynamic loading:\n```bash\nLFX_DEV=1 make backend                    # Load all components dynamically\nLFX_DEV=mistral,openai make backend       # Load only specific modules\n```\n\n### Code Quality\n```bash\nmake format_backend    # Format Python (ruff) - run FIRST before lint\nmake format_frontend   # Format TypeScript (biome)\nmake format            # Both\nmake lint              # mypy type checking\n```\n\n### Testing\n```bash\nmake unit_tests                    # Backend unit tests (pytest, parallel)\nmake unit_tests async=false        # Sequential tests\nuv run pytest path/to/test.py      # Single test file\nuv run pytest path/to/test.py::test_name  # Single test\n\nmake test_frontend                 # Jest unit tests\nmake tests_frontend                # Playwright e2e tests\n```\n\n### Database Migrations\n```bash\nmake alembic-revision message=\"Description\"  # Create migration\nmake alembic-upgrade                         # Apply migrations\nmake alembic-downgrade                       # Rollback one version\n```\n\n## Architecture\n\n### Monorepo Structure\n```\nsrc/\n├── backend/\n│   ├── base/langflow/     # Core backend package (langflow-base)\n│   │   ├── api/           # FastAPI routes (v1/, v2/)\n│   │   ├── components/    # Built-in Langflow components\n│   │   ├── services/      # Service layer (auth, database, cache, etc.)\n│   │   ├── graph/         # Flow graph execution engine\n│   │   └── custom/        # Custom component framework\n│   └── tests/             # Backend tests\n├── frontend/              # React/TypeScript UI\n│   └── src/\n│       ├── components/    # UI components\n│       ├── stores/        # Zustand state management\n│       └── icons/         # Component icons\n├── langflow-core/         # Usable provider-free Langflow distribution\n├── bundles/               # Curated provider integrations\n└── lfx/                   # Lightweight executor and shared primitives\n```\n\n### Key Packages\n- **langflow**: Full end-user package; depends on `langflow-core` and curated provider bundles\n- **langflow-core**: Service-complete, provider-bundle-free distribution; owns the `langflow` CLI\n- **langflow-base**: Modular application platform (API, services, graph engine); extras add service integrations\n- **lfx**: Shared execution primitives and standalone CLI (`lfx serve`, `lfx run`)\n\nThe public dependency direction is `langflow → langflow-core → langflow-base → lfx`.\nProvider packages under `src/bundles/` are added only by the full `langflow` distribution.\n\n### Service Layer\nBackend services in `src/backend/base/langflow/services/`:\n- `auth/` - Authentication\n- `authorization/` - Authorization (RBAC) plugin layer — see below\n- `database/` - SQLAlchemy models and migrations\n- `cache/` - Caching layer\n- `storage/` - File storage\n- `tracing/` - Observability integrations\n\n### Authorization (RBAC)\n\nAuthorization is a pluggable layer separate from authentication:\n\n- **OSS** ships the interface (`BaseAuthorizationService` in `lfx`) + a pass-through implementation (`LangflowAuthorizationService`) + the `authz_*` and `casbin_rule` DB schema + route guards.\n- Implementations register via the `lfx.services` entry point `authorization_service` in `lfx.toml` (same pattern as the SSO `auth_service`). A registered plugin reads the `authz_*` admin tables and writes compiled rules to `casbin_rule`.\n\nDefault is **off**: `LANGFLOW_AUTHZ_ENABLED=false`. When enabled with only the OSS stub registered, every check returns allow — the stub is a no-op so routes stay wired and audit rows still flow. Real allow/deny requires a registered authorization plugin.\n\nRoute guards live in `langflow.services.authorization.guards` (the legacy `langflow.services.authorization.utils` path re-exports them for backward compatibility):\n- `ensure_flow_permission(user, FlowAction.*, flow_id=..., flow_user_id=..., workspace_id=..., folder_id=...)` — single-flow CRUD + execute\n- `ensure_deployment_permission(user, DeploymentAction.*, deployment_id=..., deployment_user_id=..., workspace_id=..., project_id=...)`\n- `ensure_project_permission(user, ProjectAction.*, project_id=..., project_user_id=..., workspace_id=...)`\n- `ensure_knowledge_base_permission(user, KnowledgeBaseAction.*, kb_name=..., kb_user_id=...)`\n- `ensure_variable_permission(user, VariableAction.*, variable_id=..., variable_user_id=...)`\n- `ensure_file_permission(user, FileAction.*, file_id=..., file_user_id=...)`\n- `ensure_share_permission(user, ShareAction.*, share_id=..., share_user_id=...)`\n- `filter_visible_resources(user, resource_type=..., candidates=..., act=...)` — list-endpoint filter; safe no-op in OSS\n\nThe enforcement request shape is `(subject, domain, object, action)`:\n- subject = `user:{uuid}`\n- domain = `project:{uuid}` → `workspace:{uuid}` → `*` (resolved by `_resolve_flow_domain`; the more specific domain wins so project-scoped grants match directly while workspace-scoped grants still flow down via plugin-side role inheritance)\n- object = `flow:{uuid}` / `deployment:{uuid}` / `project:{uuid}` / `flow:*` / etc.\n- action = `read` / `write` / `create` / `delete` / `execute` / `deploy`\n\n**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher, variable PATCH/DELETE in `variable.py`) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.\n\n**Share CRUD API (Phase 3):** `/api/v1/authz/shares` provides POST / GET / PATCH / DELETE on `authz_share` rows. The handler enforces an OSS floor (resource owner or superuser may administer shares for that resource) so the OSS pass-through cannot let a non-owner mint share rows. Each write fires `BaseAuthorizationService.invalidate_user` / `invalidate_all` so a registered enforcer can drop cached policy. Audit rows are written via `audit_decision` with `share:create` / `share:update` / `share:delete` actions.\n\n**Audit query API (Phase 4):** `GET /api/v1/authz/audit` (superuser-only) exposes a paginated, filterable view of `authz_audit_log`. Supports `user_id`, `resource_type`, `resource_id`, `action`, `result`, `since`, `until` filters; page size capped at 200.\n\n**Default role catalog (Phase 4):** the consolidated foundations migration `7c8d9e0f1a2b_authz_foundations` seeds the three built-in `is_system=True` roles (viewer / developer / admin) with `\"{resource}:{action}\"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.\n\n## Component Development\n\nComponents live in `src/backend/base/langflow/components/`. To add a new component:\n\n1. Create component class inheriting from `Component`\n2. Define `display_name`, `description`, `icon`, `inputs`, `outputs`\n3. Add to `__init__.py` (alphabetical order)\n4. Run with `LFX_DEV=1 make backend` for hot reload\n\n**IMPORTANT:** Changing a component's class name is a breaking change and should never be done. The class name serves as an identifier used to match components in saved flows and to flag them for updates in the UI. Renaming it will break existing flows that use that component.\n\n### Component Structure\n```python\nfrom langflow.custom import Component\nfrom langflow.io import MessageTextInput, Output\n\nclass MyComponent(Component):\n    display_name = \"My Component\"\n    description = \"What it does\"\n    icon = \"component-icon\"  # Lucide icon name or custom\n\n    inputs = [\n        MessageTextInput(name=\"input_value\", display_name=\"Input\"),\n    ]\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"process\"),\n    ]\n\n    def process(self) -> Message:\n        # Component logic\n        return Message(text=self.input_value)\n```\n\n### Component Testing\nTests go in `src/backend/tests/unit/components/`. Use base classes:\n- `ComponentTestBaseWithClient` - Components needing API access\n- `ComponentTestBaseWithoutClient` - Pure logic components\n\nRequired fixtures: `component_class`, `default_kwargs`, `file_names_mapping`\n\n## Frontend Development\n\n- **React 19** + TypeScript + Vite\n- **Zustand** for state management\n- **@xyflow/react** for graph visualization\n- **Tailwind CSS** for styling\n\n### Custom Icons\n1. Create SVG component in `src/frontend/src/icons/YourIcon/`\n2. Export with `forwardRef` and `isDark` prop support\n3. Add to `lazyIconImports.ts`\n4. Set `icon = \"YourIcon\"` in Python component\n\n## Testing Notes\n\n- `@pytest.mark.api_key_required` - Tests requiring external API keys\n- `@pytest.mark.no_blockbuster` - Skip blockbuster plugin\n- Database tests may fail in batch but pass individually\n- Pre-commit hooks require `uv run git commit`\n- Always use `uv run` when running Python commands\n- When running tests inside a sub-package (e.g. `langflow-base`, `lfx`), sync that package's dev group first: `uv sync --group dev --package langflow-base`. The default `uv sync` only resolves the top-level workspace and may leave dev-only test deps (e.g. `fakeredis`) uninstalled.\n\n### Graph Testing Pattern\n\nProper Graph tests follow this pattern:\n1. Build graph with connected components\n2. Connect them via `.set()` calls\n3. Call `async_start` and iterate over the results\n4. Validate the results\n\n### Testing Best Practices\n\n- Avoid mocking in tests when possible\n- Prefer real integrations for more reliable tests\n\n## Version Management\n```bash\nmake patch v=1.5.0  # Update version across all packages\n```\n\nThis updates: `pyproject.toml`, `src/backend/base/pyproject.toml`, `src/frontend/package.json`\n\n## Pre-commit Workflow\n\nPre-commit hooks run ruff and biome automatically on `git commit`, so manual\nformatting is not required. To avoid an extra commit cycle when you have many\nchanges:\n\n1. Run `make format_backend` once before staging - fixes most ruff issues up front.\n2. Run `uv run git commit` (the `uv run` ensures pre-commit finds the right Python).\n3. If you touched backend code, run `make unit_tests` locally for faster feedback than CI.\n\n## Pull Request Guidelines\n\n- Follow [semantic commit conventions](https://www.conventionalcommits.org/)\n- Reference any issues fixed (e.g., `Fixes #1234`)\n- Ensure all tests pass before submitting\n\n## Documentation\n\nDocumentation uses Docusaurus and lives in `docs/`:\n```bash\ncd docs\nyarn install\nyarn start        # Dev server on port 3000 (prompts for 3001 if 3000 is in use)\n```\n",".agents/skills/e2e-testing/SKILL.md":"---\nname: e2e-testing\ndescription: Write and review Playwright E2E tests for Langflow. Trigger when the user asks to write, fix, or review E2E tests, spec files, Playwright tests, or integration tests that exercise the full UI. Also trigger when modifying data-testid attributes, test helpers in tests/utils/, or fixture configuration.\n---\n\n# Langflow E2E Testing (Playwright)\n\n## When to Apply\n\n- User asks to **write E2E tests** for a feature or flow\n- User asks to **fix a failing E2E test**\n- User asks to **review E2E test coverage**\n- User modifies `data-testid` attributes in components (may break existing tests)\n- User changes test utilities in `src/frontend/tests/utils/`\n\n**Do NOT apply** when:\n- User asks about unit tests (use `frontend-testing` skill for Jest)\n- User asks about backend tests (use `backend-code-review` skill for pytest)\n\n## Tech Stack\n\n| Tool | Version | Purpose |\n|------|---------|---------|\n| Playwright | 1.59.1 | E2E test runner + browser automation |\n| Chromium | (bundled) | Default browser (Firefox/Safari disabled) |\n| Custom fixtures | `tests/fixtures.ts` | Auto-detects API errors and flow execution failures |\n\n## Key Commands\n\n```bash\n# Run all E2E tests\nnpx playwright test\n\n# Run tests filtered by tag\nnpx playwright test --grep \"@release\"\nnpx playwright test --grep \"@workspace\"\nnpx playwright test --grep \"@starter-projects\"\n\n# Run a specific test file\nnpx playwright test tests/core/features/run-flow.spec.ts\n\n# Debug mode (headed browser + step through)\nnpx playwright test --debug\n\n# Show HTML report after run\nnpx playwright show-report\n\n# Update snapshots (if used)\nnpx playwright test --update-snapshots\n```\n\n## Configuration\n\n**File**: `src/frontend/playwright.config.ts`\n\n| Setting | Value | Why |\n|---------|-------|-----|\n| `fullyParallel` | `true` | Tests run in parallel for speed |\n| `timeout` | 5 minutes | Flow builds can be slow; prevents false timeouts |\n| `retries` | 3 (local), 2 (CI) | Flaky network/rendering issues; retries catch them |\n| `workers` | 2 | Balances speed and resource usage |\n| `actionTimeout` | 20s | Individual action timeout (click, fill, etc.) |\n| `trace` | `on-first-retry` | Captures trace on failures for debugging |\n| `baseURL` | `http://localhost:3000` | Vite dev server |\n\n**WebServer**: Playwright auto-starts backend (uvicorn on 7860) + frontend (npm start on 3000).\n\n## Directory Structure\n\n```\nsrc/frontend/tests/\n├── fixtures.ts                     # Custom test fixture with error detection\n├── globalTeardown.ts               # Cleanup (removes temp DB after tests)\n├── core/\n│   ├── features/                   # Main feature tests (run-flow, playground, etc.)\n│   ├── integrations/               # Starter project / template tests\n│   ├── regression/                 # Bug regression tests\n│   └── unit/                       # Component-level Playwright tests\n├── extended/\n│   ├── features/                   # Extended features (MCP, auto-save, etc.)\n│   ├── integrations/               # Extended integrations\n│   └── regression/                 # Extended regressions\n└── utils/                          # 37+ shared helper functions\n```\n\n## File Naming\n\n- **kebab-case** with `.spec.ts` suffix: `run-flow.spec.ts`, `playground.spec.ts`, `flow-lock.spec.ts`\n- Template tests may use spaces: `Document QA.spec.ts`, `Social Media Agent.spec.ts`\n- Sharded tests for parallelization: `chatInputOutputUser-shard-0.spec.ts`\n\n> **Note**: E2E tests use `.spec.ts` (Playwright convention). Unit tests use `.test.tsx` (Jest convention). Do not mix them.\n\n## Test Anatomy\n\n### Basic Test\n\n```typescript\nimport { expect, test } from \"../../fixtures\";\nimport { awaitBootstrapTest } from \"../../utils/await-bootstrap-test\";\n\ntest(\n  \"user should be able to run a flow successfully\",\n  { tag: [\"@release\", \"@workspace\"] },\n  async ({ page }) => {\n    await awaitBootstrapTest(page);\n\n    // Arrange: Create a flow\n    await page.getByTestId(\"blank-flow\").click();\n\n    // Act: Add components and run\n    await page.getByTestId(\"sidebar-search-input\").fill(\"Chat Output\");\n    // ... setup ...\n\n    // Assert: Verify result\n    await expect(page.getByTestId(\"build-status-success\")).toBeVisible({ timeout: 30000 });\n  },\n);\n```\n\n### With test.describe\n\n```typescript\ntest.describe(\"Flow Lock Feature\", () => {\n  test(\n    \"should lock and unlock a flow\",\n    { tag: [\"@release\", \"@api\"] },\n    async ({ page }) => {\n      // ...\n    },\n  );\n\n  test(\n    \"should prevent editing when locked\",\n    { tag: [\"@release\"] },\n    async ({ page }) => {\n      // ...\n    },\n  );\n});\n```\n\n### With Serial Mode (tests that depend on order)\n\n```typescript\ntest.describe.configure({ mode: \"serial\" });\n\ntest(\"step 1: create flow\", async ({ page }) => { /* ... */ });\ntest(\"step 2: edit flow\", async ({ page }) => { /* ... */ });\ntest(\"step 3: delete flow\", async ({ page }) => { /* ... */ });\n```\n\n### With Event Delivery Modes (streaming/polling/direct)\n\n```typescript\nimport { withEventDeliveryModes } from \"../../utils/withEventDeliveryModes\";\n\nwithEventDeliveryModes(\n  \"Document Q&A should work\",\n  { tag: [\"@release\", \"@starter-projects\"] },\n  async ({ page }) => {\n    // This test runs 3 times: streaming, polling, direct\n    // Each mode is configured automatically via route interception\n  },\n);\n```\n\n## Tags System\n\nEvery test MUST be tagged with `@release` — the release run greps for it, so an\nuntagged or wrongly-tagged spec silently drops out of release coverage. Add the\ndomain tag(s) below on top of `@release` (a test can have more than one). These\nsix are the only allowed tags; do not invent new ones.\n\n| Tag | Purpose | When to Use |\n|-----|---------|-------------|\n| `@release` | Part of the release run (required on every spec) | All tests |\n| `@workspace` | Workspace/flow management | Creating, editing, deleting flows |\n| `@api` | API-dependent features | Tests that call backend endpoints |\n| `@database` | Database operations | Tests involving persistence |\n| `@components` | Component-level tests | Individual component behavior |\n| `@starter-projects` | Template/starter project tests | Pre-built flow templates |\n\n```typescript\n// Right: tag your test\ntest(\"my feature test\", { tag: [\"@release\", \"@workspace\"] }, async ({ page }) => { ... });\n\n// Wrong: no tags — test can't be filtered\ntest(\"my feature test\", async ({ page }) => { ... });\n```\n\n## Custom Fixtures: Error Detection\n\n**Always import `test` and `expect` from `../../fixtures`, NOT from `@playwright/test`.**\n\n```typescript\n// Right\nimport { expect, test } from \"../../fixtures\";\n\n// Wrong — bypasses error detection\nimport { expect, test } from \"@playwright/test\";\n```\n\nWhy: The custom fixture automatically monitors all `/api/` responses and fails the test if:\n- HTTP 400, 404, 422, or 500 errors occur\n- Flow execution returns `error: true` in event streams\n- Python exceptions appear in streamed responses\n\nTo opt-in to expected errors (e.g., testing error handling):\n```typescript\ntest(\"should show error on invalid input\", { tag: [\"@release\"] }, async ({ page }) => {\n  page.allowFlowErrors();  // Allow flow errors for this test\n  // ... test that expects errors ...\n});\n```\n\n## Selector Strategy\n\n### Priority (in order of preference)\n\n1. **`getByTestId`** — Most stable, used 95% of the time in Langflow\n2. **`getByRole`** — For buttons, headings, and form elements\n3. **`getByText`** — For visible text content\n4. **`waitForSelector`** — For CSS selectors and dynamic elements\n5. **`locator`** — For complex selectors (CSS, XPath)\n\n### Common data-testid Patterns\n\n**Canvas & Navigation:**\n- `blank-flow` — New blank flow button\n- `sidebar-search-input` — Component search\n- `canvas_controls_dropdown` — Canvas controls menu\n- `fit_view`, `zoom_out`, `zoom_in` — Canvas controls\n- `react-flow-id` — ReactFlow canvas container\n\n**Component Fields:**\n- `popover-anchor-input-{fieldname}` — Input field for a component parameter\n- `input-chat-playground` — Playground chat input\n- `div-chat-message` — Chat message in playground\n\n**Actions:**\n- `add-component-button-{component}` — Add component to canvas\n- `button-send` — Send chat message\n- `button_run_{component}` — Run specific component\n- `publish-button`, `save-flow-button` — Flow actions\n- `edit-fields-button` — Toggle inspection panel field editor\n\n**Modals & Panels:**\n- `modal-title` — Modal heading\n- `icon-Globe` — Global variables\n- `icon-Lock` — Flow lock toggle\n- `session-selector` — Playground session switcher\n\n### Important: Global Variables and Badges\n\nWhen a component field has a global variable selected (`load_from_db: true` + `value: \"OPENAI_API_KEY\"`), the field renders a **badge** instead of an `<input>` element. This means `getByTestId(\"popover-anchor-input-api_key\")` will NOT find the element — it doesn't exist in the DOM.\n\nTemplates with global variables pre-selected: Market Research, Price Deal Finder, Research Agent.\nTemplates without (input IS rendered): Instagram Copywriter.\n\n## Core Helper Functions\n\nLocated in `src/frontend/tests/utils/`:\n\n| Function | What it Does | When to Use |\n|----------|-------------|-------------|\n| `awaitBootstrapTest(page)` | Waits for app to fully load | **Start of every test** |\n| `initialGPTsetup(page)` | Full setup: adjustView → updateComponents → selectModel → addKey → adjustView → unselectNodes | Tests that need OpenAI configured |\n| `adjustScreenView(page, opts?)` | Fit view + zoom out | After adding components to canvas |\n| `zoomOut(page, times)` | Zoom out N times | When components are too small |\n| `selectGptModel(page)` | Selects gpt-4o-mini for all Language Model nodes | GPT-dependent tests |\n| `addOpenAiInputKey(page)` | Fills OPENAI_API_KEY for all openai_api_key fields | Tests requiring API key |\n| `enableInspectPanel(page)` | Toggles inspection panel ON | **MUST call before `edit-fields-button`** |\n| `disableInspectPanel(page)` | Toggles inspection panel OFF | Cleanup after inspection |\n| `updateOldComponents(page)` | Clicks \"Update all\" if outdated components exist | After loading saved flows |\n| `unselectNodes(page)` | Clicks empty canvas area to deselect all nodes | After node operations |\n| `renameFlow(page, { flowName })` | Renames the current flow | Flow management tests |\n| `uploadFile(page, filename)` | Uploads a file from test assets | File upload tests |\n| `withEventDeliveryModes(...)` | Runs test 3x: streaming, polling, direct | Starter project tests |\n\n### initialGPTsetup Options\n\n```typescript\nawait initialGPTsetup(page);  // All steps\n\nawait initialGPTsetup(page, {\n  skipAdjustScreenView: true,\n  skipUpdateOldComponents: true,\n  skipSelectGptModel: true,\n});\n```\n\n### Inspection Panel Pattern (CRITICAL)\n\n```typescript\n// MUST enable inspection panel FIRST\nawait enableInspectPanel(page);\n\n// Click a node to select it\nawait page.getByTestId(\"title-OpenAI\").click();\n\n// Open field editor\nawait page.getByTestId(\"edit-fields-button\").click();\n\n// Toggle field visibility\nawait page.getByTestId(\"showmodel_name\").click();\n\n// Close field editor\nawait page.getByTestId(\"edit-fields-button\").click();\n```\n\n**If you skip `enableInspectPanel(page)`, the `edit-fields-button` will NOT be visible.**\n\n## Skip Patterns\n\n```typescript\n// Skip test if env var missing\ntest.skip(!process?.env?.OPENAI_API_KEY, \"OPENAI_API_KEY required to run this test\");\n\n// Skip test unconditionally with reason\ntest.skip(true, \"Feature not yet implemented with new designs\");\n```\n\n## Writing Good E2E Tests\n\n### Do:\n- **Tag every test** with `@release` (plus any domain tags that apply)\n- **Import from `../../fixtures`**, not `@playwright/test`\n- **Start with `awaitBootstrapTest(page)`** — always\n- **Use `getByTestId`** for stable selectors\n- **Set explicit timeouts** on `waitForSelector` and `expect(...).toBeVisible()` for async operations\n- **Test the complete user flow**: setup → action → verification\n- **Use `withEventDeliveryModes`** for tests that involve flow execution (chat, build)\n\n### Don't:\n- **Don't use `page.waitForTimeout()`** unless absolutely necessary — prefer `waitForSelector` or `expect().toBeVisible()`\n- **Don't hardcode API keys** — read from `process.env.OPENAI_API_KEY`\n- **Don't skip tests without a reason** — always provide the second argument to `test.skip()`\n- **Don't import from `@playwright/test`** — use the custom fixtures\n- **Don't forget `enableInspectPanel(page)`** before accessing `edit-fields-button`\n- **Don't assume input fields exist** when global variables are selected (badge renders instead)\n\n### Challenge Tests (Apply Here Too)\n\nE2E tests should also cover adversarial scenarios:\n- **Invalid input**: paste 10K characters, special characters (`<script>alert(1)</script>`), empty submissions\n- **Network interruption**: what happens if the user loses connection mid-build?\n- **Permission boundaries**: can a user access another user's flow via direct URL?\n- **Concurrent actions**: double-click delete, rapid chat messages\n- **Error recovery**: does the UI recover gracefully from a 500 error?\n\n## References\n\n- [Selector Patterns](references/selectors.md) — Complete data-testid catalog\n- [Helper Functions](references/helpers.md) — Detailed documentation of all 37+ utility functions\n- [Test Fixtures](references/fixtures.md) — Custom fixture error detection behavior\n",".agents/skills/ibm-a11y-pr-remediation/SKILL.md":"---\nname: ibm-a11y-pr-remediation\ndescription: Scan every frontend surface touched by the current PR/branch for IBM Equal Access Level 1 accessibility issues and fix all in-scope violations by default. Discovers changed files, maps them to routes/components/states, runs both axe and IBM engines, remediates until both are green (or only documented baselines remain), and reports back. Use when the user asks to check, scan, or clean up accessibility for \"this PR\", \"my branch\", or \"my changes\" and wants fixes applied, not just a report. For a single scoped audit report without a default fix pass, use ibm-a11y-level1-audit; for a route batch scan only, use ibm-a11y-route-scan.\ndisable-model-invocation: true\n---\n\n# IBM Level 1 PR Accessibility Remediation\n\nScope is **IBM Equal Access Level 1 only**. **Default mode is fix, not report-only.** Scan every frontend surface touched by the PR/branch and remediate all in-scope issues until both engines are green (or only documented baselines remain).\n\nThis skill is a PR-scoped orchestrator. It does not duplicate detailed engine/pattern guidance — read the linked skills for that:\n\n- [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) — which engine/test layer to use, POUR checklist, axe-vs-IBM gaps, Radix/AG-Grid gotchas, baselines.\n- [ibm-a11y-route-scan](../ibm-a11y-route-scan/SKILL.md) — Python scanner options for ad-hoc route batches.\n- [ibm-a11y-level1-audit](../ibm-a11y-level1-audit/SKILL.md) — Level 1 criteria references and report template, useful when the user wants a formal audit report for the PR instead of (or in addition to) fixes.\n- [frontend-i18n](../frontend-i18n/SKILL.md) — accessible names / UI strings must go through i18n.\n\n## Mandate\n\n1. Diff the PR (or current branch vs its merge base) for `src/frontend/**` changes.\n2. Map changed files → UI surfaces → routes / components / states to scan.\n3. Run **both** axe (Jest where applicable) and IBM Equal Access (Playwright `page.runA11yScan` and/or `scripts/a11y/a11y_scan.py` — see `ibm-a11y-testing-guide` / `ibm-a11y-route-scan`).\n4. **Fix every in-scope Level 1 violation** in the changed surfaces. Do not stop at a findings list unless the user says **report only** (in that case, hand off to `ibm-a11y-level1-audit`).\n5. Re-scan until assert mode passes. Add/update a11y specs when coverage is missing.\n6. Report what changed, commands run, and any baselined/deferred debt.\n\nDo **not** invent new tag names, silently disable scans, or expand into IBM Level 2/3 unless asked.\n\n## Progress checklist\n\n```\nIBM L1 PR A11y:\n- [ ] 1. Collect changed frontend files\n- [ ] 2. Map files → surfaces / routes / states\n- [ ] 3. Scan (IBM + axe)\n- [ ] 4. Fix all in-scope violations\n- [ ] 5. Add/update tests if needed\n- [ ] 6. Re-scan assert-green\n- [ ] 7. Report back\n```\n\n## 1. Collect changed frontend files\n\nPrefer the PR merge base when a PR exists; otherwise the branch merge base vs `main`/`master`.\n\n```bash\n# PR number known\ngh pr diff <n> --name-only | grep -E '^src/frontend/' || true\n\n# Current branch vs upstream default\nBASE=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD origin/master)\ngit diff --name-only \"$BASE\"...HEAD -- 'src/frontend/**'\n\n# Include uncommitted work when the user is mid-change\ngit diff --name-only HEAD -- 'src/frontend/**'\ngit diff --name-only --cached -- 'src/frontend/**'\ngit ls-files --others --exclude-standard 'src/frontend/**'\n```\n\nInclude:\n- `src/frontend/src/**/*.{tsx,ts,jsx,js,css}` (UI)\n- `src/frontend/tests/a11y/**` (existing coverage)\n- Locale files only when they change accessible names / labels\n\nSkip pure non-UI churn unless it affects a11y (e.g. test helpers that change focus / ARIA). If **no** frontend files changed, say so and stop.\n\n## 2. Map files → surfaces\n\nFor each changed file, identify:\n\n| Change type | Scan target |\n|-------------|-------------|\n| Page / route | That route + meaningful states (empty/populated/modal/mobile) |\n| Shared component (`TableComponent`, dialogs, menus) | **Every** consumer page that uses it — not only the file you touched |\n| Primitive | Jest axe on the primitive + any Playwright surface that embeds it |\n| Spec / baseline only | Re-run that spec; no product fix unless it fails |\n| `a11y_routes.json` | Update static coverage; run static or route scan |\n\nList interactive controls and states (default, empty, populated, open modal / menu, selected row, error, mobile). Prefer existing specs under `src/frontend/tests/a11y/`.\n\n## 3. Scan (both engines must pass)\n\nAutomated a11y is not one tool — see `ibm-a11y-testing-guide` for the full engine comparison and gotchas. Summary:\n\n- **axe-core** — Jest `axe()` (`@/utils/a11y-test`), jsdom-only.\n- **IBM Equal Access** — stricter on ARIA structure and keyboard semantics. Playwright `page.runA11yScan(label)` for stateful surfaces (modals/menus/selected/editing); `scripts/a11y/a11y_scan.py` for default-loaded page only.\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# Python scanner playwright deps are NOT in default uv sync.\n# One-time: uv run --with playwright playwright install chromium\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes /settings/<route> \\\n  --out /tmp/a11y.json --markdown /tmp/a11y.md --timeout-ms 45000\n```\n\n`RUN_A11Y=true` runs the scan; `RUN_A11Y_ASSERT=true` fails on new violations. After changing a shared component, re-scan every page that uses it.\n\nFor component-only changes:\n\n```bash\ncd src/frontend\nnpx jest path/to/<name>.a11y.test.tsx --runInBand\n```\n\nDo not invent findings — prefer scanner output plus manual Level 1 spot checks scanners miss (keyboard trap both ways, focus restore, 320px reflow, color-not-only).\n\n## 4. Fix all in-scope Level 1 violations\n\nDefault: **fix**. Only list proposed fixes without editing if the user asked **report only** — then hand off to `ibm-a11y-level1-audit` for the formal report format.\n\nRules:\n- Prefer semantic HTML over ARIA.\n- Follow the Langflow patterns in `ibm-a11y-testing-guide` (AG Grid, Radix `asChild`, focus restore, icon-only `aria-label`).\n- Route new UI strings / `aria-label`s through i18n (`t(...)`, all locale files) per `frontend-i18n`.\n- Keep fixes minimal; do not refactor unrelated UI.\n- Do **not** silently disable scans. Use IBM baselines under `src/frontend/tests/a11y/baselines/` only for documented framework debt.\n- Map each issue to a Level 1 WCAG/IBM id; defer anything listed as Level 2/3 in the criteria guide (`ibm-a11y-level1-audit/references/ibm-level1-criteria.md`) unless the user expands scope.\n\n### Manual Level 1 spot checks (when relevant)\n\n- **2.1.1 / 2.1.2:** Tab and Shift+Tab; Escape closes overlays; no trap.\n- **2.4.3 / 2.4.7:** Focus order matches visual order; focus ring visible.\n- **1.4.10:** 320px / ~400% zoom — no essential horizontal scroll.\n- **1.4.1:** Status/errors not color-only.\n- **3.3.1 / 3.3.2:** Errors in text and tied to fields; inputs labeled.\n\n## 5. Tests / coverage\n\n| Surface | Spec |\n|---------|------|\n| Static routes | `static-routes.a11y.spec.ts` (+ `scripts/a11y/a11y_routes.json`) |\n| Auth | `auth-pages.a11y.spec.ts` |\n| Core pages | `core-pages.a11y.spec.ts` |\n| Data-rich | `files.a11y.spec.ts`, `api-keys.a11y.spec.ts`, `global-variables.a11y.spec.ts` |\n| Other data-rich | `data-rich-routes.a11y.spec.ts` |\n\nIf you fixed a state with no scan, add one (and keyboard tests for custom keyboard behavior) following `files.a11y.spec.ts` / `api-keys.a11y.spec.ts` patterns.\n\nTag every Playwright a11y test `@release` plus a domain tag (`@workspace` / `@api` / `@database` / `@components` / `@starter-projects`). Import `test`/`expect` from `../fixtures`.\n\n## 6. Re-scan and report\n\nRe-run the same scans with `RUN_A11Y_ASSERT=true`. Each finding → `fixed` | `baselined` | `open`.\n\nWhen done, state:\n- Changed frontend files considered\n- Surfaces / states scanned\n- Fixes applied (files + what)\n- Commands run — whether **both** axe and IBM ran and both reported zero\n- Specs / baselines added or updated\n- States skipped and why\n- Remaining risk or accepted limitation\n","CLAUDE.md":"# Claude Agent Guidelines & System Prompt\n\nPath: `CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/CLAUDE.md)","DESIGN.md":"# UI Design System & Style Tokens\n\nPath: `DESIGN.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/DESIGN.md)",".cursor/rules/docs_development.mdc":"# Cursor Rule: docs_development\n\nPath: `.cursor/rules/docs_development.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.cursor/rules/docs_development.mdc)","docs/agents/ANTI-PATTERNS.md":"# Subagent: anti-patterns\n\nPath: `docs/agents/ANTI-PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/ANTI-PATTERNS.md)","docs/agents/ARCHITECTURE.md":"# Subagent: architecture\n\nPath: `docs/agents/ARCHITECTURE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/ARCHITECTURE.md)","docs/agents/COMPONENTS.md":"# Subagent: components\n\nPath: `docs/agents/COMPONENTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/COMPONENTS.md)","docs/agents/CONTRACTS.md":"# Subagent: contracts\n\nPath: `docs/agents/CONTRACTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/CONTRACTS.md)","docs/agents/PHILOSOPHY.md":"# Subagent: philosophy\n\nPath: `docs/agents/PHILOSOPHY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/PHILOSOPHY.md)","docs/agents/TESTING.md":"# Subagent: testing\n\nPath: `docs/agents/TESTING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/TESTING.md)",".agents/skills/backend-code-review/references/architecture-rule.md":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/architecture-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/architecture-rule.md)",".agents/skills/backend-code-review/references/db-schema-rule.md":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/db-schema-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/db-schema-rule.md)",".agents/skills/backend-code-review/references/repositories-rule.md":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/repositories-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/repositories-rule.md)",".agents/skills/backend-code-review/references/sqlalchemy-rule.md":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/sqlalchemy-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/sqlalchemy-rule.md)",".agents/skills/component-refactoring/references/complexity-patterns.md":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/complexity-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/complexity-patterns.md)",".agents/skills/component-refactoring/references/component-splitting.md":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/component-splitting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/component-splitting.md)",".agents/skills/component-refactoring/references/hook-extraction.md":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/hook-extraction.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/hook-extraction.md)",".agents/skills/e2e-testing/references/fixtures.md":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/fixtures.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/fixtures.md)",".agents/skills/e2e-testing/references/helpers.md":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/helpers.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/helpers.md)",".agents/skills/e2e-testing/references/selectors.md":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/selectors.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/selectors.md)",".agents/skills/frontend-code-review/references/business-logic.md":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/business-logic.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/business-logic.md)",".agents/skills/frontend-code-review/references/code-quality.md":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/code-quality.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/code-quality.md)",".agents/skills/frontend-code-review/references/performance.md":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/performance.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/performance.md)",".agents/skills/frontend-query-mutation/references/query-patterns.md":"# References Skill\n\nPath: `.agents/skills/frontend-query-mutation/references/query-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/query-patterns.md)",".agents/skills/frontend-query-mutation/references/runtime-rules.md":"# References Skill\n\nPath: `.agents/skills/frontend-query-mutation/references/runtime-rules.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/runtime-rules.md)",".agents/skills/frontend-testing/references/async-testing.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/async-testing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/async-testing.md)",".agents/skills/frontend-testing/references/checklist.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/checklist.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/checklist.md)",".agents/skills/frontend-testing/references/common-patterns.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/common-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/common-patterns.md)",".agents/skills/frontend-testing/references/domain-components.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/domain-components.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/domain-components.md)",".agents/skills/frontend-testing/references/mocking.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/mocking.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/mocking.md)",".agents/skills/frontend-testing/references/workflow.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/workflow.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/workflow.md)",".agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md":"# References Skill\n\nPath: `.agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md)",".agents/skills/ibm-a11y-level1-audit/references/report-template.md":"# References Skill\n\nPath: `.agents/skills/ibm-a11y-level1-audit/references/report-template.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/references/report-template.md)"},"files":{".agents/skills/frontend-query-mutation/SKILL.md":"---\nname: frontend-query-mutation\ndescription: Guide for implementing Langflow frontend query and mutation patterns with Axios and TanStack React Query v5. Trigger when creating or updating API hooks in controllers/API/queries, consuming UseRequestProcessor in components, deciding whether to use useQuery or useMutation, handling conditional queries, cache invalidation, mutation error handling, or migrating legacy API calls to the query hook pattern.\n---\n\n# Frontend Query & Mutation\n\n## Intent\n\n- Keep API hooks in `controllers/API/queries/{domain}/` as the organized source of truth.\n- Use `UseRequestProcessor` for consistent retry, invalidation, and error handling.\n- Use Axios `api` instance for all HTTP calls (never raw `fetch` for REST).\n- Use `performStreamingRequest()` for streaming operations (build, chat).\n- Keep cache invalidation in mutation `onSettled` callbacks.\n\n## Architecture Overview\n\nLangflow uses **Axios + TanStack React Query v5** for data fetching. There is no oRPC or contract layer. The architecture is:\n\n```\nComponent\n  -> API hook (controllers/API/queries/{domain}/use-{verb}-{resource}.ts)\n    -> UseRequestProcessor (controllers/API/services/request-processor.ts)\n      -> useQuery / useMutation (TanStack React Query)\n        -> queryFn / mutationFn\n          -> api.get/post/patch/delete (Axios instance from controllers/API/api.tsx)\n```\n\n### Key Files\n\n| File | Purpose |\n|------|---------|\n| `controllers/API/api.tsx` | Axios instance, `ApiInterceptor` component, `performStreamingRequest()` |\n| `controllers/API/services/request-processor.ts` | `UseRequestProcessor` hook wrapping `useQuery`/`useMutation` with retry defaults |\n| `controllers/API/helpers/constants.ts` | URL constants (`URLs`) and `getURL()` helper |\n| `controllers/API/queries/{domain}/` | Domain-organized query and mutation hooks |\n| `types/api/index.ts` | `useQueryFunctionType`, `useMutationFunctionType` type helpers |\n| `stores/` | Zustand stores for client-side state |\n\n## Workflow\n\n1. Identify the change surface.\n   - Read `references/query-patterns.md` for hook structure, naming, URL patterns, and query/mutation call-site shape.\n   - Read `references/runtime-rules.md` for conditional queries, invalidation, error handling, and streaming.\n   - Read both references when a task spans hook creation and runtime behavior.\n2. Implement the hook following existing patterns.\n   - Use `UseRequestProcessor` for `query()` and `mutate()` wrappers.\n   - Use the `api` Axios instance for all HTTP calls.\n   - Use `getURL()` for constructing API paths.\n   - Type the hook with `useQueryFunctionType` or `useMutationFunctionType`.\n3. Preserve Langflow conventions.\n   - Query keys are arrays with the hook name: `[\"useGetFlows\"]`.\n   - Mutations invalidate related queries in `onSettled`.\n   - Zustand store updates happen in the query/mutation function, not in components.\n   - `UseRequestProcessor` provides default retry (5 for queries, 3 for mutations) with exponential backoff.\n\n## Files Commonly Touched\n\n- `controllers/API/queries/{domain}/use-get-*.ts` (query hooks)\n- `controllers/API/queries/{domain}/use-post-*.ts` (create mutations)\n- `controllers/API/queries/{domain}/use-patch-*.ts` (update mutations)\n- `controllers/API/queries/{domain}/use-delete-*.ts` (delete mutations)\n- `controllers/API/helpers/constants.ts` (URL constants)\n- `types/api/index.ts` (API type definitions)\n- `stores/` (Zustand store updates triggered by queries)\n\n## References\n\n- Use `references/query-patterns.md` for hook structure, naming conventions, directory layout, and anti-patterns.\n- Use `references/runtime-rules.md` for conditional queries, invalidation, `mutate` versus `mutateAsync`, error handling, and streaming requests.\n\nTreat this skill as the single query and mutation entry point for Langflow frontend work. Keep detailed rules in the reference files instead of duplicating them in project docs.\n",".agents/skills/frontend-i18n/SKILL.md":"---\nname: frontend-i18n\ndescription: Add, change, or review user-facing text in the Langflow frontend using the i18n system (i18next / react-i18next). Use whenever a change adds or edits UI strings — labels, buttons, tooltips, modals, toasts, error messages, empty states — or when reviewing a diff that contains user-facing text. Every user-facing string must go through the translation system and every new key must exist in ALL locale files. Do NOT use for backend strings, log messages, or code identifiers.\n---\n\n# Frontend i18n\n\nThe Langflow frontend is internationalized with **i18next + react-i18next**. Locales live in `src/frontend/src/locales/` — currently `en`, `de`, `es`, `fr`, `ja`, `pt`, `zh-Hans`. A hardcoded user-facing string, or a key missing from one locale, ships broken UI for part of the user base.\n\n## The two rules\n\n1. **Every user-facing string goes through `t(...)`** — never hardcoded JSX text for labels, buttons, tooltips, modals, toasts, errors, placeholders, `aria-label`s, or empty states.\n2. **Every new key is added to ALL locale files in the same PR** (`en.json`, `de.json`, `es.json`, `fr.json`, `ja.json`, `pt.json`, `zh-Hans.json`). `fallbackLng: \"en\"` means a missing key silently shows English — it won't crash, which is exactly why reviews must catch it.\n\n## How it works here\n\n- Config: `src/frontend/src/i18n.ts` — a custom i18next instance. `en` is bundled statically; other languages are **lazy-loaded** by `loadLanguage()` via dynamic import. Language preference comes from `localStorage.getItem(\"languagePreference\")`, normalized (e.g. `zh-CN` → `zh-Hans`, unknown → `en`).\n- Keys are **flat, dot-namespaced by feature**: `deleteModal.title`, `errors.fileTooLarge`, `crash.restartButton`. Follow the existing namespace of the area you're touching; create a new prefix only for a genuinely new surface.\n- Interpolation uses `{{variable}}` in the string and an options object in the call.\n\n## Adding a string (the pattern)\n\n```tsx\nimport { useTranslation } from \"react-i18next\";\n\nconst { t } = useTranslation();\n\n<span>{t(\"deleteModal.title\")}</span>\n<p>{t(\"errors.fileTooLarge\", { maxSizeMB: \"10MB\" })}</p>\n```\n\nIn `locales/en.json` (and **every** other locale file):\n\n```json\n\"errors.fileTooLarge\": \"The file size is too large. Please select a file smaller than {{maxSizeMB}}.\"\n```\n\nFor the non-English locales, write a real translation when confident; if not, add the key with the English value and flag it in the PR — a present-but-untranslated key is visible and searchable, a missing key is invisible.\n\n## Rules that prevent real bugs\n\n- **Don't concatenate translated fragments** (`t(\"a\") + name + t(\"b\")`) — word order differs across languages. Use one key with interpolation: `t(\"greeting\", { name })`.\n- **Don't put JSX/HTML inside translation strings**; compose in JSX around `t()` calls (see how `crash.descriptionBefore` / `crash.githubIssues` / `crash.descriptionAfter` split around a link).\n- **Plurals**: use i18next plural forms (`_one` / `_other` suffixes), not `count === 1 ? ... : ...`.\n- **Keys are code**: renaming or deleting a key means updating **all seven** locale files — an orphan key in one file is dead weight; a rename missed in one file is a regression.\n- Dates/numbers/currency: format with `Intl.*` using the active locale, never hardcoded formats.\n\n## Review checklist (apply to any diff with UI text)\n\n- [ ] No hardcoded user-facing string in JSX (including `placeholder`, `title`, `aria-label`, toast/error text).\n- [ ] Every new key exists in **all** locale files (grep the key across `src/frontend/src/locales/*.json`).\n- [ ] No concatenation of translated fragments; interpolation used instead.\n- [ ] Removed/renamed keys cleaned up in all locales (no orphans).\n- [ ] Key follows the existing dot-namespace of the feature area.\n\nQuick check for a key across locales:\n\n```bash\nfor f in src/frontend/src/locales/*.json; do grep -L '\"my.new.key\"' \"$f\"; done\n```\n\n(prints the locale files where the key is **missing** — should print nothing)\n",".agents/skills/frontend-code-review/SKILL.md":"---\nname: frontend-code-review\ndescription: \"Review frontend code (.tsx, .ts, .js files) for quality, performance, and correctness against Langflow's frontend conventions. Supports pending-change reviews and file-targeted reviews.\"\n---\n\n# Frontend Code Review\n\n## When to use this skill\n\nUse this skill whenever the user asks to **review, analyze, or improve** frontend code (`.tsx`, `.ts`, `.js` files) under the `src/frontend/` directory. Supports the following review modes:\n\n1. **Pending-change review** -- inspect staged or working-tree files slated for commit and flag checklist violations before submission.\n2. **File-targeted review** -- review the specific file(s) the user names and report the relevant checklist findings.\n\nDo NOT use this skill when:\n\n- The request is about backend code (`.py` files under `src/backend/`).\n- The user is not asking for a review/analysis/improvement of frontend code.\n- The scope is outside `src/frontend/` (unless the user explicitly asks to review frontend-related changes elsewhere).\n\n## How to use this skill\n\nFollow these steps when using this skill:\n\n1. **Identify the review mode** (pending-change vs file-targeted) based on the user's input. Keep the scope tight: review only what the user provided or explicitly referenced.\n2. Follow the rules defined in the **Checklist** to perform the review. If no checklist rule matches, apply **General Review Rules** as a fallback.\n3. Compose the final output strictly following the **Required Output Format**.\n\nNotes when using this skill:\n\n- Always include actionable fixes or suggestions (including possible code snippets).\n- Use `File:Line` references when a file path and line numbers are available; otherwise, use the most specific identifier you can.\n- The Langflow frontend uses React 19, TypeScript 5.4, Vite 7 with SWC, Zustand for state management, TanStack React Query for server state, @xyflow/react v12 for graph visualization, Radix UI + shadcn-ui components, Tailwind CSS v3, and Biome for linting/formatting.\n\n## Checklist\n\n- **Code quality**: For any reviewed file, follow [references/code-quality.md](references/code-quality.md) to check styling conventions, TypeScript usage, Biome compliance, and component patterns.\n- **Performance**: If the review scope involves React components, hooks, Zustand stores, React Query usage, or @xyflow/react node rendering, follow [references/performance.md](references/performance.md) to check for re-render issues, memoization, and data flow patterns.\n- **Business logic**: If the review scope involves custom nodes (GenericNode), flow state, API calls, the component system, global variables, or the inspection panel, follow [references/business-logic.md](references/business-logic.md) to check for Langflow-specific correctness.\n- **i18n**: If the diff adds or changes user-facing text (labels, buttons, tooltips, modals, toasts, errors, placeholders, aria-labels), follow the [frontend-i18n skill](../frontend-i18n/SKILL.md): no hardcoded UI strings — everything through `t(...)` — and every new key present in **all** locale files (`src/frontend/src/locales/*.json`).\n\n## General Review Rules\n\n### 1. Security Review\n\nCheck for:\n- XSS vulnerabilities (dangerouslySetInnerHTML, unescaped user input)\n- Sensitive data exposure in client-side code\n- Insecure direct object references in API calls\n- Hardcoded secrets, tokens, or API keys\n\n### 2. Accessibility Review\n\nCheck for:\n- Missing aria labels on interactive elements\n- Keyboard navigation support\n- Proper use of semantic HTML elements\n- Color contrast issues in custom styling\n\n### 3. Code Quality Review\n\nCheck for:\n- Code duplication (DRY violations — extract at 3+ identical usages)\n- Functions/components doing too much (SRP violations — if you need \"and\" to describe it, split it)\n- Deep nesting or complex conditionals (prefer early returns and guard clauses)\n- Magic numbers/strings without named constants\n- Poor naming: generic names (`data`, `result`, `temp`), missing verb prefixes on functions, missing `is`/`has`/`can`/`should` prefixes on booleans\n- Missing error handling or error boundaries\n- Incomplete TypeScript type coverage (no `any`, no `as any` casts)\n- Comments that explain WHAT instead of WHY\n- Commented-out code (use version control)\n- Boolean parameters that switch component behavior (use two components instead)\n- Mutable patterns where `const` or immutable alternatives exist\n- Production files exceeding ~500 lines (red flag at 600+)\n- `console.log` in production code (Biome flags this)\n\n### 4. Testing Impact Review\n\nCheck for:\n- Changes to data-testid attributes that may break E2E tests\n- Modified component interfaces that require test updates\n- New interactive elements missing data-testid attributes\n\n### 5. Pre-Commit Verification\n\nFor pending-change reviews, verify:\n- `npm run format` (Biome formatter) — zero diffs\n- `npm run lint` (Biome linter) — zero errors\n- `npm test` (Jest) — zero failures\n\n## Required Output Format\n\nWhen this skill is invoked, the response must exactly follow one of the two templates:\n\n### Template A (any findings)\n\n```markdown\n# Code Review\n\nFound <N> urgent issues that need to be fixed:\n\n## 1. <brief description of issue>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n\n### Suggested fix\n\n<brief description of suggested fix with code example>\n\n---\n\n... (repeat for each urgent issue) ...\n\nFound <M> suggestions for improvement:\n\n## 1. <brief description of suggestion>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n\n### Suggested fix\n\n<brief description of suggested fix with code example>\n\n---\n\n... (repeat for each suggestion) ...\n```\n\n- If there are no urgent issues, omit that section. If there are no suggestions, omit that section.\n- If the issue count exceeds 10, summarize as \"10+ urgent issues\" or \"10+ suggestions\" and output only the first 10 items.\n- Do not compress the blank lines between sections; keep them as-is for readability.\n- If Template A is used (there are issues to fix) and at least one issue requires code changes, append a brief follow-up question after the structured output asking whether the user wants the suggested fixes applied. For example: \"Would you like me to apply the suggested fixes to address these issues?\"\n\n### Template B (no issues)\n\n```markdown\n## Code Review\n\nNo issues found.\n```\n",".agents/skills/backend-code-review/SKILL.md":"---\nname: backend-code-review\ndescription: Review backend code for quality, security, maintainability, and best practices based on established checklist rules. Use when the user requests a review, analysis, or improvement of backend files (e.g., `.py`) under the `src/backend/` directory. Do NOT use for frontend files (e.g., `.tsx`, `.ts`, `.js`). Supports pending-change review, code snippets review, and file-focused review.\n---\n\n# Backend Code Review\n\n## When to use this skill\n\nUse this skill whenever the user asks to **review, analyze, or improve** backend code (e.g., `.py`) under the `src/backend/` directory. Supports the following review modes:\n\n- **Pending-change review**: when the user asks to review current changes (inspect staged/working-tree files slated for commit to get the changes).\n- **Code snippets review**: when the user pastes code snippets (e.g., a function/class/module excerpt) into the chat and asks for a review.\n- **File-focused review**: when the user points to specific files and asks for a review of those files (one file or a small, explicit set of files, e.g., `src/backend/base/langflow/api/v1/flows.py`).\n\nDo NOT use this skill when:\n\n- The request is about frontend code or UI (e.g., `.tsx`, `.ts`, `.js`, `src/frontend/`).\n- The user is not asking for a review/analysis/improvement of backend code.\n- The scope is not under `src/backend/` (unless the user explicitly asks to review backend-related changes outside `src/backend/`).\n\n## How to use this skill\n\nFollow these steps when using this skill:\n\n1. **Identify the review mode** (pending-change vs snippet vs file-focused) based on the user's input. Keep the scope tight: review only what the user provided or explicitly referenced.\n2. Follow the rules defined in **Checklist** to perform the review. If no Checklist rule matches, apply **General Review Rules** as a fallback to perform the best-effort review.\n3. Compose the final output strictly following the **Required Output Format**.\n\nNotes when using this skill:\n- Always include actionable fixes or suggestions (including possible code snippets).\n- Use best-effort `File:Line` references when a file path and line numbers are available; otherwise, use the most specific identifier you can.\n\n## Checklist\n\n- db schema design: if the review scope includes code/files under `src/backend/base/langflow/services/database/models/` or Alembic migrations under `src/backend/base/langflow/alembic/versions/`, follow [references/db-schema-rule.md](references/db-schema-rule.md) to perform the review\n- architecture: if the review scope involves route/service/model layering, dependency direction, or moving responsibilities across modules, follow [references/architecture-rule.md](references/architecture-rule.md) to perform the review\n- service abstraction: if the review scope contains table/model operations (e.g., `select(...)`, `session.execute(...)`, joins, CRUD) and is not already inside a service under `src/backend/base/langflow/services/`, follow [references/repositories-rule.md](references/repositories-rule.md) to perform the review\n- sqlalchemy patterns: if the review scope involves SQLAlchemy/SQLModel session/query usage, db transaction/crud usage, `session_scope()` usage, or raw SQL usage, follow [references/sqlalchemy-rule.md](references/sqlalchemy-rule.md) to perform the review\n\n## General Review Rules\n\n### 1. Security Review\n\nCheck for:\n- SQL injection vulnerabilities (especially raw `text()` queries with string interpolation). Consequence: attacker can read/modify/delete any data in the database.\n- Server-Side Request Forgery (SSRF) in component HTTP calls. Consequence: attacker uses the server to scan internal networks or access cloud metadata endpoints.\n- Command injection (especially in subprocess or shell-executing components). Consequence: attacker gains shell access to the server.\n- Insecure deserialization (pickle, yaml.load without SafeLoader). Consequence: arbitrary code execution on the server.\n- Hardcoded secrets/credentials. Consequence: secrets leak via git history and are impossible to fully revoke.\n- Improper authentication/authorization (missing `CurrentActiveUser` dependency). Consequence: unauthenticated users can access protected endpoints.\n- Insecure direct object references (missing `user_id` scoping on queries). Consequence: user A can read/modify user B's flows, variables, API keys.\n- Path traversal in file storage operations. Consequence: attacker reads arbitrary server files (e.g., `/etc/passwd`, `.env`).\n\n### 2. Performance Review\n\nCheck for:\n- N+1 queries (especially in loops calling `session.execute()`). Consequence: 100 flows = 101 DB queries instead of 2; page load goes from 50ms to 5s.\n- Missing database indexes on frequently queried columns. Consequence: full table scans on large datasets; queries degrade from O(log n) to O(n).\n- Memory leaks (unbounded caches, retained references in long-lived services). Consequence: server OOM after hours of operation; pods restart in production.\n- Blocking operations in async code (`time.sleep()`, synchronous I/O, CPU-bound work without `run_in_executor`). Consequence: entire event loop stalls; all concurrent requests hang until the blocking call completes.\n- Missing caching opportunities for expensive computations. Consequence: repeated computation of the same result on every request.\n- Large result sets loaded entirely into memory without pagination. Consequence: memory spike + slow response when user has 10K+ flows.\n\n### 3. Code Quality Review\n\nCheck for:\n- Code forward compatibility with Python 3.10-3.13\n- Code duplication (DRY violations — extract when the *exact same business rule* is duplicated in 3+ places)\n- Functions doing too much (SRP violations — if you need \"and\" to describe it, split it)\n- Deep nesting / complex conditionals (prefer early returns and guard clauses)\n- Magic numbers/strings (extract to named constants or enums)\n- Poor naming: unclear abbreviations, misleading names, generic names (`data`, `result`, `obj`, `temp`). Functions should use verbs (`get`, `create`, `validate`). Booleans should use prefixes (`is_`, `has_`, `can_`, `should_`).\n- Missing error handling (bare `except`, swallowed exceptions, silent failures)\n- Incomplete type coverage (use strong typing, avoid `Any` where a concrete type is known)\n- Use Python 3.10+ union syntax (`X | Y` not `Union[X, Y]`, `X | None` not `Optional[X]`)\n- Use `TYPE_CHECKING` guard for imports only needed for type annotations (prevents circular imports)\n- Use `Annotated[Type, Depends(...)]` with project aliases (`CurrentActiveUser`, `DbSession`, `DbSessionReadOnly`) for FastAPI DI\n- Google-style docstrings (enforced by Ruff): `Args:`, `Returns:`, `Raises:` sections for public functions\n- Violations of SOLID principles\n- YAGNI violations (code that anticipates future needs without a present requirement)\n- Line length exceeding 120 characters (project Ruff config)\n- Comments that explain WHAT instead of WHY (comments should only explain reasoning, not restate code)\n- Commented-out code (use version control instead)\n- Boolean parameters that switch function behavior (split into two named functions instead)\n- Mutable shared state where immutable alternatives exist (prefer returning new objects over mutation)\n\n### 4. File Structure Review\n\nCheck for:\n- Production files exceeding ~500 lines of code (excluding imports, types, and docstrings). Files above 600 lines are a red flag and should be split by responsibility. Why: Files above 500 lines have statistically higher defect rates and take longer to review. They signal multiple responsibilities (SRP violation). In Langflow, services like `DatabaseService` that grow beyond this limit should have their CRUD operations extracted to dedicated modules.\n- Test files exceeding ~1000 lines. Split by logical grouping if exceeded.\n- No more than 5 functions with different responsibilities in a single file (per AGENTS-example.md).\n- Each file has a single reason to exist and a single reason to change (SRP).\n- No generic file names: `utils.py`, `helpers.py`, `misc.py`, `common.py` as standalone files. Why: A file named `utils.py` becomes a dumping ground for unrelated functions. Within months it has 50+ functions covering formatting, validation, parsing, and HTTP calls — violating SRP. Each function group should be in a file named after its responsibility (`formatting.py`, `validation.py`).\n\n### 5. Testing Review\n\nCheck for:\n- Missing test coverage for new code paths\n- Tests that don't test behavior (testing implementation details)\n- Flaky test patterns (time-dependent, order-dependent, external-service-dependent)\n- Proper use of `pytest.mark.asyncio` for async tests\n- Excessive mocking (prefer real integrations per project conventions)\n- Coverage target: 80% (minimum acceptable: 75%)\n- Test anti-patterns: The Liar (passes but doesn't verify claimed behavior), The Mirror (asserts exactly what code does), The Giant (50+ lines setup), The Mockery (tests only mock setup), The Inspector (coupled to implementation), The Chain Gang (depends on execution order), The Flaky (inconsistent results)\n\n**Happy path tests are the foundation but are NOT enough.** Tests MUST also challenge the code to find real defects:\n\n- **Unexpected inputs**: `None`, `\"\"`, `[]`, `{}`, `0`, `-1`, `UUID(\"00000000-0000-0000-0000-000000000000\")`\n- **Boundary values**: max length strings, exactly at the limit, one past the limit, zero items, max items\n- **Malformed data**: missing required fields, extra unexpected fields, wrong types, invalid formats\n- **Error states**: what happens when the database is down? When an external API returns 500? When the user doesn't exist?\n- **What should NOT happen**: verify that user A CANNOT access user B's flows. Verify that a deleted flow returns 404. Verify that invalid `endpoint_name` is rejected with 422.\n- **Error messages and types**: not just that it fails, but that it fails with the RIGHT exception and the RIGHT message\n- **Concurrency**: what happens when two requests try to update the same flow simultaneously?\n\n**Write tests based on REQUIREMENTS/SPEC, not on what the source code currently does.** This is how you catch bugs where the code diverges from expected behavior.\n\n**When a test fails:** first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.\n\n### 6. Observability Review\n\nCheck for:\n- Use the async logger from `lfx.log.logger` with `a`-prefixed methods (`adebug`, `ainfo`, `awarning`, `aerror`, `aexception`). Never use `print()` or stdlib `logging`.\n- Log at key decision points and boundaries, not inside tight loops\n- Include: operation name, relevant IDs, outcome (success/failure), duration if relevant\n- Correct log levels: ERROR (broken, needs attention), WARN (degraded but recoverable), INFO (significant events), DEBUG (diagnostic, off in prod)\n- **ZERO PII TOLERANCE**: Never log email addresses, user names, phone numbers, tokens, passwords. Only approved identifiers: `user_id`, `flow_id`, `session_id`\n- No `print()` statements — these go to production logs\n- Use `{e!s}` for string representation of exceptions in log messages\n\n### 7. Pre-Commit Verification\n\nFor pending-change reviews, verify the author has run:\n- `make format_backend` (Ruff formatter) — inconsistent formatting creates noisy diffs that hide real changes in code review. Format first, review second.\n- `make lint` (MyPy type checking) — type errors caught at lint time are 10x cheaper to fix than runtime crashes in production. Langflow services use duck typing via `Service` base class; MyPy catches mismatches early.\n- `make unit_tests` (pytest) — a failing test means the change breaks existing behavior. Never merge with failing tests; investigate whether the code or the test is wrong.\n\n## Required Output Format\n\nWhen this skill is invoked, the response must exactly follow one of the two templates:\n\n### Template A (any findings)\n\n```markdown\n# Code Review Summary\n\nFound <X> critical issues need to be fixed:\n\n## 🔴 Critical (Must Fix)\n\n### 1. <brief description of the issue>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<detailed explanation and references of the issue>\n\n#### Suggested Fix\n\n1. <brief description of suggested fix>\n2. <code example> (optional, omit if not applicable)\n\n---\n... (repeat for each critical issue) ...\n\nFound <Y> suggestions for improvement:\n\n## 🟡 Suggestions (Should Consider)\n\n### 1. <brief description of the suggestion>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<detailed explanation and references of the suggestion>\n\n#### Suggested Fix\n\n1. <brief description of suggested fix>\n2. <code example> (optional, omit if not applicable)\n\n---\n... (repeat for each suggestion) ...\n\nFound <Z> optional nits:\n\n## 🟢 Nits (Optional)\n### 1. <brief description of the nit>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<explanation and references of the optional nit>\n\n#### Suggested Fix\n\n- <minor suggestions>\n\n---\n... (repeat for each nits) ...\n\n## ✅ What's Good\n\n- <Positive feedback on good patterns>\n```\n\n- If there are no critical issues or suggestions or optional nits or good points, just omit that section.\n- If the issue number is more than 10, summarize as \"Found 10+ critical issues/suggestions/optional nits\" and only output the first 10 items.\n- Don't compress the blank lines between sections; keep them as-is for readability.\n- If there is any issue that requires code changes, append a brief follow-up question to ask whether the user wants to apply the fix(es) after the structured output. For example: \"Would you like me to use the Suggested fix(es) to address these issues?\"\n\n### Template B (no issues)\n\n```markdown\n## Code Review Summary\n✅ No issues found.\n```\n",".agents/skills/frontend-testing/SKILL.md":"# Frontend Testing Skill - Langflow\n\n## When to Apply\n\nActivate this skill when:\n- Writing new unit or integration tests for React components, hooks, utilities, or Zustand stores\n- Reviewing existing tests for correctness, coverage, or best practices\n- Improving test coverage for under-tested modules\n- Debugging flaky or failing tests\n- Refactoring test code for maintainability\n\n## Tech Stack\n\n| Technology | Version | Purpose |\n|---|---|---|\n| Jest | 30.x | Test runner and assertion framework |\n| ts-jest | 29.x | TypeScript transform for Jest |\n| React Testing Library | 16.x | Component rendering and DOM queries |\n| @testing-library/user-event | 14.x | Realistic user interaction simulation |\n| @testing-library/jest-dom | 6.x | Extended DOM matchers |\n| jsdom | (via jest-environment-jsdom 30.x) | Browser environment simulation |\n| React | 19.x | UI framework |\n| TypeScript | 5.4 | Type safety |\n| Zustand | 4.x | State management |\n| React Router DOM | 6.x | Client-side routing |\n| @tanstack/react-query | 5.x | Server state management |\n| Axios | 1.x | HTTP client |\n\n## Project Configuration\n\n- **Jest config**: `src/frontend/jest.config.js`\n- **Setup files**: `src/frontend/jest.setup.js` (globals/mocks) and `src/frontend/src/setupTests.ts` (DOM matchers, ResizeObserver, IntersectionObserver, matchMedia)\n- **Path alias**: `@/` maps to `<rootDir>/src/`\n- **Test match patterns**: `src/**/__tests__/**/*.{test,spec}.{ts,tsx}` and `src/**/*.{test,spec}.{ts,tsx}`\n- **Transform**: Custom `transform-import-meta.js` handles `import.meta` for Jest compatibility\n- **Global mocks** (in `jest.setup.js`): `@radix-ui/react-form`, `react-markdown`, `remark-gfm`, `remark-math`, `rehype-mathjax/browser`, `lucide-react/dynamicIconImports`, `@/components/common/genericIconComponent`, `@/icons/BotMessageSquare`, `@/stores/darkStore`, `localStorage`, `sessionStorage`, `crypto`\n\n## Key Commands\n\n```bash\n# Run all tests\nnpm test\n\n# Run a specific test file\nnpm test -- path/to/file.test.tsx\n\n# Run tests matching a pattern\nnpm test -- --testPathPattern=\"alertStore\"\n\n# Run tests in watch mode\nnpm run test:watch\n\n# Run tests with coverage\nnpm run test:coverage\n\n# Run a single test file with coverage\nnpm test -- --coverage --collectCoverageFrom='src/path/to/source.ts' path/to/__tests__/source.test.ts\n```\n\n## File Naming and Location\n\nTest files follow one of two patterns:\n\n1. **Dedicated `__tests__` directory** (preferred for components and modules):\n   ```\n   src/components/core/my-component/\n   ├── my-component.tsx\n   └── __tests__/\n       └── my-component.test.tsx\n   ```\n\n2. **Co-located test file** (acceptable for utilities and simple modules):\n   ```\n   src/utils/\n   ├── myUtil.ts\n   └── myUtil.test.ts\n   ```\n\nNaming convention: `ComponentName.test.tsx` for components, `hook-name.test.ts` for hooks, `util-name.test.ts` for utilities.\n\n**Do NOT use `.spec.tsx`** -- while technically matched, the project convention is `.test.tsx`.\n\n## Test Structure Template\n\n```tsx\nimport { render, screen } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport MyComponent from \"../MyComponent\";\n\n// Mock dependencies (use jest.mock, NOT vi.mock)\njest.mock(\"@/controllers/API/api\", () => ({\n  get: jest.fn(),\n  post: jest.fn(),\n}));\n\ndescribe(\"MyComponent\", () => {\n  beforeEach(() => {\n    jest.clearAllMocks();\n  });\n\n  describe(\"rendering\", () => {\n    it(\"should render the component with default props\", () => {\n      // Arrange\n      render(<MyComponent />);\n\n      // Act - (none for render test)\n\n      // Assert\n      expect(screen.getByRole(\"button\", { name: /submit/i })).toBeInTheDocument();\n    });\n  });\n\n  describe(\"user interactions\", () => {\n    it(\"should call onSubmit when the form is submitted\", async () => {\n      // Arrange\n      const user = userEvent.setup();\n      const onSubmit = jest.fn();\n      render(<MyComponent onSubmit={onSubmit} />);\n\n      // Act\n      await user.click(screen.getByRole(\"button\", { name: /submit/i }));\n\n      // Assert\n      expect(onSubmit).toHaveBeenCalledTimes(1);\n    });\n  });\n});\n```\n\n## Incremental Testing Workflow\n\nWhen testing a directory with multiple files, follow this order:\n\n1. **Identify all source files** in the target directory\n2. **Order by complexity** (simplest first):\n   - Pure utility functions (no React, no side effects)\n   - Constants and configuration objects\n   - Custom hooks (no UI rendering)\n   - Simple presentational components (no state, no side effects)\n   - Stateful components with local state\n   - Components using Zustand stores\n   - Components with API calls or complex async behavior\n   - Integration-level components that compose many children\n3. **For each file**:\n   a. Read the source file completely\n   b. Identify all exported functions, components, and types\n   c. Write tests covering all branches and edge cases\n   d. Run the tests and fix any failures\n   e. Check coverage and add tests for uncovered lines\n   f. Move to the next file\n4. **Run full directory coverage** at the end to verify\n\n## Complexity-Based Test Ordering\n\nWithin a single test file, order test cases from simplest to most complex:\n\n1. Default rendering / initial state\n2. Props variations and conditional rendering\n3. User interactions (clicks, typing, form submission)\n4. Async operations (API calls, timers)\n5. Error states and edge cases\n6. Integration with stores or context\n7. Cleanup and unmount behavior\n\n## Core Principles\n\n### Arrange-Act-Assert (AAA)\nEvery test should have a clear three-phase structure. Use blank lines to separate each phase for readability.\n\n### Black-Box Testing\nTest the component from the user's perspective. Query by role, label, text, or `data-testid` -- never by CSS class, internal state variable, or implementation detail.\n\n### Single Behavior Per Test\nEach `it()` block should verify exactly one behavior. If you need to write \"and\" in the test name, split it into two tests.\n\n### Semantic Test Names\nUse descriptive names that explain the expected behavior:\n- Good: `\"should disable the submit button when the form is invalid\"`\n- Bad: `\"button test\"` or `\"test 1\"`\n\nFormat: `\"should [expected behavior] when [condition]\"`\n\n## Required Test Scenarios\n\nFor every component, cover at minimum:\n\n### Rendering\n- Default render with no optional props\n- Render with all optional props provided\n- Conditional rendering branches (if/else in JSX)\n\n### Props and State\n- Each prop variation that changes rendered output\n- Default prop values\n- State transitions triggered by user actions\n\n### User Interactions\n- Click handlers\n- Form input and submission\n- Keyboard navigation (if applicable)\n- Hover/focus states (if applicable)\n\n### Challenge Tests (MANDATORY — not optional)\n\n**Happy path tests alone are NOT enough.** They only confirm the code works when everything is perfect. Real bugs hide in the cracks. You MUST write tests that actively TRY TO BREAK the code:\n\n**Unexpected inputs:**\n- `null`, `undefined`, `\"\"`, `[]`, `{}`, `0`, `-1`, `NaN`, `Infinity`\n- What happens when a required prop is missing?\n- What happens when data from the API comes back with missing fields?\n\n**Boundary values:**\n- Max length strings (paste 10,000 chars in an input)\n- Exactly at the limit, one past the limit\n- Zero items, one item, maximum items\n- First page, last page, out-of-range page\n\n**Malformed data:**\n- API returns `{ data: null }` instead of `{ data: [] }`\n- JSON with extra unexpected fields\n- Dates in wrong format, numbers as strings\n\n**Error states:**\n- Network failure (API rejects with 500)\n- Authentication expired mid-action (401)\n- Resource not found (404)\n- Permission denied (403)\n- Timeout\n\n**What should NOT happen:**\n- Verify that deleting a flow does NOT delete flows from other users\n- Verify that a read-only user CANNOT trigger write mutations\n- Verify that XSS payloads in user input are sanitized\n\n**Rapid/concurrent actions:**\n- Double-click on submit button\n- Rapid repeated API calls\n- Unmount component while async operation is in flight\n\n**Write tests based on REQUIREMENTS, not on what the source code does.** This is how you catch bugs where the code diverges from expected behavior.\n\n**When a test fails:** first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.\n\n### Async Behavior\n- Loading states\n- Success states\n- Error states\n- Timeout/retry behavior\n\n## Coverage Goals\n\nPer source file:\n- **Function coverage**: 100%\n- **Branch coverage**: > 95%\n- **Line coverage**: > 95%\n- **Statement coverage**: > 95%\n\nRun coverage for a specific file:\n```bash\nnpm test -- --coverage --collectCoverageFrom='src/path/to/file.ts' src/path/to/__tests__/file.test.ts\n```\n\n## Important Rules\n\n1. **Never use Vitest APIs**: Use `jest.fn()`, `jest.mock()`, `jest.spyOn()`, `jest.mocked()` -- never `vi.*` equivalents.\n2. **Never mock base UI components** from `@/components/ui/` -- render them as-is.\n3. **Check `jest.setup.js` before mocking**: Many modules are already globally mocked (darkStore, genericIconComponent, react-markdown, radix-form, etc.). Do not re-mock them.\n4. **Use `@testing-library/user-event`** over `fireEvent` for user interactions.\n5. **Wrap state updates in `act()`** when testing Zustand stores or React state changes.\n6. **Clean up after each test**: Use `beforeEach(() => jest.clearAllMocks())` and `afterEach` for timers.\n7. **Always write both happy path AND adversarial tests** (null, undefined, empty values, boundary conditions, error states).\n8. **Minimum coverage: 75%** (target 80%). Below 75% the task is not complete.\n\n## Forbidden Test Anti-Patterns\n\n| Pattern | Problem | How to Detect |\n|---------|---------|---------------|\n| **The Liar** | Test passes but doesn't verify the behavior it claims to test | Assertions don't match the test name |\n| **The Mirror** | Test reads source code and asserts exactly what the code does — finds zero bugs | Test would never fail even if logic changes |\n| **The Giant** | 50+ lines of setup, multiple acts, dozens of assertions | Should be 5+ separate tests |\n| **The Mockery** | So many mocks that the test only tests the mock setup | Count mocks — if > 3 deep, rethink |\n| **The Inspector** | Coupled to implementation details, breaks on any refactor | Tests internal state instead of behavior |\n| **The Chain Gang** | Tests depend on execution order or share mutable state | Tests fail when run in isolation |\n| **The Flaky** | Sometimes passes, sometimes fails with no code changes | Non-deterministic assertions or timing issues |\n\n## References\n\n- [Incremental Testing Workflow](references/workflow.md) - Step-by-step process for testing directories\n- [Mocking Guide](references/mocking.md) - Patterns for mocking APIs, stores, routers, and context\n- [Async Testing](references/async-testing.md) - Patterns for async operations, timers, and waitFor\n- [Common Patterns](references/common-patterns.md) - Query priority, events, forms, modals, data-driven tests\n- [Test Checklist](references/checklist.md) - Pre-submission verification checklist\n- [Domain Components](references/domain-components.md) - Langflow-specific component testing patterns\n- [Component Template](assets/component-test.template.tsx) - Starter template for component tests\n- [Hook Template](assets/hook-test.template.ts) - Starter template for hook tests\n- [Utility Template](assets/utility-test.template.ts) - Starter template for utility tests\n",".agents/skills/ibm-a11y-level1-audit/SKILL.md":"---\nname: ibm-a11y-level1-audit\ndescription: Perform a scoped IBM Equal Access Level 1 compliance audit of a chosen Langflow frontend surface (routes, components, or a PR) and produce a findings report mapped to WCAG/IBM Level 1 criteria. Default behavior is audit and report only — fixes are applied only when the user explicitly asks for remediation in the same request. Use when the user asks for an IBM Level 1 audit, a Level 1 compliance report, or to find/report Level 1 WCAG issues on a specific surface. For scanning a batch of routes without a report, see ibm-a11y-route-scan. For scanning and fixing an entire PR/branch end-to-end by default, see ibm-a11y-pr-remediation.\n---\n\n# IBM Accessibility Level 1 Audit\n\nAudit → report → (fix only if asked) → verify. Scope is **IBM Equal Access Toolkit v7.3 Level 1 only**. Do not expand into Level 2/3 unless the user asks.\n\nThis skill's default deliverable is a **report**, not a diff. If the user wants a full scan-and-fix pass across an entire PR/branch, use `ibm-a11y-pr-remediation` instead — it is fix-by-default. This skill fixes only when the user explicitly asks for remediation of the audited surface in the same request.\n\n## Related skills\n\n| Skill | Use for |\n|-------|---------|\n| [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) | How to write/run axe + IBM scans, baselines, component gotchas |\n| [ibm-a11y-route-scan](../ibm-a11y-route-scan/SKILL.md) | Python route scanner + Markdown/HTML report generation |\n| [ibm-a11y-pr-remediation](../ibm-a11y-pr-remediation/SKILL.md) | Fix-by-default scan of an entire PR/branch |\n| [frontend-i18n](../frontend-i18n/SKILL.md) | Any new/changed accessible names or UI strings |\n\n## Sources of truth\n\n1. **Level 1 criteria (engineering guide):** [references/ibm-level1-criteria.md](references/ibm-level1-criteria.md) — full checklist, deferred L2/L3 list, common failures, implementation patterns.\n2. **Langflow captured IBM Level 1 filter:** `src/frontend/tests/a11y/ibm-able-level-1-requirements.md` — 21 requirements from the IBM Able UI Level 1 filter. Prefer the engineering guide when the two disagree on pace; still map findings to both IDs when useful.\n3. **Verification hosts:** `src/frontend/tests/a11y/` — Playwright specs, baselines, README. Confirm this path exists before claiming coverage.\n\n## Workflow\n\nCopy and track:\n\n```\nLevel 1 Audit Progress:\n- [ ] 1. Scope the surface\n- [ ] 2. Scan (IBM + axe as applicable)\n- [ ] 3. Map findings to Level 1 criteria\n- [ ] 4. Write the report\n- [ ] 5. Fix violations (only if the user asked for remediation)\n- [ ] 6. Verify with tests/a11y (only if step 5 ran)\n- [ ] 7. Re-scan and update report status (only if step 5 ran)\n```\n\n### 1. Scope the surface\n\nIdentify what to audit from the user request:\n\n- Specific route(s), page(s), or component(s)\n- Pending git changes under `src/frontend`\n- Default: static routes + any stateful specs that touch the changed surface\n\nRead `src/frontend/tests/a11y/README.md` and list existing specs that already cover the surface.\n\n### 2. Scan\n\nRun **both** engines when the surface is interactive UI (see `ibm-a11y-testing-guide`):\n\n```bash\n# Playwright IBM scans (live DOM / stateful)\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# Optional: HTML triage report\nnpm run a11y:html-report --silent\n# → coverage/accessibility-reports/index.html\n\n# Ad-hoc route batch (default-loaded page only) — see ibm-a11y-route-scan for details\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y.json \\\n  --markdown /tmp/langflow-a11y.md \\\n  --html /tmp/langflow-a11y.html \\\n  --timeout-ms 45000\n```\n\nFor component-only changes, also run Jest axe where a `__tests__/*.a11y.test.tsx` exists.\n\nDo not invent findings. Prefer scanner output + manual Level 1 checks scanners miss (keyboard trap both directions, focus restore, reflow at 320px, color-not-only cues).\n\n### 3. Map findings to Level 1\n\nFor each issue, assign:\n\n- **WCAG / IBM ID** from [references/ibm-level1-criteria.md](references/ibm-level1-criteria.md) (e.g. `2.1.1`, `4.1.2`)\n- **IBM ruleId** when from Equal Access (`aria_accessiblename_exists`, `element_tabbable_role_valid`, …)\n- **Severity:** `violation` | `potentialviolation` | `manual`\n- **In scope?** Drop or defer anything listed under **Deferred to Level 2 & Level 3** in the criteria doc unless the user expands scope.\n\n### 4. Write the report\n\nUse [references/report-template.md](references/report-template.md). Deliver the report in the chat (and write a file only if the user asks for a path).\n\nRequired sections: Summary, Scope, Findings table, Fixes applied / proposed, Verification, Remaining risk / baselines.\n\n**Stop here by default.** List proposed fixes in the report without editing files, unless the user's request explicitly also asked for remediation (e.g. \"audit and fix\", \"fix what you find\"). If they only asked for an audit or report, do not proceed to step 5.\n\n### 5. Fix violations (only when explicitly requested)\n\nOnly continue past the report if the user's request explicitly asked for fixes alongside the audit. Otherwise stop after step 4.\n\n- Prefer semantic HTML over ARIA.\n- Follow Langflow patterns in `ibm-a11y-testing-guide` (AG Grid, Radix `asChild`, focus restore, icon-only `aria-label`).\n- Route new UI strings / `aria-label`s through `frontend-i18n`.\n- Do **not** silently disable scans. Use IBM baselines under `src/frontend/tests/a11y/baselines/` only for documented framework debt (see `ibm-a11y-testing-guide`).\n- Keep fixes minimal; do not refactor unrelated UI.\n\n### 6. Verify with `tests/a11y`\n\nConfirm path: `src/frontend/tests/a11y/`.\n\n| Surface | Spec to run / update |\n|---------|----------------------|\n| Static routes | `static-routes.a11y.spec.ts` (+ `scripts/a11y/a11y_routes.json` if new route) |\n| Auth | `auth-pages.a11y.spec.ts` |\n| Core pages | `core-pages.a11y.spec.ts` |\n| Data-rich (files, API keys, globals) | `files.a11y.spec.ts`, `api-keys.a11y.spec.ts`, `global-variables.a11y.spec.ts` |\n| Other data-rich | `data-rich-routes.a11y.spec.ts` |\n| Baselines | `baselines/*.json` |\n\nCommands:\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<relevant>.a11y.spec.ts --project=chromium --workers=5\n```\n\nIf coverage is missing for a fixed state, add a scan (and keyboard test when custom keyboard behavior was fixed) following `files.a11y.spec.ts` / `api-keys.a11y.spec.ts` patterns.\n\n### 7. Close the loop\n\nRe-run the same scans. Update the report: each finding → `fixed` | `baselined` | `open`. State commands run and whether IBM assert mode passed.\n\n## Manual Level 1 spot checks\n\nScanners miss some Level 1 tasks — spot-check when relevant:\n\n- **2.1.1 / 2.1.2:** Tab and Shift+Tab through the surface; Escape closes overlays; no trap.\n- **2.4.3 / 2.4.7:** Focus order matches visual order; focus ring visible.\n- **1.4.10:** 320px width / ~400% zoom — no essential horizontal scroll.\n- **1.4.1:** Status/errors not color-only.\n- **3.3.1 / 3.3.2:** Errors named in text and tied to fields; inputs labeled.\n\n## Best practices (data grids + modals)\n\nWhen auditing **settings tables** (especially `/settings/global-variables`) and similar AG Grid + modal flows, treat these as Level 1 best practices (2.1.1 / 2.4.3) — not edge-case gotchas.\n\n### Selectable-row keyboard map\n\nReference implementation: `GlobalVariablesPage` + `tests/a11y/global-variables.a11y.spec.ts`.\n\n| Key | Behavior |\n|-----|----------|\n| **Space** | Toggle the row selection checkbox (do **not** open edit) |\n| **Enter** | Open the Update Variable modal for the focused row |\n\nImplementation notes:\n\n- Handle in page-level `onCellKeyDown` only (do not change shared `TableComponent` defaults for other grids unless the product asks for the same map).\n- Add `suppressKeyboardEvent` on that page’s column defs for Enter/Space so AG Grid’s built-in Space selection does not fight the custom handler.\n- Sync React selection state after `node.setSelected` so toolbar delete enablement updates (`TableOptions.hasSelection` is read at render time).\n\n### Modal open/close — retain last position (focus restore)\n\n- Opening edit from a row/cell must remember the focused cell (`rowIndex` + `colId`).\n- Closing the edit modal (Escape, Cancel, or successful save) must restore focus to that same cell via `api.setFocusedCell` + DOM `.focus()`, using a few `requestAnimationFrame`s to outlast Radix dialog focus cleanup.\n- Create modal opened from **Add New** should restore to that trigger (Radix default when a real `DialogTrigger` exists).\n- Verify with a Playwright keyboard test: open from a cell → Escape → `document.activeElement` is still that cell (or its `col-id`), then Enter can open again without a manual mouse re-focus.\n\nFor AG Grid pagination/tab traps, Radix `asChild`, and popover Esc restore details, see [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) (Gotchas vs Best practices sections).\n\n## Out of scope (unless asked)\n\n- IBM Level 2/3 criteria listed as deferred in the criteria reference\n- Section 508 software-only rows (web UI covered via 4.1.2)\n- Media captions (1.2.x) when the surface has no audio/video\n- Fixing violations without an explicit remediation request (report only by default)\n",".agents/skills/ibm-a11y-route-scan/SKILL.md":"---\nname: ibm-a11y-route-scan\ndescription: Batch-scan Langflow frontend routes for accessibility issues using the Python IBM Equal Access scanner (scripts/a11y/a11y_scan.py) and produce JSON/Markdown/HTML reports. Scans the default-loaded state of each route only (use explicit state files for modals). Use when asked to scan one or more routes, produce an accessibility report for pages, or batch-check static routes. Reports findings only — does not fix code, run Playwright/axe tests, or perform a formal Level 1 audit; see ibm-a11y-testing-guide, ibm-a11y-level1-audit, and ibm-a11y-pr-remediation for those.\n---\n\n# IBM Route Scanner\n\nUse this skill when asked to scan Langflow frontend pages for accessibility issues with the local Python scanner. This skill only reports — it does not modify code. For test-writing guidance, formal audits, or PR-wide fixes, see `ibm-a11y-testing-guide`, `ibm-a11y-level1-audit`, and `ibm-a11y-pr-remediation`.\n\n## Scanner\n\nUse the Python script:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-report.json \\\n  --markdown /tmp/langflow-a11y-report.md \\\n  --html /tmp/langflow-a11y-report.html \\\n  --timeout-ms 45000\n```\n\nScript options:\n\n- `--url`: base app URL, usually `http://localhost:3000`.\n- `--routes-file`: route manifest JSON file. Prefer `scripts/a11y/a11y_routes.json`.\n- `--route-group`: manifest group to scan. Default: `static`.\n- `--routes`: comma-separated route paths to scan.\n- `--route`: one route path; can be repeated instead of `--routes`.\n- `--levels`: comma-separated issue levels. Default: `violation`.\n- `--out`: JSON report path.\n- `--markdown`: optional Markdown report path.\n- `--html`: optional self-contained HTML report path.\n- `--timeout-ms`: per-route timeout.\n- `--quiet-ms`: network quiet window before scanning. Default: `1000`.\n- `--states-file`: JSON file with explicit modal/state actions.\n- `--headed`: show browser while scanning.\n\n## Route Selection\n\nUse `scripts/a11y/a11y_routes.json` as the source of truth for route selection.\n\nThe normal CI/local batch is the manifest `static` group. Prefer that unless the user asks for custom, dynamic, or gated routes.\n\nCommon manifest-backed command:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-static.json \\\n  --markdown /tmp/langflow-a11y-static.md \\\n  --html /tmp/langflow-a11y-static.html\n```\n\nDynamic routes need real IDs before scanning:\n\n- `/flow/:id/`\n- `/flow/:id/view`\n- `/playground/:id/`\n- `/assets/knowledge-bases/:sourceId/chunks`\n\nFor dynamic routes, get IDs from the loaded app, API responses, or existing test data before replacing placeholders.\n\n## Examples\n\nScan one route:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --route /flows \\\n  --out /tmp/langflow-a11y-flows.json\n```\n\nScan multiple routes:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-report.json \\\n  --markdown /tmp/langflow-a11y-report.md \\\n  --html /tmp/langflow-a11y-report.html\n```\n\nScan more than violations:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --levels violation,potentialviolation,recommendation \\\n  --out /tmp/langflow-a11y-expanded.json\n```\n\nScan route plus modal states:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --states-file /tmp/langflow-a11y-states.json \\\n  --out /tmp/langflow-a11y-modal-report.json \\\n  --markdown /tmp/langflow-a11y-modal-report.md \\\n  --html /tmp/langflow-a11y-modal-report.html \\\n  --timeout-ms 45000\n```\n\nState file shape:\n\n```json\n[\n  {\n    \"route\": \"/settings/global-variables\",\n    \"states\": [\n      {\n        \"name\": \"new-global-variable-modal\",\n        \"open\": [\n          { \"click\": \"[data-testid='api-key-button-store']\" },\n          { \"waitFor\": \"[role='dialog']\" }\n        ],\n        \"close\": [\n          { \"press\": \"Escape\" },\n          { \"waitForHidden\": \"[role='dialog']\" }\n        ]\n      }\n    ]\n  }\n]\n```\n\nSupported state actions:\n\n- `{ \"click\": \"<css selector>\" }`\n- `{ \"clickText\": \"<visible text>\" }`\n- `{ \"clickRole\": { \"role\": \"button\", \"name\": \"Create\" } }`\n- `{ \"fill\": { \"selector\": \"<css selector>\", \"value\": \"text\" } }`\n- `{ \"press\": \"Escape\" }`\n- `{ \"press\": { \"selector\": \"<css selector>\", \"key\": \"Enter\" } }`\n- `{ \"waitFor\": \"<css selector>\" }`\n- `{ \"waitForHidden\": \"<css selector>\" }`\n- `{ \"waitForText\": \"<visible text>\" }`\n- `{ \"wait\": 500 }`\n- `{ \"scroll\": { \"selector\": \"<css selector>\", \"to\": \"bottom\" } }` — `to` is `top` or\n  `bottom`; use `\"by\": <pixels>` for a relative scroll, and omit `selector` to scroll the\n  page. Needed for virtualised grids whose tabbable rows only disappear after scrolling.\n\n## Report\n\nThe scanner always writes JSON. It can also write Markdown and HTML for presentation.\n\nUse JSON for exact data. Use Markdown for PR comments or issues. Use HTML when the user wants a browsable report.\n\nSummarize:\n\n- report path\n- Markdown/HTML report paths, when generated\n- total issue count\n- per-route issue count\n- per-route API request count\n- per-route request failure count\n- top rule IDs\n\nUse report fields directly:\n\n- `totalIssues`\n- `results[].route`\n- `results[].state`\n- `results[].phase`\n- `results[].apiRequests`\n- `results[].requestFailures`\n- `results[].diagnostics`\n- `results[].issues[].ruleId`\n\n## Rules\n\n- Use only scanner output for findings.\n- Do not invent route names. Read `routes.tsx`.\n- Do not auto-click arbitrary buttons to find modals. Use explicit state actions.\n- Avoid destructive modal actions unless the user explicitly asks and data is safe.\n- If a route has zero API requests, mention that scan quality may be limited.\n- This skill reports only; do not edit files. If the user also wants fixes applied, hand off to `ibm-a11y-pr-remediation` or `ibm-a11y-level1-audit`.\n",".agents/skills/component-refactoring/SKILL.md":"---\nname: component-refactoring\ndescription: Refactor high-complexity React components in Langflow frontend. Use when manual complexity assessment shows complexity > 50 or lineCount > 300, when the user asks for code splitting, hook extraction, or complexity reduction; avoid for simple/well-structured components, third-party wrappers, or when the user explicitly wants testing without refactoring.\n---\n\n# Langflow Component Refactoring Skill\n\nRefactor high-complexity React components in the Langflow frontend codebase with the patterns and workflow below.\n\n> **Complexity Threshold**: Components with complexity > 50 (measured manually by counting conditionals, nesting levels, and lines) should be refactored before testing.\n\n## Quick Reference\n\n### Commands (run from `src/frontend/`)\n\n```bash\ncd src/frontend\n\n# Lint with Biome\nnpm run lint\n\n# Type checking\nnpm run type-check\n\n# Run tests\nnpm test\n```\n\n### Manual Complexity Assessment\n\nSince Langflow does not have automated complexity analysis tools, assess components manually:\n\n1. **Count conditionals**: Each `if/else`, `switch/case`, ternary, `&&`/`||` chain adds +1.\n2. **Count nesting levels**: Each level of nesting within conditionals or loops adds +1.\n3. **Count total lines**: Target < 300 lines per component file.\n4. **Count state hooks**: More than 5 `useState` calls suggests hook extraction.\n5. **Count effects**: More than 3 `useEffect` calls suggests effect consolidation.\n\n### Complexity Score Interpretation\n\n| Score | Level | Action |\n|-------|-------|--------|\n| 0-25 | Simple | Ready for testing |\n| 26-50 | Medium | Consider minor refactoring |\n| 51-75 | Complex | **Refactor before testing** |\n| 76-100 | Very Complex | **Must refactor** |\n\n## Core Refactoring Patterns\n\n### Pattern 1: Extract Custom Hooks\n\n**When**: Component has complex state management, multiple `useState`/`useEffect`, or business logic mixed with UI.\n\n**Langflow Convention**: Place hooks in a `hooks/` subdirectory or alongside the component as `use-<feature>.ts`. Langflow uses kebab-case filenames with `use-` prefix.\n\n```typescript\n// Before: Complex state logic in component\nconst FlowPage: FC = () => {\n  const [nodes, setNodes] = useState<Node[]>([])\n  const [edges, setEdges] = useState<Edge[]>([])\n  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)\n\n  // 50+ lines of state management logic...\n\n  return <div>...</div>\n}\n\n// After: Extract to custom hook\n// hooks/use-flow-state.ts\nexport const useFlowState = (flowId: string) => {\n  const [nodes, setNodes] = useState<Node[]>([])\n  const [edges, setEdges] = useState<Edge[]>([])\n  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)\n\n  // Related state management logic here\n\n  return { nodes, setNodes, edges, setEdges, buildStatus, setBuildStatus }\n}\n\n// Component becomes cleaner\nconst FlowPage: FC = () => {\n  const { nodes, setNodes, edges, setEdges } = useFlowState(flowId)\n  return <div>...</div>\n}\n```\n\n**Langflow Examples**:\n- `src/frontend/src/hooks/use-add-component.ts`\n- `src/frontend/src/hooks/use-unsaved-changes.ts`\n- `src/frontend/src/hooks/use-refresh-model-inputs.ts`\n\n### Pattern 2: Extract Sub-Components\n\n**When**: Single component has multiple UI sections, conditional rendering blocks, or repeated patterns.\n\n**Langflow Convention**: Place sub-components in subdirectories or as separate files in the same directory. UI primitives go in `components/ui/`, domain components in `components/core/`, reusable components in `components/common/`.\n\n```typescript\n// Before: Monolithic JSX with multiple sections\nconst GenericNode = () => {\n  return (\n    <div>\n      {/* 100 lines of header UI */}\n      {/* 100 lines of parameter fields */}\n      {/* 100 lines of output handles */}\n    </div>\n  )\n}\n\n// After: Split into focused components\n// CustomNodes/GenericNode/\n//   generic-node.tsx       (orchestration only — kebab-case, descriptive name)\n//   components/\n//     node-header.tsx\n//     node-parameters.tsx\n//     node-outputs.tsx\n\nconst GenericNode = () => {\n  return (\n    <div>\n      <NodeHeader nodeData={data} />\n      <NodeParameters fields={fields} />\n      <NodeOutputs outputs={outputs} />\n    </div>\n  )\n}\n```\n\n**Langflow Examples**:\n- `src/frontend/src/CustomNodes/GenericNode/components/`\n- `src/frontend/src/components/core/`\n- `src/frontend/src/components/ui/`\n\n### Pattern 3: Simplify Conditional Logic\n\n**When**: Deep nesting (> 3 levels), complex ternaries, or multiple `if/else` chains.\n\n```typescript\n// Before: Deeply nested conditionals\nconst getFieldComponent = (field: InputFieldType) => {\n  if (field.type === \"str\") {\n    if (field.multiline) {\n      return <TextAreaComponent />\n    } else if (field.password) {\n      return <PasswordInput />\n    } else if (field.options?.length) {\n      return <Dropdown options={field.options} />\n    } else {\n      return <InputComponent />\n    }\n  } else if (field.type === \"int\") {\n    return <IntComponent />\n  } else if (field.type === \"float\") {\n    return <FloatComponent />\n  }\n  return null\n}\n\n// After: Use lookup tables + early returns\nconst FIELD_COMPONENT_MAP: Record<string, FC<FieldProps>> = {\n  int: IntComponent,\n  float: FloatComponent,\n  bool: ToggleComponent,\n  code: CodeAreaComponent,\n}\n\nconst STR_VARIANT_MAP: Record<string, FC<FieldProps>> = {\n  multiline: TextAreaComponent,\n  password: PasswordInput,\n  options: Dropdown,\n}\n\nconst getFieldComponent = (field: InputFieldType) => {\n  if (field.type !== \"str\") {\n    const Component = FIELD_COMPONENT_MAP[field.type]\n    return Component ? <Component {...field} /> : null\n  }\n\n  const variant = field.multiline ? \"multiline\"\n    : field.password ? \"password\"\n    : field.options?.length ? \"options\"\n    : \"default\"\n\n  const Component = STR_VARIANT_MAP[variant] ?? InputComponent\n  return <Component {...field} />\n}\n```\n\n### Pattern 4: Extract API/Data Logic\n\n**When**: Component directly handles API calls, data transformation, or complex async operations.\n\n**Langflow Convention**:\n- This skill is for component decomposition, not query/mutation design.\n- When refactoring data fetching, use `frontend-query-mutation` for query patterns, `UseRequestProcessor`, cache invalidation, and mutation error handling.\n- Do not create thin passthrough `useQuery` wrappers during refactoring; only extract a custom hook when it truly orchestrates multiple queries/mutations or shared derived state.\n- API hooks live in `controllers/API/queries/{domain}/`.\n\n**Langflow Examples**:\n- `src/frontend/src/controllers/API/queries/flows/use-post-add-flow.ts`\n- `src/frontend/src/controllers/API/queries/variables/use-get-global-variables.ts`\n- `src/frontend/src/controllers/API/queries/folders/use-get-folders.ts`\n\n### Pattern 5: Extract Modal/Dialog Management\n\n**When**: Component manages multiple modals with complex open/close states.\n\n**Langflow Convention**: Modals should be extracted with their state management.\n\n```typescript\n// Before: Multiple modal states in component\nconst FlowToolbar = () => {\n  const [showExportModal, setShowExportModal] = useState(false)\n  const [showShareModal, setShowShareModal] = useState(false)\n  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)\n  const [showApiModal, setShowApiModal] = useState(false)\n  // 5+ more modal states...\n}\n\n// After: Extract to modal management hook\ntype ModalType = \"export\" | \"share\" | \"delete\" | \"api\" | null\n\nconst useFlowToolbarModals = () => {\n  const [activeModal, setActiveModal] = useState<ModalType>(null)\n\n  const openModal = useCallback((type: ModalType) => setActiveModal(type), [])\n  const closeModal = useCallback(() => setActiveModal(null), [])\n\n  return {\n    activeModal,\n    openModal,\n    closeModal,\n    isOpen: (type: ModalType) => activeModal === type,\n  }\n}\n```\n\n### Pattern 6: Extract Form Logic\n\n**When**: Complex form validation, submission handling, or field transformation.\n\n**Langflow Convention**: Extract form state and validation into hooks.\n\n```typescript\n// Extract form validation and submission\nconst useFlowSettingsForm = (initialValues: FlowSettings) => {\n  const [values, setValues] = useState(initialValues)\n  const [errors, setErrors] = useState<Record<string, string>>({})\n\n  const validate = useCallback(() => {\n    const newErrors: Record<string, string> = {}\n    if (!values.name?.trim()) newErrors.name = \"Name is required\"\n    if (values.endpoint_name && !/^[a-z0-9-]+$/.test(values.endpoint_name)) {\n      newErrors.endpoint_name = \"Must be lowercase alphanumeric with hyphens\"\n    }\n    setErrors(newErrors)\n    return Object.keys(newErrors).length === 0\n  }, [values])\n\n  const handleChange = useCallback((field: string, value: any) => {\n    setValues((prev) => ({ ...prev, [field]: value }))\n  }, [])\n\n  return { values, errors, validate, handleChange }\n}\n```\n\n## Langflow-Specific Refactoring Guidelines\n\n### 1. Zustand Store Selectors\n\n**When**: Component reads many values from a Zustand store, causing unnecessary re-renders.\n\n```typescript\n// Before: Selecting too many values\nconst Component = () => {\n  const flowStore = useFlowStore()\n  // Component re-renders on ANY store change\n}\n\n// After: Use individual selectors\nconst Component = () => {\n  const nodes = useFlowStore((state) => state.nodes)\n  const edges = useFlowStore((state) => state.edges)\n  // Component only re-renders when nodes or edges change\n}\n```\n\n**Langflow Reference**: All stores in `src/frontend/src/stores/` follow this selector pattern.\n\n### 2. Custom Node Components\n\n**When**: Refactoring flow node components (`CustomNodes/GenericNode/`).\n\n**Conventions**:\n- Keep node logic in custom hooks\n- Extract parameter rendering to separate components\n- Use the existing `components/` subdirectory for sub-components\n\n```\nCustomNodes/GenericNode/\n  generic-node.tsx             # Node registration and main render (kebab-case, NOT index.tsx)\n  components/\n    handle-render.tsx           # Handle rendering\n    node-description.tsx        # Node description display\n    node-input-field.tsx        # Input field rendering\n    node-name.tsx               # Node name display\n    node-output-field.tsx       # Output field rendering\n    node-status.tsx             # Build status display\n```\n\n### 3. Flow Canvas Components\n\n**When**: Refactoring components related to the flow editor canvas.\n\n**Conventions**:\n- `@xyflow/react` v12 is the canvas library\n- Keep canvas event handlers separate from UI rendering\n- Use `useFlowStore` for flow state management\n- Use `useFlowsManagerStore` for multi-flow management\n\n### 4. API Query Hook Components\n\n**When**: Refactoring components that consume API data.\n\n**Conventions**:\n- Use existing query hooks from `controllers/API/queries/`\n- Access `UseRequestProcessor` for new queries/mutations\n- Use query key arrays like `[\"useGetGlobalVariables\"]` for cache management\n- Cache invalidation belongs in mutation `onSettled` callbacks\n\n## Refactoring Workflow\n\n### Step 1: Assess Complexity\n\nManually count:\n- Total conditionals (if/else, switch, ternary, &&/||)\n- Maximum nesting depth\n- Total lines of code\n- Number of useState/useEffect hooks\n- Number of distinct UI sections\n\n### Step 2: Plan\n\nCreate a refactoring plan based on detected features:\n\n| Detected Feature | Refactoring Action |\n|------------------|-------------------|\n| 5+ `useState` hooks with related state | Extract custom hook |\n| API calls in component body | Extract to query hook |\n| 3+ event handlers with logic | Extract event handlers to hook |\n| 300+ lines | Split into sub-components |\n| Deep conditional nesting (>3) | Simplify conditional logic |\n| Multiple modal states | Extract modal management |\n\n### Step 3: Execute Incrementally\n\n1. **Extract one piece at a time**\n2. **Run lint, type-check, and tests after each extraction**\n3. **Verify functionality before next step**\n\n```\nFor each extraction:\n  1. Extract code\n  2. Run: npm run lint\n  3. Run: npm run type-check\n  4. Run: npm test\n  5. Test functionality manually\n  6. PASS? -> Next extraction\n     FAIL? -> Fix before continuing\n```\n\n### Step 4: Verify\n\nAfter refactoring, re-assess complexity manually:\n\n- Target complexity < 50\n- Target line count < 300\n- Target max nesting depth <= 3\n- Target max function length < 30 lines\n\n## Common Mistakes to Avoid\n\n### Over-Engineering\n\n```typescript\n// Too many tiny hooks\nconst useButtonText = () => useState(\"Click\")\nconst useButtonDisabled = () => useState(false)\nconst useButtonLoading = () => useState(false)\n\n// Cohesive hook with related state\nconst useButtonState = () => {\n  const [text, setText] = useState(\"Click\")\n  const [disabled, setDisabled] = useState(false)\n  const [loading, setLoading] = useState(false)\n  return { text, setText, disabled, setDisabled, loading, setLoading }\n}\n```\n\n### Breaking Existing Patterns\n\n- Follow existing directory structures in `components/ui/`, `components/core/`, `components/common/`\n- Maintain naming conventions (kebab-case files, PascalCase components)\n- Preserve export patterns for compatibility\n- Keep Zustand store selector patterns consistent\n\n### Premature Abstraction\n\n- Only extract when there is clear complexity benefit\n- Do not create abstractions for single-use code\n- Keep refactored code in the same domain area\n\n### Bypassing UseRequestProcessor\n\n- Do not call `useQuery` or `useMutation` directly for API calls\n- Always use the `UseRequestProcessor` pattern for consistency with retry and invalidation logic\n- See `frontend-query-mutation` skill for API hook patterns\n\n## References\n\n### Langflow Codebase Examples\n\n- **Hook extraction**: `src/frontend/src/hooks/`\n- **Component splitting**: `src/frontend/src/CustomNodes/GenericNode/components/`\n- **UI components**: `src/frontend/src/components/ui/`\n- **Core components**: `src/frontend/src/components/core/`\n- **Common components**: `src/frontend/src/components/common/`\n- **API query hooks**: `src/frontend/src/controllers/API/queries/`\n- **Zustand stores**: `src/frontend/src/stores/`\n\n### Related Skills\n\n- `frontend-query-mutation` - For API query and mutation patterns\n- `frontend-testing` - For testing refactored components\n",".agents/skills/ibm-a11y-testing-guide/SKILL.md":"---\nname: ibm-a11y-testing-guide\ndescription: Reference guide for writing and running Langflow frontend accessibility tests with axe (Jest) and IBM Equal Access (Playwright `page.runA11yScan`). Covers which engine/test layer to pick, the POUR checklist, axe-vs-IBM rule gaps, Radix/AG-Grid component gotchas, and IBM baselines. Use when writing or reviewing a11y tests, debugging a specific axe/IBM violation, or deciding which test layer fits a UI surface. Does not run scans, audit a whole surface, or fix a PR end-to-end — see ibm-a11y-route-scan, ibm-a11y-level1-audit, and ibm-a11y-pr-remediation for those.\n---\n\n# IBM/axe Accessibility Testing Guide\n\nReference material for **how** to test a given UI surface. This skill does not decide what to scan or drive a fix loop by itself — use it while writing tests, debugging a violation, or implementing a fix identified by `ibm-a11y-route-scan`, `ibm-a11y-level1-audit`, or `ibm-a11y-pr-remediation`.\n\n## When To Use\n\nUse this skill for frontend work under `src/frontend` when the change affects:\n\n- UI components, primitives, pages, routes, dialogs, drawers, popovers, dropdowns, tabs, accordions, tables, forms, navigation, or canvas controls.\n- ARIA attributes, labels, roles, focus management, tab order, keyboard interaction, disabled states, loading states, or error states.\n- A request to write, fix, or review an axe/IBM accessibility test.\n\nUse alongside:\n\n- `frontend-testing` for Jest/React Testing Library patterns.\n- `e2e-testing` for Playwright patterns.\n- `frontend-i18n` when adding or changing user-facing labels, accessible names, `aria-label`, tooltips, or visible strings.\n- `ibm-a11y-route-scan` to batch-scan routes with the Python scanner and produce Markdown/HTML reports.\n- `ibm-a11y-level1-audit` for a scoped IBM Level 1 compliance audit and report.\n- `ibm-a11y-pr-remediation` to scan and fix an entire PR/branch end-to-end.\n\n## Goal\n\nDo not stop at \"run axe on default render.\" Inspect the changed UI and cover every meaningful user-visible state, for both keyboard and screen-reader users, that can expose accessibility bugs.\n\n## The Bar: two engines, both must pass\n\nAutomated a11y is not one tool. Run BOTH — they catch different classes of bug:\n\n- **axe-core** — Jest `axe()` (`@/utils/a11y-test`), jsdom-only. Fast; strong on contrast, labels, roles, ARIA basics.\n- **IBM Equal Access** — the stricter engine on ARIA structure and keyboard semantics; catches real WCAG Level-1 issues axe silently passes. Two entry points, **same engine** (`aChecker`):\n  - Playwright **`page.runA11yScan(label)`** — runs `aChecker.getCompliance` in-browser on the **live DOM**, so it scans open modals / menus / selected / editing states. This is IBM, **not** axe (despite the name).\n  - `scripts/a11y/a11y_scan.py` (see `ibm-a11y-route-scan`) — route scanner for the default-loaded page only.\n\nA page that is green on axe is NOT done. When the change touches a table/tree/listbox/menu/composite widget, IBM is mandatory. Prefer `page.runA11yScan` for stateful surfaces (it sees the DOM after your interactions); use the Python scanner for a quick default-load route check.\n\nVerify commands:\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# The Python scanner's playwright deps are NOT in the default `uv sync`.\n# One-time: `uv run --with playwright playwright install chromium`\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes /settings/<route> \\\n  --out /tmp/a11y.json --markdown /tmp/a11y.md --timeout-ms 45000\n```\n\n`RUN_A11Y=true` runs the scan; add `RUN_A11Y_ASSERT=true` to actually fail on new violations (without it the scan is informational). Both engines must report zero. After changing a shared component, re-scan every page that uses it, not just the one you touched.\n\n## First Pass\n\n1. Read the changed files and nearby tests.\n2. Identify the UI surface:\n   - Primitive/component only\n   - Composed component\n   - Page/route\n   - Stateful workflow\n   - Shared helper that changes labels, roles, tab order, or focus behavior\n3. List interactive surfaces:\n   - Buttons, links, inputs, checkboxes, switches, radios, selects, comboboxes\n   - Dropdown menus, popovers, dialogs, drawers, tooltips\n   - Tabs, accordions, tables/treegrids, row actions, pagination\n   - Keyboard-only interactions and focus traps\n4. List states worth scanning:\n   - Default, populated, empty, loading, error/validation visible\n   - Disabled/read-only, selected/expanded/open\n   - Modal/dropdown/popover open\n   - Mobile viewport when layout changes\n\n## What To Check (POUR checklist)\n\n### Perceivable\n- Non-text content has a text alternative; decorative images are hidden from AT.\n- Text contrast ≥ 4.5:1; non-text (icons, borders, focus rings) ≥ 3:1.\n- Layout reflows at 320px / 400% zoom without horizontal scroll or loss of content.\n\n### Operable (keyboard is the big one)\n- Every control is reachable AND operable by keyboard (Enter/Space activate; arrows where expected).\n- Focus is always visible.\n- Focus order is logical and matches visual order.\n- **No keyboard trap — test Shift+Tab too, not just Tab.** Focus can always move out of a widget both ways.\n- Disabled controls are not in the tab order.\n- Composite widgets (grid/tree/listbox/menu/tablist) are ONE logical tab stop with a roving `tabindex`; arrows move within.\n- After async transitions (route change, panel open/close) focus lands somewhere useful, never silently on `<body>`.\n\n### Understandable\n- Inputs have programmatic labels; visible label matches accessible name.\n- Errors are identified in text and associated with their field; give a suggestion when possible.\n- The same control is identified consistently across the app.\n\n### Robust (name / role / value)\n- Interactive elements expose a correct role, name, and state.\n- Valid ARIA parent/child (e.g. `rowgroup` owns `row`, `list` owns `listitem`).\n- No tabbable element with a non-widget role (`presentation`/`none`).\n- `aria-hidden=\"true\"` never wraps a focusable element.\n\n## axe vs IBM: known gaps\n\nThings axe passes but IBM flags (all real WCAG Level 1):\n\n| IBM rule | WCAG | Typical cause |\n| --- | --- | --- |\n| `element_tabbable_role_valid` | 4.1.2 | tabbable element with `role=\"presentation\"` / no widget role (focus sentinels, wrappers) |\n| `aria_child_valid` | 1.3.1 | `rowgroup`/`list`/`tablist` owning no valid child role |\n| `aria_child_tabbable` | 2.1.1 / 4.1.2 | composite widget with no tabbable descendant (needs roving `tabindex`) |\n| `aria_hidden_focus_misuse` | 4.1.2 | focusable element inside `aria-hidden=\"true\"` |\n| `aria_accessiblename_exists` | 4.1.2 | element with a widget role but no accessible name — e.g. an AG Grid `columnheader` with neither `field` nor `headerName` (icon-only action column) |\n| `aria_content_in_landmark` | 1.3.1 | content outside any landmark — e.g. a Radix menu/popover portaled to `<body>`. `role=\"menu\"` is NOT landmark-exempt; only `aria-modal` dialogs are. Often unfixable app-wide → baseline it (see below) |\n\nRule of thumb: touch a table/tree/listbox/menu → scan with IBM.\n\n## Component Gotchas\n\n- **Data grids (AG Grid — the shared `TableComponent`)**: the grid's own tab guards + pagination are the fragile part.\n  - Disabled paging buttons must be `tabindex=\"-1\"`, but NEVER `inert`/`disabled` — that breaks AG Grid's tab guards and traps reverse (Shift+Tab) entry (WCAG 2.1.2).\n  - AG Grid still *programmatically* focuses disabled paging on tab-out; handle with its `tabToNextCell` hook plus a container-scoped `focusin` redirect (defer the redirect to `requestAnimationFrame` — focus changes inside a `focusin` handler are ignored, and AG Grid restores the last-focused cell on the next tick).\n  - Empty rowgroups need `role=\"presentation\"`; the grid needs one roving-tabindex row.\n  - **Icon-only / action column header**: AG Grid derives the `columnheader` accessible name from `field`; a column with neither `field` nor `headerName` is nameless (`aria_accessiblename_exists`, 4.1.2). Give it a `headerName` and hide it visually with a `headerClass` (sr-only clip on `.ag-header-cell-text`) so the header stays blank but named.\n  - **Interactive control inside a cell** (menu trigger, button): AG Grid navigates **cell-by-cell** and never tab-focuses the inner control, so it's keyboard-inoperable. Activate it from the grid's `onCellKeyDown` on Enter/Space (give the column a `colId` to target it). A **Radix trigger opens on keydown/pointerdown, not a synthetic `.click()`** — focus the trigger and **re-dispatch the key** (`new KeyboardEvent(\"keydown\", {key, bubbles:true})`), guarding against re-entry (skip when `target` is already the button). `TableComponent` spreads `{...props}` to AG Grid, so pass `onCellKeyDown` from the page — no shared-component change.\n  - **Focus visible on borderless grids** (`.ag-no-border`): the theme suppresses the cell focus outline (`outline:none`), hiding keyboard focus (2.4.7). Restore it with a `:focus-visible` ring scoped to `.ag-no-border .ag-cell:focus-visible` (+ header/row). `:focus-visible` **distinguishes modality on AG cells** — mouse click resolves to `:focus`, keyboard nav to `:focus-visible` — so the ring shows for keyboard only and mouse stays quiet. CAVEAT when verifying: probe with a **clean, first-interaction** page; a session that already used the keyboard makes `:focus-visible` report `true` for a later mouse click (heuristic contamination).\n  - Set `ensureDomOrder: true` so DOM order matches visual order for tab navigation.\n  - `TableComponent` is shared across settings/traces — fix once in the grid patch, then re-scan every grid page. Its pagination/tab-out rework lives in LE-1720 / PR #13953; don't duplicate it per-page.\n- **Modals/drawers**: title + description, focus trapped inside while open, focus restored to the trigger on close, Escape closes.\n- **Dropdowns/popovers**: reachable by keyboard, Escape closes, focus returns to the trigger.\n- **Radix (shadcn) primitives** — three recurring traps:\n  - **`Trigger asChild` double tab stop**: a `DialogTrigger`/`DropdownMenuTrigger` wrapping a real `<button>` **without `asChild`** makes Radix render its *own* `<button>` around it → nested buttons → two consecutive tab stops (2.4.3). Always pass `asChild` when the child is already an interactive element. (Watch the trigger wrapper's own `asChild={cond}` default — `DeleteConfirmationModal` needs an explicit `asChild`.)\n  - **Focus-restore race**: on close, Radix restores focus to its trigger **once, asynchronously**. If the menu action programmatically moves focus elsewhere (e.g. `startEditingCell` opens an inline editor), Radix steals it back. Re-assert focus on your target across a few `requestAnimationFrame`s to outlast the one-time restore. For rename-style flows, `.select()` the input too.\n  - **Portaled menu landmark**: `DropdownMenu`/`Popover` content portals to `<body>`, tripping `aria_content_in_landmark`. Portaling into `<main>` is not an option when `<main>` is `overflow-hidden` (clips the menu). Treat as app-wide debt → **baseline it** (below); the menu's real a11y (role, named trigger, keyboard open/close, focus restore) is covered by a keyboard test instead.\n- **Disabled controls**: keep them out of the tab order. Only use `aria-disabled` (instead of native `disabled`) when the control must stay discoverable, and keep it non-operable.\n- **Icon-only buttons**: real `<button>` with an `aria-label`.\n- **Custom clickable divs/cells**: prefer a semantic element; if a cell/card must open something it needs Enter/Space handling, not just `onClick`.\n\n## Best practices (settings grids + modals)\n\nApply these when building or reviewing AG Grid tables that open an edit modal (reference: `GlobalVariablesPage`, `tests/a11y/global-variables.a11y.spec.ts`):\n\n- **Keyboard map on selectable rows:** **Space** toggles the row checkbox; **Enter** opens the row’s edit/detail UI. Do not use Space to open the modal.\n- **Scope the map on the page:** implement with page-level `onCellKeyDown` and per-column `suppressKeyboardEvent` for Enter/Space so AG Grid’s default Space handling does not compete. Prefer not changing shared `TableComponent` defaults unless the same map is product-wide.\n- **Keep selection UI in sync:** after `node.setSelected`, update React selection state so toolbar actions (e.g. delete) enable/disable correctly (`TableOptions.hasSelection` is read at render time).\n- **Retain place on modal close (2.4.3):** remember the focused cell (`rowIndex` + `colId`) when opening edit; on close (Escape, Cancel, or save) restore with `api.setFocusedCell` + DOM `.focus()`, using a few `requestAnimationFrame`s to outlast Radix dialog cleanup. Controlled edit dialogs without a Radix trigger need this explicitly. Create flows that use a real `DialogTrigger` (e.g. Add New) should restore to that trigger.\n- **Verify with keyboard tests:** open from a cell → Escape → focus is still on that cell; Enter can open again without a mouse re-click.\n\n## Choose Test Layer\n\n### Jest + axe\n\nUse Jest when the changed surface is a primitive or component that renders in jsdom without full app routing.\n\n```tsx\nimport { render, screen } from \"@testing-library/react\";\nimport { axe } from \"@/utils/a11y-test\";\n\nit(\"should_have_no_axe_violations\", async () => {\n  const { container } = render(<Component />);\n  expect(await axe(container)).toHaveNoViolations();\n});\n```\n\nNotes: `a11y-test.ts` disables `color-contrast` (jsdom can't measure layout). Prefer role/label/text queries. Assert accessible names explicitly for icon-only / visually-hidden / generated labels. Put tests at `__tests__/<name>.a11y.test.tsx`.\n\n```bash\ncd src/frontend\nnpx jest path/to/<name>.a11y.test.tsx --runInBand\n```\n\n### Playwright + `page.runA11yScan`\n\nUse Playwright when the surface needs real browser layout, routing, app state, mocking, modals, tables, focus order, keyboard behavior, or full-page interactions.\n\n```ts\nimport { expect, test } from \"../fixtures\";\nimport { awaitBootstrapTest } from \"../utils/await-bootstrap-test\";\n\ntest(\"scans populated state\", { tag: [\"@release\", \"@api\"] }, async ({ page }) => {\n  await awaitBootstrapTest(page, { skipModal: true });\n  await page.goto(\"/settings/example\");\n  await expect(page.getByText(\"Example\")).toBeVisible();\n  await page.runA11yScan(\"settings-example-populated\");\n});\n```\n\nRules: put specs under `tests/a11y/<feature>.a11y.spec.ts`; import `test`/`expect` from `../fixtures`; tag every test `@release` plus a domain tag (`@workspace`/`@api`/`@database`/`@components`/`@starter-projects`); stable scan names (lowercase, feature-first, state-specific); mock API for deterministic states; disable animations for focus assertions; use explicit interactions (no random clicking of destructive controls). If the route belongs in static coverage, update `scripts/a11y/a11y_routes.json`.\n\n```bash\ncd src/frontend\nRUN_A11Y=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\nnpm run a11y:html-report --silent\n```\n\n## IBM baselines (accepting known debt)\n\nSome IBM violations are real but framework-level and can't be fixed per-page (e.g. `aria_content_in_landmark` on a Radix menu portal). Track them with an IBM baseline so the scan stays green while the debt is recorded — do NOT silently drop the scan or the assertion.\n\nHow it works (`fixtures.ts` + `.achecker.yml`):\n\n- `baselineFolder: tests/a11y/baselines`. The baseline file name is the **scan label**: `{project}__{label}.json`, e.g. `chromium__assets-files-actions-menu.json` (dashes preserved; the first scan in a test has no index suffix).\n- On each run IBM's `filterReport` loads that file and marks any result matching a baseline entry by **`path.dom` + `ruleId` + `reasonId`** as `ignored: true`. `countNewA11yViolations` counts `violation && !ignored`, so baselined issues don't fail.\n\nCreate one:\n\n1. Run the scan once — it writes the full report to `coverage/accessibility-reports/{label}.json`.\n2. Copy the offending result(s) into `tests/a11y/baselines/{label}.json` as `{ \"results\": [ { \"ruleId\", \"reasonId\", \"path\": { \"dom\": … } } ] }`. A **minimal** baseline (only the entries to ignore) is clearest; add a `description` field noting why and how to resurface it.\n3. Re-run → the scan passes. Commit the baseline so the debt is tracked; **deleting it resurfaces the violation**. Point at the baseline from a comment in the spec.\n\nMatching is exact-DOM-path, so baselines can go stale if unrelated `<body>`-level portals (toasts) shift the portal index — scan the state with no other overlays open.\n\n## Route/Page Coverage Matrix\n\nFor route/page changes, cover the smallest matrix representing real user states:\n\n- Loaded/default, empty data, populated data\n- Primary modal open, dropdown/popover open\n- Validation or error visible\n- Selected/expanded table row or bulk-action state\n- Mobile viewport when responsive layout changes\n\nDon't force states the page can't enter; briefly explain any skipped state.\n\n## Data-Rich Route Pattern\n\nUse `tests/a11y/api-keys.a11y.spec.ts` and `tests/a11y/files.a11y.spec.ts` as the models for route-level work: mock API data; scan populated table; scan empty table; open create modal and scan; submit to the generated-result modal and scan; open a text-cell modal and scan; select a row and scan the selected state; set a mobile viewport and scan. Add keyboard-interaction tests (not scans) for tab-out, reverse re-entry, Enter-to-open, focus restore, and focus-visible where the surface has custom keyboard behavior. This is the expected bar for data-rich routes.\n\n**Driving grid row states**: client-rendered states (upload progress %, upload-failed/error, disabled rows) are usually driven by fields the cell renderer reads off the row (`params.data.*`). If the list query returns the response untransformed, just inject the field into the mocked rows (e.g. `{ ...file, progress: -1 }`) to scan the error/progress state — no need to simulate the real flow.\n\n## Report Back\n\nWhen done, state:\n\n- Test files added or updated, and states scanned.\n- Commands run — including whether BOTH axe and IBM were run and both reported zero.\n- Any states not covered and why.\n- Any remaining a11y risk or accepted limitation.\n","AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to AI coding agents when working with code in this repository.\n\n## Project Overview\n\nLangflow is a visual workflow builder for AI-powered agents. It has a Python/FastAPI backend, React/TypeScript frontend, and a lightweight executor CLI (lfx).\n\n## Prerequisites\n\n- **Python:** 3.10-3.14\n- **uv:** >=0.4 (Python package manager)\n- **Node.js:** >=20.19.0 (v22.12 LTS recommended)\n- **npm:** v10.9+\n- **make:** For build coordination\n\n## Common Commands\n\n### Development Setup\n```bash\nmake init              # Install all dependencies + pre-commit hooks\nmake run_cli           # Build and run Langflow (http://localhost:7860)\nmake run_clic          # Clean build and run (use when frontend issues occur)\n```\n\n### Development Mode (Hot Reload)\n```bash\nmake backend           # FastAPI on port 7860 (terminal 1)\nmake frontend          # Vite dev server on port 3000 (terminal 2)\n```\n\nFor component development, enable dynamic loading:\n```bash\nLFX_DEV=1 make backend                    # Load all components dynamically\nLFX_DEV=mistral,openai make backend       # Load only specific modules\n```\n\n### Code Quality\n```bash\nmake format_backend    # Format Python (ruff) - run FIRST before lint\nmake format_frontend   # Format TypeScript (biome)\nmake format            # Both\nmake lint              # mypy type checking\n```\n\n### Testing\n```bash\nmake unit_tests                    # Backend unit tests (pytest, parallel)\nmake unit_tests async=false        # Sequential tests\nuv run pytest path/to/test.py      # Single test file\nuv run pytest path/to/test.py::test_name  # Single test\n\nmake test_frontend                 # Jest unit tests\nmake tests_frontend                # Playwright e2e tests\n```\n\n### Database Migrations\n```bash\nmake alembic-revision message=\"Description\"  # Create migration\nmake alembic-upgrade                         # Apply migrations\nmake alembic-downgrade                       # Rollback one version\n```\n\n## Architecture\n\n### Monorepo Structure\n```\nsrc/\n├── backend/\n│   ├── base/langflow/     # Core backend package (langflow-base)\n│   │   ├── api/           # FastAPI routes (v1/, v2/)\n│   │   ├── components/    # Built-in Langflow components\n│   │   ├── services/      # Service layer (auth, database, cache, etc.)\n│   │   ├── graph/         # Flow graph execution engine\n│   │   └── custom/        # Custom component framework\n│   └── tests/             # Backend tests\n├── frontend/              # React/TypeScript UI\n│   └── src/\n│       ├── components/    # UI components\n│       ├── stores/        # Zustand state management\n│       └── icons/         # Component icons\n├── langflow-core/         # Usable provider-free Langflow distribution\n├── bundles/               # Curated provider integrations\n└── lfx/                   # Lightweight executor and shared primitives\n```\n\n### Key Packages\n- **langflow**: Full end-user package; depends on `langflow-core` and curated provider bundles\n- **langflow-core**: Service-complete, provider-bundle-free distribution; owns the `langflow` CLI\n- **langflow-base**: Modular application platform (API, services, graph engine); extras add service integrations\n- **lfx**: Shared execution primitives and standalone CLI (`lfx serve`, `lfx run`)\n\nThe public dependency direction is `langflow → langflow-core → langflow-base → lfx`.\nProvider packages under `src/bundles/` are added only by the full `langflow` distribution.\n\n### Service Layer\nBackend services in `src/backend/base/langflow/services/`:\n- `auth/` - Authentication\n- `authorization/` - Authorization (RBAC) plugin layer — see below\n- `database/` - SQLAlchemy models and migrations\n- `cache/` - Caching layer\n- `storage/` - File storage\n- `tracing/` - Observability integrations\n\n### Authorization (RBAC)\n\nAuthorization is a pluggable layer separate from authentication:\n\n- **OSS** ships the interface (`BaseAuthorizationService` in `lfx`) + a pass-through implementation (`LangflowAuthorizationService`) + the `authz_*` and `casbin_rule` DB schema + route guards.\n- Implementations register via the `lfx.services` entry point `authorization_service` in `lfx.toml` (same pattern as the SSO `auth_service`). A registered plugin reads the `authz_*` admin tables and writes compiled rules to `casbin_rule`.\n\nDefault is **off**: `LANGFLOW_AUTHZ_ENABLED=false`. When enabled with only the OSS stub registered, every check returns allow — the stub is a no-op so routes stay wired and audit rows still flow. Real allow/deny requires a registered authorization plugin.\n\nRoute guards live in `langflow.services.authorization.guards` (the legacy `langflow.services.authorization.utils` path re-exports them for backward compatibility):\n- `ensure_flow_permission(user, FlowAction.*, flow_id=..., flow_user_id=..., workspace_id=..., folder_id=...)` — single-flow CRUD + execute\n- `ensure_deployment_permission(user, DeploymentAction.*, deployment_id=..., deployment_user_id=..., workspace_id=..., project_id=...)`\n- `ensure_project_permission(user, ProjectAction.*, project_id=..., project_user_id=..., workspace_id=...)`\n- `ensure_knowledge_base_permission(user, KnowledgeBaseAction.*, kb_name=..., kb_user_id=...)`\n- `ensure_variable_permission(user, VariableAction.*, variable_id=..., variable_user_id=...)`\n- `ensure_file_permission(user, FileAction.*, file_id=..., file_user_id=...)`\n- `ensure_share_permission(user, ShareAction.*, share_id=..., share_user_id=...)`\n- `filter_visible_resources(user, resource_type=..., candidates=..., act=...)` — list-endpoint filter; safe no-op in OSS\n\nThe enforcement request shape is `(subject, domain, object, action)`:\n- subject = `user:{uuid}`\n- domain = `project:{uuid}` → `workspace:{uuid}` → `*` (resolved by `_resolve_flow_domain`; the more specific domain wins so project-scoped grants match directly while workspace-scoped grants still flow down via plugin-side role inheritance)\n- object = `flow:{uuid}` / `deployment:{uuid}` / `project:{uuid}` / `flow:*` / etc.\n- action = `read` / `write` / `create` / `delete` / `execute` / `deploy`\n\n**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher, variable PATCH/DELETE in `variable.py`) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.\n\n**Share CRUD API (Phase 3):** `/api/v1/authz/shares` provides POST / GET / PATCH / DELETE on `authz_share` rows. The handler enforces an OSS floor (resource owner or superuser may administer shares for that resource) so the OSS pass-through cannot let a non-owner mint share rows. Each write fires `BaseAuthorizationService.invalidate_user` / `invalidate_all` so a registered enforcer can drop cached policy. Audit rows are written via `audit_decision` with `share:create` / `share:update` / `share:delete` actions.\n\n**Audit query API (Phase 4):** `GET /api/v1/authz/audit` (superuser-only) exposes a paginated, filterable view of `authz_audit_log`. Supports `user_id`, `resource_type`, `resource_id`, `action`, `result`, `since`, `until` filters; page size capped at 200.\n\n**Default role catalog (Phase 4):** the consolidated foundations migration `7c8d9e0f1a2b_authz_foundations` seeds the three built-in `is_system=True` roles (viewer / developer / admin) with `\"{resource}:{action}\"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.\n\n## Component Development\n\nComponents live in `src/backend/base/langflow/components/`. To add a new component:\n\n1. Create component class inheriting from `Component`\n2. Define `display_name`, `description`, `icon`, `inputs`, `outputs`\n3. Add to `__init__.py` (alphabetical order)\n4. Run with `LFX_DEV=1 make backend` for hot reload\n\n**IMPORTANT:** Changing a component's class name is a breaking change and should never be done. The class name serves as an identifier used to match components in saved flows and to flag them for updates in the UI. Renaming it will break existing flows that use that component.\n\n### Component Structure\n```python\nfrom langflow.custom import Component\nfrom langflow.io import MessageTextInput, Output\n\nclass MyComponent(Component):\n    display_name = \"My Component\"\n    description = \"What it does\"\n    icon = \"component-icon\"  # Lucide icon name or custom\n\n    inputs = [\n        MessageTextInput(name=\"input_value\", display_name=\"Input\"),\n    ]\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"process\"),\n    ]\n\n    def process(self) -> Message:\n        # Component logic\n        return Message(text=self.input_value)\n```\n\n### Component Testing\nTests go in `src/backend/tests/unit/components/`. Use base classes:\n- `ComponentTestBaseWithClient` - Components needing API access\n- `ComponentTestBaseWithoutClient` - Pure logic components\n\nRequired fixtures: `component_class`, `default_kwargs`, `file_names_mapping`\n\n## Frontend Development\n\n- **React 19** + TypeScript + Vite\n- **Zustand** for state management\n- **@xyflow/react** for graph visualization\n- **Tailwind CSS** for styling\n\n### Custom Icons\n1. Create SVG component in `src/frontend/src/icons/YourIcon/`\n2. Export with `forwardRef` and `isDark` prop support\n3. Add to `lazyIconImports.ts`\n4. Set `icon = \"YourIcon\"` in Python component\n\n## Testing Notes\n\n- `@pytest.mark.api_key_required` - Tests requiring external API keys\n- `@pytest.mark.no_blockbuster` - Skip blockbuster plugin\n- Database tests may fail in batch but pass individually\n- Pre-commit hooks require `uv run git commit`\n- Always use `uv run` when running Python commands\n- When running tests inside a sub-package (e.g. `langflow-base`, `lfx`), sync that package's dev group first: `uv sync --group dev --package langflow-base`. The default `uv sync` only resolves the top-level workspace and may leave dev-only test deps (e.g. `fakeredis`) uninstalled.\n\n### Graph Testing Pattern\n\nProper Graph tests follow this pattern:\n1. Build graph with connected components\n2. Connect them via `.set()` calls\n3. Call `async_start` and iterate over the results\n4. Validate the results\n\n### Testing Best Practices\n\n- Avoid mocking in tests when possible\n- Prefer real integrations for more reliable tests\n\n## Version Management\n```bash\nmake patch v=1.5.0  # Update version across all packages\n```\n\nThis updates: `pyproject.toml`, `src/backend/base/pyproject.toml`, `src/frontend/package.json`\n\n## Pre-commit Workflow\n\nPre-commit hooks run ruff and biome automatically on `git commit`, so manual\nformatting is not required. To avoid an extra commit cycle when you have many\nchanges:\n\n1. Run `make format_backend` once before staging - fixes most ruff issues up front.\n2. Run `uv run git commit` (the `uv run` ensures pre-commit finds the right Python).\n3. If you touched backend code, run `make unit_tests` locally for faster feedback than CI.\n\n## Pull Request Guidelines\n\n- Follow [semantic commit conventions](https://www.conventionalcommits.org/)\n- Reference any issues fixed (e.g., `Fixes #1234`)\n- Ensure all tests pass before submitting\n\n## Documentation\n\nDocumentation uses Docusaurus and lives in `docs/`:\n```bash\ncd docs\nyarn install\nyarn start        # Dev server on port 3000 (prompts for 3001 if 3000 is in use)\n```\n",".agents/skills/e2e-testing/SKILL.md":"---\nname: e2e-testing\ndescription: Write and review Playwright E2E tests for Langflow. Trigger when the user asks to write, fix, or review E2E tests, spec files, Playwright tests, or integration tests that exercise the full UI. Also trigger when modifying data-testid attributes, test helpers in tests/utils/, or fixture configuration.\n---\n\n# Langflow E2E Testing (Playwright)\n\n## When to Apply\n\n- User asks to **write E2E tests** for a feature or flow\n- User asks to **fix a failing E2E test**\n- User asks to **review E2E test coverage**\n- User modifies `data-testid` attributes in components (may break existing tests)\n- User changes test utilities in `src/frontend/tests/utils/`\n\n**Do NOT apply** when:\n- User asks about unit tests (use `frontend-testing` skill for Jest)\n- User asks about backend tests (use `backend-code-review` skill for pytest)\n\n## Tech Stack\n\n| Tool | Version | Purpose |\n|------|---------|---------|\n| Playwright | 1.59.1 | E2E test runner + browser automation |\n| Chromium | (bundled) | Default browser (Firefox/Safari disabled) |\n| Custom fixtures | `tests/fixtures.ts` | Auto-detects API errors and flow execution failures |\n\n## Key Commands\n\n```bash\n# Run all E2E tests\nnpx playwright test\n\n# Run tests filtered by tag\nnpx playwright test --grep \"@release\"\nnpx playwright test --grep \"@workspace\"\nnpx playwright test --grep \"@starter-projects\"\n\n# Run a specific test file\nnpx playwright test tests/core/features/run-flow.spec.ts\n\n# Debug mode (headed browser + step through)\nnpx playwright test --debug\n\n# Show HTML report after run\nnpx playwright show-report\n\n# Update snapshots (if used)\nnpx playwright test --update-snapshots\n```\n\n## Configuration\n\n**File**: `src/frontend/playwright.config.ts`\n\n| Setting | Value | Why |\n|---------|-------|-----|\n| `fullyParallel` | `true` | Tests run in parallel for speed |\n| `timeout` | 5 minutes | Flow builds can be slow; prevents false timeouts |\n| `retries` | 3 (local), 2 (CI) | Flaky network/rendering issues; retries catch them |\n| `workers` | 2 | Balances speed and resource usage |\n| `actionTimeout` | 20s | Individual action timeout (click, fill, etc.) |\n| `trace` | `on-first-retry` | Captures trace on failures for debugging |\n| `baseURL` | `http://localhost:3000` | Vite dev server |\n\n**WebServer**: Playwright auto-starts backend (uvicorn on 7860) + frontend (npm start on 3000).\n\n## Directory Structure\n\n```\nsrc/frontend/tests/\n├── fixtures.ts                     # Custom test fixture with error detection\n├── globalTeardown.ts               # Cleanup (removes temp DB after tests)\n├── core/\n│   ├── features/                   # Main feature tests (run-flow, playground, etc.)\n│   ├── integrations/               # Starter project / template tests\n│   ├── regression/                 # Bug regression tests\n│   └── unit/                       # Component-level Playwright tests\n├── extended/\n│   ├── features/                   # Extended features (MCP, auto-save, etc.)\n│   ├── integrations/               # Extended integrations\n│   └── regression/                 # Extended regressions\n└── utils/                          # 37+ shared helper functions\n```\n\n## File Naming\n\n- **kebab-case** with `.spec.ts` suffix: `run-flow.spec.ts`, `playground.spec.ts`, `flow-lock.spec.ts`\n- Template tests may use spaces: `Document QA.spec.ts`, `Social Media Agent.spec.ts`\n- Sharded tests for parallelization: `chatInputOutputUser-shard-0.spec.ts`\n\n> **Note**: E2E tests use `.spec.ts` (Playwright convention). Unit tests use `.test.tsx` (Jest convention). Do not mix them.\n\n## Test Anatomy\n\n### Basic Test\n\n```typescript\nimport { expect, test } from \"../../fixtures\";\nimport { awaitBootstrapTest } from \"../../utils/await-bootstrap-test\";\n\ntest(\n  \"user should be able to run a flow successfully\",\n  { tag: [\"@release\", \"@workspace\"] },\n  async ({ page }) => {\n    await awaitBootstrapTest(page);\n\n    // Arrange: Create a flow\n    await page.getByTestId(\"blank-flow\").click();\n\n    // Act: Add components and run\n    await page.getByTestId(\"sidebar-search-input\").fill(\"Chat Output\");\n    // ... setup ...\n\n    // Assert: Verify result\n    await expect(page.getByTestId(\"build-status-success\")).toBeVisible({ timeout: 30000 });\n  },\n);\n```\n\n### With test.describe\n\n```typescript\ntest.describe(\"Flow Lock Feature\", () => {\n  test(\n    \"should lock and unlock a flow\",\n    { tag: [\"@release\", \"@api\"] },\n    async ({ page }) => {\n      // ...\n    },\n  );\n\n  test(\n    \"should prevent editing when locked\",\n    { tag: [\"@release\"] },\n    async ({ page }) => {\n      // ...\n    },\n  );\n});\n```\n\n### With Serial Mode (tests that depend on order)\n\n```typescript\ntest.describe.configure({ mode: \"serial\" });\n\ntest(\"step 1: create flow\", async ({ page }) => { /* ... */ });\ntest(\"step 2: edit flow\", async ({ page }) => { /* ... */ });\ntest(\"step 3: delete flow\", async ({ page }) => { /* ... */ });\n```\n\n### With Event Delivery Modes (streaming/polling/direct)\n\n```typescript\nimport { withEventDeliveryModes } from \"../../utils/withEventDeliveryModes\";\n\nwithEventDeliveryModes(\n  \"Document Q&A should work\",\n  { tag: [\"@release\", \"@starter-projects\"] },\n  async ({ page }) => {\n    // This test runs 3 times: streaming, polling, direct\n    // Each mode is configured automatically via route interception\n  },\n);\n```\n\n## Tags System\n\nEvery test MUST be tagged with `@release` — the release run greps for it, so an\nuntagged or wrongly-tagged spec silently drops out of release coverage. Add the\ndomain tag(s) below on top of `@release` (a test can have more than one). These\nsix are the only allowed tags; do not invent new ones.\n\n| Tag | Purpose | When to Use |\n|-----|---------|-------------|\n| `@release` | Part of the release run (required on every spec) | All tests |\n| `@workspace` | Workspace/flow management | Creating, editing, deleting flows |\n| `@api` | API-dependent features | Tests that call backend endpoints |\n| `@database` | Database operations | Tests involving persistence |\n| `@components` | Component-level tests | Individual component behavior |\n| `@starter-projects` | Template/starter project tests | Pre-built flow templates |\n\n```typescript\n// Right: tag your test\ntest(\"my feature test\", { tag: [\"@release\", \"@workspace\"] }, async ({ page }) => { ... });\n\n// Wrong: no tags — test can't be filtered\ntest(\"my feature test\", async ({ page }) => { ... });\n```\n\n## Custom Fixtures: Error Detection\n\n**Always import `test` and `expect` from `../../fixtures`, NOT from `@playwright/test`.**\n\n```typescript\n// Right\nimport { expect, test } from \"../../fixtures\";\n\n// Wrong — bypasses error detection\nimport { expect, test } from \"@playwright/test\";\n```\n\nWhy: The custom fixture automatically monitors all `/api/` responses and fails the test if:\n- HTTP 400, 404, 422, or 500 errors occur\n- Flow execution returns `error: true` in event streams\n- Python exceptions appear in streamed responses\n\nTo opt-in to expected errors (e.g., testing error handling):\n```typescript\ntest(\"should show error on invalid input\", { tag: [\"@release\"] }, async ({ page }) => {\n  page.allowFlowErrors();  // Allow flow errors for this test\n  // ... test that expects errors ...\n});\n```\n\n## Selector Strategy\n\n### Priority (in order of preference)\n\n1. **`getByTestId`** — Most stable, used 95% of the time in Langflow\n2. **`getByRole`** — For buttons, headings, and form elements\n3. **`getByText`** — For visible text content\n4. **`waitForSelector`** — For CSS selectors and dynamic elements\n5. **`locator`** — For complex selectors (CSS, XPath)\n\n### Common data-testid Patterns\n\n**Canvas & Navigation:**\n- `blank-flow` — New blank flow button\n- `sidebar-search-input` — Component search\n- `canvas_controls_dropdown` — Canvas controls menu\n- `fit_view`, `zoom_out`, `zoom_in` — Canvas controls\n- `react-flow-id` — ReactFlow canvas container\n\n**Component Fields:**\n- `popover-anchor-input-{fieldname}` — Input field for a component parameter\n- `input-chat-playground` — Playground chat input\n- `div-chat-message` — Chat message in playground\n\n**Actions:**\n- `add-component-button-{component}` — Add component to canvas\n- `button-send` — Send chat message\n- `button_run_{component}` — Run specific component\n- `publish-button`, `save-flow-button` — Flow actions\n- `edit-fields-button` — Toggle inspection panel field editor\n\n**Modals & Panels:**\n- `modal-title` — Modal heading\n- `icon-Globe` — Global variables\n- `icon-Lock` — Flow lock toggle\n- `session-selector` — Playground session switcher\n\n### Important: Global Variables and Badges\n\nWhen a component field has a global variable selected (`load_from_db: true` + `value: \"OPENAI_API_KEY\"`), the field renders a **badge** instead of an `<input>` element. This means `getByTestId(\"popover-anchor-input-api_key\")` will NOT find the element — it doesn't exist in the DOM.\n\nTemplates with global variables pre-selected: Market Research, Price Deal Finder, Research Agent.\nTemplates without (input IS rendered): Instagram Copywriter.\n\n## Core Helper Functions\n\nLocated in `src/frontend/tests/utils/`:\n\n| Function | What it Does | When to Use |\n|----------|-------------|-------------|\n| `awaitBootstrapTest(page)` | Waits for app to fully load | **Start of every test** |\n| `initialGPTsetup(page)` | Full setup: adjustView → updateComponents → selectModel → addKey → adjustView → unselectNodes | Tests that need OpenAI configured |\n| `adjustScreenView(page, opts?)` | Fit view + zoom out | After adding components to canvas |\n| `zoomOut(page, times)` | Zoom out N times | When components are too small |\n| `selectGptModel(page)` | Selects gpt-4o-mini for all Language Model nodes | GPT-dependent tests |\n| `addOpenAiInputKey(page)` | Fills OPENAI_API_KEY for all openai_api_key fields | Tests requiring API key |\n| `enableInspectPanel(page)` | Toggles inspection panel ON | **MUST call before `edit-fields-button`** |\n| `disableInspectPanel(page)` | Toggles inspection panel OFF | Cleanup after inspection |\n| `updateOldComponents(page)` | Clicks \"Update all\" if outdated components exist | After loading saved flows |\n| `unselectNodes(page)` | Clicks empty canvas area to deselect all nodes | After node operations |\n| `renameFlow(page, { flowName })` | Renames the current flow | Flow management tests |\n| `uploadFile(page, filename)` | Uploads a file from test assets | File upload tests |\n| `withEventDeliveryModes(...)` | Runs test 3x: streaming, polling, direct | Starter project tests |\n\n### initialGPTsetup Options\n\n```typescript\nawait initialGPTsetup(page);  // All steps\n\nawait initialGPTsetup(page, {\n  skipAdjustScreenView: true,\n  skipUpdateOldComponents: true,\n  skipSelectGptModel: true,\n});\n```\n\n### Inspection Panel Pattern (CRITICAL)\n\n```typescript\n// MUST enable inspection panel FIRST\nawait enableInspectPanel(page);\n\n// Click a node to select it\nawait page.getByTestId(\"title-OpenAI\").click();\n\n// Open field editor\nawait page.getByTestId(\"edit-fields-button\").click();\n\n// Toggle field visibility\nawait page.getByTestId(\"showmodel_name\").click();\n\n// Close field editor\nawait page.getByTestId(\"edit-fields-button\").click();\n```\n\n**If you skip `enableInspectPanel(page)`, the `edit-fields-button` will NOT be visible.**\n\n## Skip Patterns\n\n```typescript\n// Skip test if env var missing\ntest.skip(!process?.env?.OPENAI_API_KEY, \"OPENAI_API_KEY required to run this test\");\n\n// Skip test unconditionally with reason\ntest.skip(true, \"Feature not yet implemented with new designs\");\n```\n\n## Writing Good E2E Tests\n\n### Do:\n- **Tag every test** with `@release` (plus any domain tags that apply)\n- **Import from `../../fixtures`**, not `@playwright/test`\n- **Start with `awaitBootstrapTest(page)`** — always\n- **Use `getByTestId`** for stable selectors\n- **Set explicit timeouts** on `waitForSelector` and `expect(...).toBeVisible()` for async operations\n- **Test the complete user flow**: setup → action → verification\n- **Use `withEventDeliveryModes`** for tests that involve flow execution (chat, build)\n\n### Don't:\n- **Don't use `page.waitForTimeout()`** unless absolutely necessary — prefer `waitForSelector` or `expect().toBeVisible()`\n- **Don't hardcode API keys** — read from `process.env.OPENAI_API_KEY`\n- **Don't skip tests without a reason** — always provide the second argument to `test.skip()`\n- **Don't import from `@playwright/test`** — use the custom fixtures\n- **Don't forget `enableInspectPanel(page)`** before accessing `edit-fields-button`\n- **Don't assume input fields exist** when global variables are selected (badge renders instead)\n\n### Challenge Tests (Apply Here Too)\n\nE2E tests should also cover adversarial scenarios:\n- **Invalid input**: paste 10K characters, special characters (`<script>alert(1)</script>`), empty submissions\n- **Network interruption**: what happens if the user loses connection mid-build?\n- **Permission boundaries**: can a user access another user's flow via direct URL?\n- **Concurrent actions**: double-click delete, rapid chat messages\n- **Error recovery**: does the UI recover gracefully from a 500 error?\n\n## References\n\n- [Selector Patterns](references/selectors.md) — Complete data-testid catalog\n- [Helper Functions](references/helpers.md) — Detailed documentation of all 37+ utility functions\n- [Test Fixtures](references/fixtures.md) — Custom fixture error detection behavior\n",".agents/skills/ibm-a11y-pr-remediation/SKILL.md":"---\nname: ibm-a11y-pr-remediation\ndescription: Scan every frontend surface touched by the current PR/branch for IBM Equal Access Level 1 accessibility issues and fix all in-scope violations by default. Discovers changed files, maps them to routes/components/states, runs both axe and IBM engines, remediates until both are green (or only documented baselines remain), and reports back. Use when the user asks to check, scan, or clean up accessibility for \"this PR\", \"my branch\", or \"my changes\" and wants fixes applied, not just a report. For a single scoped audit report without a default fix pass, use ibm-a11y-level1-audit; for a route batch scan only, use ibm-a11y-route-scan.\ndisable-model-invocation: true\n---\n\n# IBM Level 1 PR Accessibility Remediation\n\nScope is **IBM Equal Access Level 1 only**. **Default mode is fix, not report-only.** Scan every frontend surface touched by the PR/branch and remediate all in-scope issues until both engines are green (or only documented baselines remain).\n\nThis skill is a PR-scoped orchestrator. It does not duplicate detailed engine/pattern guidance — read the linked skills for that:\n\n- [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) — which engine/test layer to use, POUR checklist, axe-vs-IBM gaps, Radix/AG-Grid gotchas, baselines.\n- [ibm-a11y-route-scan](../ibm-a11y-route-scan/SKILL.md) — Python scanner options for ad-hoc route batches.\n- [ibm-a11y-level1-audit](../ibm-a11y-level1-audit/SKILL.md) — Level 1 criteria references and report template, useful when the user wants a formal audit report for the PR instead of (or in addition to) fixes.\n- [frontend-i18n](../frontend-i18n/SKILL.md) — accessible names / UI strings must go through i18n.\n\n## Mandate\n\n1. Diff the PR (or current branch vs its merge base) for `src/frontend/**` changes.\n2. Map changed files → UI surfaces → routes / components / states to scan.\n3. Run **both** axe (Jest where applicable) and IBM Equal Access (Playwright `page.runA11yScan` and/or `scripts/a11y/a11y_scan.py` — see `ibm-a11y-testing-guide` / `ibm-a11y-route-scan`).\n4. **Fix every in-scope Level 1 violation** in the changed surfaces. Do not stop at a findings list unless the user says **report only** (in that case, hand off to `ibm-a11y-level1-audit`).\n5. Re-scan until assert mode passes. Add/update a11y specs when coverage is missing.\n6. Report what changed, commands run, and any baselined/deferred debt.\n\nDo **not** invent new tag names, silently disable scans, or expand into IBM Level 2/3 unless asked.\n\n## Progress checklist\n\n```\nIBM L1 PR A11y:\n- [ ] 1. Collect changed frontend files\n- [ ] 2. Map files → surfaces / routes / states\n- [ ] 3. Scan (IBM + axe)\n- [ ] 4. Fix all in-scope violations\n- [ ] 5. Add/update tests if needed\n- [ ] 6. Re-scan assert-green\n- [ ] 7. Report back\n```\n\n## 1. Collect changed frontend files\n\nPrefer the PR merge base when a PR exists; otherwise the branch merge base vs `main`/`master`.\n\n```bash\n# PR number known\ngh pr diff <n> --name-only | grep -E '^src/frontend/' || true\n\n# Current branch vs upstream default\nBASE=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD origin/master)\ngit diff --name-only \"$BASE\"...HEAD -- 'src/frontend/**'\n\n# Include uncommitted work when the user is mid-change\ngit diff --name-only HEAD -- 'src/frontend/**'\ngit diff --name-only --cached -- 'src/frontend/**'\ngit ls-files --others --exclude-standard 'src/frontend/**'\n```\n\nInclude:\n- `src/frontend/src/**/*.{tsx,ts,jsx,js,css}` (UI)\n- `src/frontend/tests/a11y/**` (existing coverage)\n- Locale files only when they change accessible names / labels\n\nSkip pure non-UI churn unless it affects a11y (e.g. test helpers that change focus / ARIA). If **no** frontend files changed, say so and stop.\n\n## 2. Map files → surfaces\n\nFor each changed file, identify:\n\n| Change type | Scan target |\n|-------------|-------------|\n| Page / route | That route + meaningful states (empty/populated/modal/mobile) |\n| Shared component (`TableComponent`, dialogs, menus) | **Every** consumer page that uses it — not only the file you touched |\n| Primitive | Jest axe on the primitive + any Playwright surface that embeds it |\n| Spec / baseline only | Re-run that spec; no product fix unless it fails |\n| `a11y_routes.json` | Update static coverage; run static or route scan |\n\nList interactive controls and states (default, empty, populated, open modal / menu, selected row, error, mobile). Prefer existing specs under `src/frontend/tests/a11y/`.\n\n## 3. Scan (both engines must pass)\n\nAutomated a11y is not one tool — see `ibm-a11y-testing-guide` for the full engine comparison and gotchas. Summary:\n\n- **axe-core** — Jest `axe()` (`@/utils/a11y-test`), jsdom-only.\n- **IBM Equal Access** — stricter on ARIA structure and keyboard semantics. Playwright `page.runA11yScan(label)` for stateful surfaces (modals/menus/selected/editing); `scripts/a11y/a11y_scan.py` for default-loaded page only.\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# Python scanner playwright deps are NOT in default uv sync.\n# One-time: uv run --with playwright playwright install chromium\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes /settings/<route> \\\n  --out /tmp/a11y.json --markdown /tmp/a11y.md --timeout-ms 45000\n```\n\n`RUN_A11Y=true` runs the scan; `RUN_A11Y_ASSERT=true` fails on new violations. After changing a shared component, re-scan every page that uses it.\n\nFor component-only changes:\n\n```bash\ncd src/frontend\nnpx jest path/to/<name>.a11y.test.tsx --runInBand\n```\n\nDo not invent findings — prefer scanner output plus manual Level 1 spot checks scanners miss (keyboard trap both ways, focus restore, 320px reflow, color-not-only).\n\n## 4. Fix all in-scope Level 1 violations\n\nDefault: **fix**. Only list proposed fixes without editing if the user asked **report only** — then hand off to `ibm-a11y-level1-audit` for the formal report format.\n\nRules:\n- Prefer semantic HTML over ARIA.\n- Follow the Langflow patterns in `ibm-a11y-testing-guide` (AG Grid, Radix `asChild`, focus restore, icon-only `aria-label`).\n- Route new UI strings / `aria-label`s through i18n (`t(...)`, all locale files) per `frontend-i18n`.\n- Keep fixes minimal; do not refactor unrelated UI.\n- Do **not** silently disable scans. Use IBM baselines under `src/frontend/tests/a11y/baselines/` only for documented framework debt.\n- Map each issue to a Level 1 WCAG/IBM id; defer anything listed as Level 2/3 in the criteria guide (`ibm-a11y-level1-audit/references/ibm-level1-criteria.md`) unless the user expands scope.\n\n### Manual Level 1 spot checks (when relevant)\n\n- **2.1.1 / 2.1.2:** Tab and Shift+Tab; Escape closes overlays; no trap.\n- **2.4.3 / 2.4.7:** Focus order matches visual order; focus ring visible.\n- **1.4.10:** 320px / ~400% zoom — no essential horizontal scroll.\n- **1.4.1:** Status/errors not color-only.\n- **3.3.1 / 3.3.2:** Errors in text and tied to fields; inputs labeled.\n\n## 5. Tests / coverage\n\n| Surface | Spec |\n|---------|------|\n| Static routes | `static-routes.a11y.spec.ts` (+ `scripts/a11y/a11y_routes.json`) |\n| Auth | `auth-pages.a11y.spec.ts` |\n| Core pages | `core-pages.a11y.spec.ts` |\n| Data-rich | `files.a11y.spec.ts`, `api-keys.a11y.spec.ts`, `global-variables.a11y.spec.ts` |\n| Other data-rich | `data-rich-routes.a11y.spec.ts` |\n\nIf you fixed a state with no scan, add one (and keyboard tests for custom keyboard behavior) following `files.a11y.spec.ts` / `api-keys.a11y.spec.ts` patterns.\n\nTag every Playwright a11y test `@release` plus a domain tag (`@workspace` / `@api` / `@database` / `@components` / `@starter-projects`). Import `test`/`expect` from `../fixtures`.\n\n## 6. Re-scan and report\n\nRe-run the same scans with `RUN_A11Y_ASSERT=true`. Each finding → `fixed` | `baselined` | `open`.\n\nWhen done, state:\n- Changed frontend files considered\n- Surfaces / states scanned\n- Fixes applied (files + what)\n- Commands run — whether **both** axe and IBM ran and both reported zero\n- Specs / baselines added or updated\n- States skipped and why\n- Remaining risk or accepted limitation\n","CLAUDE.md":"# Claude Agent Guidelines & System Prompt\n\nPath: `CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/CLAUDE.md)","DESIGN.md":"# UI Design System & Style Tokens\n\nPath: `DESIGN.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/DESIGN.md)",".cursor/rules/docs_development.mdc":"# Cursor Rule: docs_development\n\nPath: `.cursor/rules/docs_development.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.cursor/rules/docs_development.mdc)","docs/agents/ANTI-PATTERNS.md":"# Subagent: anti-patterns\n\nPath: `docs/agents/ANTI-PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/ANTI-PATTERNS.md)","docs/agents/ARCHITECTURE.md":"# Subagent: architecture\n\nPath: `docs/agents/ARCHITECTURE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/ARCHITECTURE.md)","docs/agents/COMPONENTS.md":"# Subagent: components\n\nPath: `docs/agents/COMPONENTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/COMPONENTS.md)","docs/agents/CONTRACTS.md":"# Subagent: contracts\n\nPath: `docs/agents/CONTRACTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/CONTRACTS.md)","docs/agents/PHILOSOPHY.md":"# Subagent: philosophy\n\nPath: `docs/agents/PHILOSOPHY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/PHILOSOPHY.md)","docs/agents/TESTING.md":"# Subagent: testing\n\nPath: `docs/agents/TESTING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/TESTING.md)",".agents/skills/backend-code-review/references/architecture-rule.md":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/architecture-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/architecture-rule.md)",".agents/skills/backend-code-review/references/db-schema-rule.md":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/db-schema-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/db-schema-rule.md)",".agents/skills/backend-code-review/references/repositories-rule.md":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/repositories-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/repositories-rule.md)",".agents/skills/backend-code-review/references/sqlalchemy-rule.md":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/sqlalchemy-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/sqlalchemy-rule.md)",".agents/skills/component-refactoring/references/complexity-patterns.md":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/complexity-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/complexity-patterns.md)",".agents/skills/component-refactoring/references/component-splitting.md":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/component-splitting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/component-splitting.md)",".agents/skills/component-refactoring/references/hook-extraction.md":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/hook-extraction.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/hook-extraction.md)",".agents/skills/e2e-testing/references/fixtures.md":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/fixtures.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/fixtures.md)",".agents/skills/e2e-testing/references/helpers.md":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/helpers.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/helpers.md)",".agents/skills/e2e-testing/references/selectors.md":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/selectors.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/selectors.md)",".agents/skills/frontend-code-review/references/business-logic.md":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/business-logic.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/business-logic.md)",".agents/skills/frontend-code-review/references/code-quality.md":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/code-quality.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/code-quality.md)",".agents/skills/frontend-code-review/references/performance.md":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/performance.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/performance.md)",".agents/skills/frontend-query-mutation/references/query-patterns.md":"# References Skill\n\nPath: `.agents/skills/frontend-query-mutation/references/query-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/query-patterns.md)",".agents/skills/frontend-query-mutation/references/runtime-rules.md":"# References Skill\n\nPath: `.agents/skills/frontend-query-mutation/references/runtime-rules.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/runtime-rules.md)",".agents/skills/frontend-testing/references/async-testing.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/async-testing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/async-testing.md)",".agents/skills/frontend-testing/references/checklist.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/checklist.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/checklist.md)",".agents/skills/frontend-testing/references/common-patterns.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/common-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/common-patterns.md)",".agents/skills/frontend-testing/references/domain-components.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/domain-components.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/domain-components.md)",".agents/skills/frontend-testing/references/mocking.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/mocking.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/mocking.md)",".agents/skills/frontend-testing/references/workflow.md":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/workflow.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/workflow.md)",".agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md":"# References Skill\n\nPath: `.agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md)",".agents/skills/ibm-a11y-level1-audit/references/report-template.md":"# References Skill\n\nPath: `.agents/skills/ibm-a11y-level1-audit/references/report-template.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/references/report-template.md)"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\nThis file provides guidance to AI coding agents when working with code in this repository.\n\n## Project Overview\n\nLangflow is a visual workflow builder for AI-powered agents. It has a Python/FastAPI backend, React/TypeScript frontend, and a lightweight executor CLI (lfx).\n\n## Prerequisites\n\n- **Python:** 3.10-3.14\n- **uv:** >=0.4 (Python package manager)\n- **Node.js:** >=20.19.0 (v22.12 LTS recommended)\n- **npm:** v10.9+\n- **make:** For build coordination\n\n## Common Commands\n\n### Development Setup\n```bash\nmake init              # Install all dependencies + pre-commit hooks\nmake run_cli           # Build and run Langflow (http://localhost:7860)\nmake run_clic          # Clean build and run (use when frontend issues occur)\n```\n\n### Development Mode (Hot Reload)\n```bash\nmake backend           # FastAPI on port 7860 (terminal 1)\nmake frontend          # Vite dev server on port 3000 (terminal 2)\n```\n\nFor component development, enable dynamic loading:\n```bash\nLFX_DEV=1 make backend                    # Load all components dynamically\nLFX_DEV=mistral,openai make backend       # Load only specific modules\n```\n\n### Code Quality\n```bash\nmake format_backend    # Format Python (ruff) - run FIRST before lint\nmake format_frontend   # Format TypeScript (biome)\nmake format            # Both\nmake lint              # mypy type checking\n```\n\n### Testing\n```bash\nmake unit_tests                    # Backend unit tests (pytest, parallel)\nmake unit_tests async=false        # Sequential tests\nuv run pytest path/to/test.py      # Single test file\nuv run pytest path/to/test.py::test_name  # Single test\n\nmake test_frontend                 # Jest unit tests\nmake tests_frontend                # Playwright e2e tests\n```\n\n### Database Migrations\n```bash\nmake alembic-revision message=\"Description\"  # Create migration\nmake alembic-upgrade                         # Apply migrations\nmake alembic-downgrade                       # Rollback one version\n```\n\n## Architecture\n\n### Monorepo Structure\n```\nsrc/\n├── backend/\n│   ├── base/langflow/     # Core backend package (langflow-base)\n│   │   ├── api/           # FastAPI routes (v1/, v2/)\n│   │   ├── components/    # Built-in Langflow components\n│   │   ├── services/      # Service layer (auth, database, cache, etc.)\n│   │   ├── graph/         # Flow graph execution engine\n│   │   └── custom/        # Custom component framework\n│   └── tests/             # Backend tests\n├── frontend/              # React/TypeScript UI\n│   └── src/\n│       ├── components/    # UI components\n│       ├── stores/        # Zustand state management\n│       └── icons/         # Component icons\n├── langflow-core/         # Usable provider-free Langflow distribution\n├── bundles/               # Curated provider integrations\n└── lfx/                   # Lightweight executor and shared primitives\n```\n\n### Key Packages\n- **langflow**: Full end-user package; depends on `langflow-core` and curated provider bundles\n- **langflow-core**: Service-complete, provider-bundle-free distribution; owns the `langflow` CLI\n- **langflow-base**: Modular application platform (API, services, graph engine); extras add service integrations\n- **lfx**: Shared execution primitives and standalone CLI (`lfx serve`, `lfx run`)\n\nThe public dependency direction is `langflow → langflow-core → langflow-base → lfx`.\nProvider packages under `src/bundles/` are added only by the full `langflow` distribution.\n\n### Service Layer\nBackend services in `src/backend/base/langflow/services/`:\n- `auth/` - Authentication\n- `authorization/` - Authorization (RBAC) plugin layer — see below\n- `database/` - SQLAlchemy models and migrations\n- `cache/` - Caching layer\n- `storage/` - File storage\n- `tracing/` - Observability integrations\n\n### Authorization (RBAC)\n\nAuthorization is a pluggable layer separate from authentication:\n\n- **OSS** ships the interface (`BaseAuthorizationService` in `lfx`) + a pass-through implementation (`LangflowAuthorizationService`) + the `authz_*` and `casbin_rule` DB schema + route guards.\n- Implementations register via the `lfx.services` entry point `authorization_service` in `lfx.toml` (same pattern as the SSO `auth_service`). A registered plugin reads the `authz_*` admin tables and writes compiled rules to `casbin_rule`.\n\nDefault is **off**: `LANGFLOW_AUTHZ_ENABLED=false`. When enabled with only the OSS stub registered, every check returns allow — the stub is a no-op so routes stay wired and audit rows still flow. Real allow/deny requires a registered authorization plugin.\n\nRoute guards live in `langflow.services.authorization.guards` (the legacy `langflow.services.authorization.utils` path re-exports them for backward compatibility):\n- `ensure_flow_permission(user, FlowAction.*, flow_id=..., flow_user_id=..., workspace_id=..., folder_id=...)` — single-flow CRUD + execute\n- `ensure_deployment_permission(user, DeploymentAction.*, deployment_id=..., deployment_user_id=..., workspace_id=..., project_id=...)`\n- `ensure_project_permission(user, ProjectAction.*, project_id=..., project_user_id=..., workspace_id=...)`\n- `ensure_knowledge_base_permission(user, KnowledgeBaseAction.*, kb_name=..., kb_user_id=...)`\n- `ensure_variable_permission(user, VariableAction.*, variable_id=..., variable_user_id=...)`\n- `ensure_file_permission(user, FileAction.*, file_id=..., file_user_id=...)`\n- `ensure_share_permission(user, ShareAction.*, share_id=..., share_user_id=...)`\n- `filter_visible_resources(user, resource_type=..., candidates=..., act=...)` — list-endpoint filter; safe no-op in OSS\n\nThe enforcement request shape is `(subject, domain, object, action)`:\n- subject = `user:{uuid}`\n- domain = `project:{uuid}` → `workspace:{uuid}` → `*` (resolved by `_resolve_flow_domain`; the more specific domain wins so project-scoped grants match directly while workspace-scoped grants still flow down via plugin-side role inheritance)\n- object = `flow:{uuid}` / `deployment:{uuid}` / `project:{uuid}` / `flow:*` / etc.\n- action = `read` / `write` / `create` / `delete` / `execute` / `deploy`\n\n**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher, variable PATCH/DELETE in `variable.py`) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.\n\n**Share CRUD API (Phase 3):** `/api/v1/authz/shares` provides POST / GET / PATCH / DELETE on `authz_share` rows. The handler enforces an OSS floor (resource owner or superuser may administer shares for that resource) so the OSS pass-through cannot let a non-owner mint share rows. Each write fires `BaseAuthorizationService.invalidate_user` / `invalidate_all` so a registered enforcer can drop cached policy. Audit rows are written via `audit_decision` with `share:create` / `share:update` / `share:delete` actions.\n\n**Audit query API (Phase 4):** `GET /api/v1/authz/audit` (superuser-only) exposes a paginated, filterable view of `authz_audit_log`. Supports `user_id`, `resource_type`, `resource_id`, `action`, `result`, `since`, `until` filters; page size capped at 200.\n\n**Default role catalog (Phase 4):** the consolidated foundations migration `7c8d9e0f1a2b_authz_foundations` seeds the three built-in `is_system=True` roles (viewer / developer / admin) with `\"{resource}:{action}\"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.\n\n## Component Development\n\nComponents live in `src/backend/base/langflow/components/`. To add a new component:\n\n1. Create component class inheriting from `Component`\n2. Define `display_name`, `description`, `icon`, `inputs`, `outputs`\n3. Add to `__init__.py` (alphabetical order)\n4. Run with `LFX_DEV=1 make backend` for hot reload\n\n**IMPORTANT:** Changing a component's class name is a breaking change and should never be done. The class name serves as an identifier used to match components in saved flows and to flag them for updates in the UI. Renaming it will break existing flows that use that component.\n\n### Component Structure\n```python\nfrom langflow.custom import Component\nfrom langflow.io import MessageTextInput, Output\n\nclass MyComponent(Component):\n    display_name = \"My Component\"\n    description = \"What it does\"\n    icon = \"component-icon\"  # Lucide icon name or custom\n\n    inputs = [\n        MessageTextInput(name=\"input_value\", display_name=\"Input\"),\n    ]\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"process\"),\n    ]\n\n    def process(self) -> Message:\n        # Component logic\n        return Message(text=self.input_value)\n```\n\n### Component Testing\nTests go in `src/backend/tests/unit/components/`. Use base classes:\n- `ComponentTestBaseWithClient` - Components needing API access\n- `ComponentTestBaseWithoutClient` - Pure logic components\n\nRequired fixtures: `component_class`, `default_kwargs`, `file_names_mapping`\n\n## Frontend Development\n\n- **React 19** + TypeScript + Vite\n- **Zustand** for state management\n- **@xyflow/react** for graph visualization\n- **Tailwind CSS** for styling\n\n### Custom Icons\n1. Create SVG component in `src/frontend/src/icons/YourIcon/`\n2. Export with `forwardRef` and `isDark` prop support\n3. Add to `lazyIconImports.ts`\n4. Set `icon = \"YourIcon\"` in Python component\n\n## Testing Notes\n\n- `@pytest.mark.api_key_required` - Tests requiring external API keys\n- `@pytest.mark.no_blockbuster` - Skip blockbuster plugin\n- Database tests may fail in batch but pass individually\n- Pre-commit hooks require `uv run git commit`\n- Always use `uv run` when running Python commands\n- When running tests inside a sub-package (e.g. `langflow-base`, `lfx`), sync that package's dev group first: `uv sync --group dev --package langflow-base`. The default `uv sync` only resolves the top-level workspace and may leave dev-only test deps (e.g. `fakeredis`) uninstalled.\n\n### Graph Testing Pattern\n\nProper Graph tests follow this pattern:\n1. Build graph with connected components\n2. Connect them via `.set()` calls\n3. Call `async_start` and iterate over the results\n4. Validate the results\n\n### Testing Best Practices\n\n- Avoid mocking in tests when possible\n- Prefer real integrations for more reliable tests\n\n## Version Management\n```bash\nmake patch v=1.5.0  # Update version across all packages\n```\n\nThis updates: `pyproject.toml`, `src/backend/base/pyproject.toml`, `src/frontend/package.json`\n\n## Pre-commit Workflow\n\nPre-commit hooks run ruff and biome automatically on `git commit`, so manual\nformatting is not required. To avoid an extra commit cycle when you have many\nchanges:\n\n1. Run `make format_backend` once before staging - fixes most ruff issues up front.\n2. Run `uv run git commit` (the `uv run` ensures pre-commit finds the right Python).\n3. If you touched backend code, run `make unit_tests` locally for faster feedback than CI.\n\n## Pull Request Guidelines\n\n- Follow [semantic commit conventions](https://www.conventionalcommits.org/)\n- Reference any issues fixed (e.g., `Fixes #1234`)\n- Ensure all tests pass before submitting\n\n## Documentation\n\nDocumentation uses Docusaurus and lives in `docs/`:\n```bash\ncd docs\nyarn install\nyarn start        # Dev server on port 3000 (prompts for 3001 if 3000 is in use)\n```\n","isInternal":false,"tokens":2792,"sizeBytes":11859},{"name":"DESIGN.md","path":"DESIGN.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/DESIGN.md","title":"UI Design System & Style Tokens","category":"project-spec","format":"markdown","content":"# UI Design System & Style Tokens\n\nPath: `DESIGN.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/DESIGN.md)","tokens":300,"sizeBytes":153},{"name":"architecture-rule.md","path":".agents/skills/backend-code-review/references/architecture-rule.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/architecture-rule.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/architecture-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/architecture-rule.md)","tokens":300,"sizeBytes":252},{"name":"db-schema-rule.md","path":".agents/skills/backend-code-review/references/db-schema-rule.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/db-schema-rule.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/db-schema-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/db-schema-rule.md)","tokens":300,"sizeBytes":246},{"name":"repositories-rule.md","path":".agents/skills/backend-code-review/references/repositories-rule.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/repositories-rule.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/repositories-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/repositories-rule.md)","tokens":300,"sizeBytes":252},{"name":"sqlalchemy-rule.md","path":".agents/skills/backend-code-review/references/sqlalchemy-rule.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/sqlalchemy-rule.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/backend-code-review/references/sqlalchemy-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/sqlalchemy-rule.md)","tokens":300,"sizeBytes":248},{"name":"SKILL.md","path":".agents/skills/backend-code-review/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/SKILL.md","title":"backend-code-review","category":"anthropic-skill","format":"markdown","content":"---\nname: backend-code-review\ndescription: Review backend code for quality, security, maintainability, and best practices based on established checklist rules. Use when the user requests a review, analysis, or improvement of backend files (e.g., `.py`) under the `src/backend/` directory. Do NOT use for frontend files (e.g., `.tsx`, `.ts`, `.js`). Supports pending-change review, code snippets review, and file-focused review.\n---\n\n# Backend Code Review\n\n## When to use this skill\n\nUse this skill whenever the user asks to **review, analyze, or improve** backend code (e.g., `.py`) under the `src/backend/` directory. Supports the following review modes:\n\n- **Pending-change review**: when the user asks to review current changes (inspect staged/working-tree files slated for commit to get the changes).\n- **Code snippets review**: when the user pastes code snippets (e.g., a function/class/module excerpt) into the chat and asks for a review.\n- **File-focused review**: when the user points to specific files and asks for a review of those files (one file or a small, explicit set of files, e.g., `src/backend/base/langflow/api/v1/flows.py`).\n\nDo NOT use this skill when:\n\n- The request is about frontend code or UI (e.g., `.tsx`, `.ts`, `.js`, `src/frontend/`).\n- The user is not asking for a review/analysis/improvement of backend code.\n- The scope is not under `src/backend/` (unless the user explicitly asks to review backend-related changes outside `src/backend/`).\n\n## How to use this skill\n\nFollow these steps when using this skill:\n\n1. **Identify the review mode** (pending-change vs snippet vs file-focused) based on the user's input. Keep the scope tight: review only what the user provided or explicitly referenced.\n2. Follow the rules defined in **Checklist** to perform the review. If no Checklist rule matches, apply **General Review Rules** as a fallback to perform the best-effort review.\n3. Compose the final output strictly following the **Required Output Format**.\n\nNotes when using this skill:\n- Always include actionable fixes or suggestions (including possible code snippets).\n- Use best-effort `File:Line` references when a file path and line numbers are available; otherwise, use the most specific identifier you can.\n\n## Checklist\n\n- db schema design: if the review scope includes code/files under `src/backend/base/langflow/services/database/models/` or Alembic migrations under `src/backend/base/langflow/alembic/versions/`, follow [references/db-schema-rule.md](references/db-schema-rule.md) to perform the review\n- architecture: if the review scope involves route/service/model layering, dependency direction, or moving responsibilities across modules, follow [references/architecture-rule.md](references/architecture-rule.md) to perform the review\n- service abstraction: if the review scope contains table/model operations (e.g., `select(...)`, `session.execute(...)`, joins, CRUD) and is not already inside a service under `src/backend/base/langflow/services/`, follow [references/repositories-rule.md](references/repositories-rule.md) to perform the review\n- sqlalchemy patterns: if the review scope involves SQLAlchemy/SQLModel session/query usage, db transaction/crud usage, `session_scope()` usage, or raw SQL usage, follow [references/sqlalchemy-rule.md](references/sqlalchemy-rule.md) to perform the review\n\n## General Review Rules\n\n### 1. Security Review\n\nCheck for:\n- SQL injection vulnerabilities (especially raw `text()` queries with string interpolation). Consequence: attacker can read/modify/delete any data in the database.\n- Server-Side Request Forgery (SSRF) in component HTTP calls. Consequence: attacker uses the server to scan internal networks or access cloud metadata endpoints.\n- Command injection (especially in subprocess or shell-executing components). Consequence: attacker gains shell access to the server.\n- Insecure deserialization (pickle, yaml.load without SafeLoader). Consequence: arbitrary code execution on the server.\n- Hardcoded secrets/credentials. Consequence: secrets leak via git history and are impossible to fully revoke.\n- Improper authentication/authorization (missing `CurrentActiveUser` dependency). Consequence: unauthenticated users can access protected endpoints.\n- Insecure direct object references (missing `user_id` scoping on queries). Consequence: user A can read/modify user B's flows, variables, API keys.\n- Path traversal in file storage operations. Consequence: attacker reads arbitrary server files (e.g., `/etc/passwd`, `.env`).\n\n### 2. Performance Review\n\nCheck for:\n- N+1 queries (especially in loops calling `session.execute()`). Consequence: 100 flows = 101 DB queries instead of 2; page load goes from 50ms to 5s.\n- Missing database indexes on frequently queried columns. Consequence: full table scans on large datasets; queries degrade from O(log n) to O(n).\n- Memory leaks (unbounded caches, retained references in long-lived services). Consequence: server OOM after hours of operation; pods restart in production.\n- Blocking operations in async code (`time.sleep()`, synchronous I/O, CPU-bound work without `run_in_executor`). Consequence: entire event loop stalls; all concurrent requests hang until the blocking call completes.\n- Missing caching opportunities for expensive computations. Consequence: repeated computation of the same result on every request.\n- Large result sets loaded entirely into memory without pagination. Consequence: memory spike + slow response when user has 10K+ flows.\n\n### 3. Code Quality Review\n\nCheck for:\n- Code forward compatibility with Python 3.10-3.13\n- Code duplication (DRY violations — extract when the *exact same business rule* is duplicated in 3+ places)\n- Functions doing too much (SRP violations — if you need \"and\" to describe it, split it)\n- Deep nesting / complex conditionals (prefer early returns and guard clauses)\n- Magic numbers/strings (extract to named constants or enums)\n- Poor naming: unclear abbreviations, misleading names, generic names (`data`, `result`, `obj`, `temp`). Functions should use verbs (`get`, `create`, `validate`). Booleans should use prefixes (`is_`, `has_`, `can_`, `should_`).\n- Missing error handling (bare `except`, swallowed exceptions, silent failures)\n- Incomplete type coverage (use strong typing, avoid `Any` where a concrete type is known)\n- Use Python 3.10+ union syntax (`X | Y` not `Union[X, Y]`, `X | None` not `Optional[X]`)\n- Use `TYPE_CHECKING` guard for imports only needed for type annotations (prevents circular imports)\n- Use `Annotated[Type, Depends(...)]` with project aliases (`CurrentActiveUser`, `DbSession`, `DbSessionReadOnly`) for FastAPI DI\n- Google-style docstrings (enforced by Ruff): `Args:`, `Returns:`, `Raises:` sections for public functions\n- Violations of SOLID principles\n- YAGNI violations (code that anticipates future needs without a present requirement)\n- Line length exceeding 120 characters (project Ruff config)\n- Comments that explain WHAT instead of WHY (comments should only explain reasoning, not restate code)\n- Commented-out code (use version control instead)\n- Boolean parameters that switch function behavior (split into two named functions instead)\n- Mutable shared state where immutable alternatives exist (prefer returning new objects over mutation)\n\n### 4. File Structure Review\n\nCheck for:\n- Production files exceeding ~500 lines of code (excluding imports, types, and docstrings). Files above 600 lines are a red flag and should be split by responsibility. Why: Files above 500 lines have statistically higher defect rates and take longer to review. They signal multiple responsibilities (SRP violation). In Langflow, services like `DatabaseService` that grow beyond this limit should have their CRUD operations extracted to dedicated modules.\n- Test files exceeding ~1000 lines. Split by logical grouping if exceeded.\n- No more than 5 functions with different responsibilities in a single file (per AGENTS-example.md).\n- Each file has a single reason to exist and a single reason to change (SRP).\n- No generic file names: `utils.py`, `helpers.py`, `misc.py`, `common.py` as standalone files. Why: A file named `utils.py` becomes a dumping ground for unrelated functions. Within months it has 50+ functions covering formatting, validation, parsing, and HTTP calls — violating SRP. Each function group should be in a file named after its responsibility (`formatting.py`, `validation.py`).\n\n### 5. Testing Review\n\nCheck for:\n- Missing test coverage for new code paths\n- Tests that don't test behavior (testing implementation details)\n- Flaky test patterns (time-dependent, order-dependent, external-service-dependent)\n- Proper use of `pytest.mark.asyncio` for async tests\n- Excessive mocking (prefer real integrations per project conventions)\n- Coverage target: 80% (minimum acceptable: 75%)\n- Test anti-patterns: The Liar (passes but doesn't verify claimed behavior), The Mirror (asserts exactly what code does), The Giant (50+ lines setup), The Mockery (tests only mock setup), The Inspector (coupled to implementation), The Chain Gang (depends on execution order), The Flaky (inconsistent results)\n\n**Happy path tests are the foundation but are NOT enough.** Tests MUST also challenge the code to find real defects:\n\n- **Unexpected inputs**: `None`, `\"\"`, `[]`, `{}`, `0`, `-1`, `UUID(\"00000000-0000-0000-0000-000000000000\")`\n- **Boundary values**: max length strings, exactly at the limit, one past the limit, zero items, max items\n- **Malformed data**: missing required fields, extra unexpected fields, wrong types, invalid formats\n- **Error states**: what happens when the database is down? When an external API returns 500? When the user doesn't exist?\n- **What should NOT happen**: verify that user A CANNOT access user B's flows. Verify that a deleted flow returns 404. Verify that invalid `endpoint_name` is rejected with 422.\n- **Error messages and types**: not just that it fails, but that it fails with the RIGHT exception and the RIGHT message\n- **Concurrency**: what happens when two requests try to update the same flow simultaneously?\n\n**Write tests based on REQUIREMENTS/SPEC, not on what the source code currently does.** This is how you catch bugs where the code diverges from expected behavior.\n\n**When a test fails:** first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.\n\n### 6. Observability Review\n\nCheck for:\n- Use the async logger from `lfx.log.logger` with `a`-prefixed methods (`adebug`, `ainfo`, `awarning`, `aerror`, `aexception`). Never use `print()` or stdlib `logging`.\n- Log at key decision points and boundaries, not inside tight loops\n- Include: operation name, relevant IDs, outcome (success/failure), duration if relevant\n- Correct log levels: ERROR (broken, needs attention), WARN (degraded but recoverable), INFO (significant events), DEBUG (diagnostic, off in prod)\n- **ZERO PII TOLERANCE**: Never log email addresses, user names, phone numbers, tokens, passwords. Only approved identifiers: `user_id`, `flow_id`, `session_id`\n- No `print()` statements — these go to production logs\n- Use `{e!s}` for string representation of exceptions in log messages\n\n### 7. Pre-Commit Verification\n\nFor pending-change reviews, verify the author has run:\n- `make format_backend` (Ruff formatter) — inconsistent formatting creates noisy diffs that hide real changes in code review. Format first, review second.\n- `make lint` (MyPy type checking) — type errors caught at lint time are 10x cheaper to fix than runtime crashes in production. Langflow services use duck typing via `Service` base class; MyPy catches mismatches early.\n- `make unit_tests` (pytest) — a failing test means the change breaks existing behavior. Never merge with failing tests; investigate whether the code or the test is wrong.\n\n## Required Output Format\n\nWhen this skill is invoked, the response must exactly follow one of the two templates:\n\n### Template A (any findings)\n\n```markdown\n# Code Review Summary\n\nFound <X> critical issues need to be fixed:\n\n## 🔴 Critical (Must Fix)\n\n### 1. <brief description of the issue>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<detailed explanation and references of the issue>\n\n#### Suggested Fix\n\n1. <brief description of suggested fix>\n2. <code example> (optional, omit if not applicable)\n\n---\n... (repeat for each critical issue) ...\n\nFound <Y> suggestions for improvement:\n\n## 🟡 Suggestions (Should Consider)\n\n### 1. <brief description of the suggestion>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<detailed explanation and references of the suggestion>\n\n#### Suggested Fix\n\n1. <brief description of suggested fix>\n2. <code example> (optional, omit if not applicable)\n\n---\n... (repeat for each suggestion) ...\n\nFound <Z> optional nits:\n\n## 🟢 Nits (Optional)\n### 1. <brief description of the nit>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<explanation and references of the optional nit>\n\n#### Suggested Fix\n\n- <minor suggestions>\n\n---\n... (repeat for each nits) ...\n\n## ✅ What's Good\n\n- <Positive feedback on good patterns>\n```\n\n- If there are no critical issues or suggestions or optional nits or good points, just omit that section.\n- If the issue number is more than 10, summarize as \"Found 10+ critical issues/suggestions/optional nits\" and only output the first 10 items.\n- Don't compress the blank lines between sections; keep them as-is for readability.\n- If there is any issue that requires code changes, append a brief follow-up question to ask whether the user wants to apply the fix(es) after the structured output. For example: \"Would you like me to use the Suggested fix(es) to address these issues?\"\n\n### Template B (no issues)\n\n```markdown\n## Code Review Summary\n✅ No issues found.\n```\n","frontmatter":{"name":"backend-code-review","description":"Review backend code for quality, security, maintainability, and best practices based on established checklist rules. Use when the user requests a review, analysis, or improvement of backend files (e.g., `.py`) under the `src/backend/` directory. Do NOT use for frontend files (e.g., `.tsx`, `.ts`, `.js`). Supports pending-change review, code snippets review, and file-focused review."},"isInternal":false,"tokens":3088,"sizeBytes":13919},{"name":"complexity-patterns.md","path":".agents/skills/component-refactoring/references/complexity-patterns.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/complexity-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/complexity-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/complexity-patterns.md)","tokens":300,"sizeBytes":260},{"name":"component-splitting.md","path":".agents/skills/component-refactoring/references/component-splitting.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/component-splitting.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/component-splitting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/component-splitting.md)","tokens":300,"sizeBytes":260},{"name":"hook-extraction.md","path":".agents/skills/component-refactoring/references/hook-extraction.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/hook-extraction.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/component-refactoring/references/hook-extraction.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/hook-extraction.md)","tokens":300,"sizeBytes":252},{"name":"SKILL.md","path":".agents/skills/component-refactoring/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/SKILL.md","title":"component-refactoring","category":"anthropic-skill","format":"markdown","content":"---\nname: component-refactoring\ndescription: Refactor high-complexity React components in Langflow frontend. Use when manual complexity assessment shows complexity > 50 or lineCount > 300, when the user asks for code splitting, hook extraction, or complexity reduction; avoid for simple/well-structured components, third-party wrappers, or when the user explicitly wants testing without refactoring.\n---\n\n# Langflow Component Refactoring Skill\n\nRefactor high-complexity React components in the Langflow frontend codebase with the patterns and workflow below.\n\n> **Complexity Threshold**: Components with complexity > 50 (measured manually by counting conditionals, nesting levels, and lines) should be refactored before testing.\n\n## Quick Reference\n\n### Commands (run from `src/frontend/`)\n\n```bash\ncd src/frontend\n\n# Lint with Biome\nnpm run lint\n\n# Type checking\nnpm run type-check\n\n# Run tests\nnpm test\n```\n\n### Manual Complexity Assessment\n\nSince Langflow does not have automated complexity analysis tools, assess components manually:\n\n1. **Count conditionals**: Each `if/else`, `switch/case`, ternary, `&&`/`||` chain adds +1.\n2. **Count nesting levels**: Each level of nesting within conditionals or loops adds +1.\n3. **Count total lines**: Target < 300 lines per component file.\n4. **Count state hooks**: More than 5 `useState` calls suggests hook extraction.\n5. **Count effects**: More than 3 `useEffect` calls suggests effect consolidation.\n\n### Complexity Score Interpretation\n\n| Score | Level | Action |\n|-------|-------|--------|\n| 0-25 | Simple | Ready for testing |\n| 26-50 | Medium | Consider minor refactoring |\n| 51-75 | Complex | **Refactor before testing** |\n| 76-100 | Very Complex | **Must refactor** |\n\n## Core Refactoring Patterns\n\n### Pattern 1: Extract Custom Hooks\n\n**When**: Component has complex state management, multiple `useState`/`useEffect`, or business logic mixed with UI.\n\n**Langflow Convention**: Place hooks in a `hooks/` subdirectory or alongside the component as `use-<feature>.ts`. Langflow uses kebab-case filenames with `use-` prefix.\n\n```typescript\n// Before: Complex state logic in component\nconst FlowPage: FC = () => {\n  const [nodes, setNodes] = useState<Node[]>([])\n  const [edges, setEdges] = useState<Edge[]>([])\n  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)\n\n  // 50+ lines of state management logic...\n\n  return <div>...</div>\n}\n\n// After: Extract to custom hook\n// hooks/use-flow-state.ts\nexport const useFlowState = (flowId: string) => {\n  const [nodes, setNodes] = useState<Node[]>([])\n  const [edges, setEdges] = useState<Edge[]>([])\n  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)\n\n  // Related state management logic here\n\n  return { nodes, setNodes, edges, setEdges, buildStatus, setBuildStatus }\n}\n\n// Component becomes cleaner\nconst FlowPage: FC = () => {\n  const { nodes, setNodes, edges, setEdges } = useFlowState(flowId)\n  return <div>...</div>\n}\n```\n\n**Langflow Examples**:\n- `src/frontend/src/hooks/use-add-component.ts`\n- `src/frontend/src/hooks/use-unsaved-changes.ts`\n- `src/frontend/src/hooks/use-refresh-model-inputs.ts`\n\n### Pattern 2: Extract Sub-Components\n\n**When**: Single component has multiple UI sections, conditional rendering blocks, or repeated patterns.\n\n**Langflow Convention**: Place sub-components in subdirectories or as separate files in the same directory. UI primitives go in `components/ui/`, domain components in `components/core/`, reusable components in `components/common/`.\n\n```typescript\n// Before: Monolithic JSX with multiple sections\nconst GenericNode = () => {\n  return (\n    <div>\n      {/* 100 lines of header UI */}\n      {/* 100 lines of parameter fields */}\n      {/* 100 lines of output handles */}\n    </div>\n  )\n}\n\n// After: Split into focused components\n// CustomNodes/GenericNode/\n//   generic-node.tsx       (orchestration only — kebab-case, descriptive name)\n//   components/\n//     node-header.tsx\n//     node-parameters.tsx\n//     node-outputs.tsx\n\nconst GenericNode = () => {\n  return (\n    <div>\n      <NodeHeader nodeData={data} />\n      <NodeParameters fields={fields} />\n      <NodeOutputs outputs={outputs} />\n    </div>\n  )\n}\n```\n\n**Langflow Examples**:\n- `src/frontend/src/CustomNodes/GenericNode/components/`\n- `src/frontend/src/components/core/`\n- `src/frontend/src/components/ui/`\n\n### Pattern 3: Simplify Conditional Logic\n\n**When**: Deep nesting (> 3 levels), complex ternaries, or multiple `if/else` chains.\n\n```typescript\n// Before: Deeply nested conditionals\nconst getFieldComponent = (field: InputFieldType) => {\n  if (field.type === \"str\") {\n    if (field.multiline) {\n      return <TextAreaComponent />\n    } else if (field.password) {\n      return <PasswordInput />\n    } else if (field.options?.length) {\n      return <Dropdown options={field.options} />\n    } else {\n      return <InputComponent />\n    }\n  } else if (field.type === \"int\") {\n    return <IntComponent />\n  } else if (field.type === \"float\") {\n    return <FloatComponent />\n  }\n  return null\n}\n\n// After: Use lookup tables + early returns\nconst FIELD_COMPONENT_MAP: Record<string, FC<FieldProps>> = {\n  int: IntComponent,\n  float: FloatComponent,\n  bool: ToggleComponent,\n  code: CodeAreaComponent,\n}\n\nconst STR_VARIANT_MAP: Record<string, FC<FieldProps>> = {\n  multiline: TextAreaComponent,\n  password: PasswordInput,\n  options: Dropdown,\n}\n\nconst getFieldComponent = (field: InputFieldType) => {\n  if (field.type !== \"str\") {\n    const Component = FIELD_COMPONENT_MAP[field.type]\n    return Component ? <Component {...field} /> : null\n  }\n\n  const variant = field.multiline ? \"multiline\"\n    : field.password ? \"password\"\n    : field.options?.length ? \"options\"\n    : \"default\"\n\n  const Component = STR_VARIANT_MAP[variant] ?? InputComponent\n  return <Component {...field} />\n}\n```\n\n### Pattern 4: Extract API/Data Logic\n\n**When**: Component directly handles API calls, data transformation, or complex async operations.\n\n**Langflow Convention**:\n- This skill is for component decomposition, not query/mutation design.\n- When refactoring data fetching, use `frontend-query-mutation` for query patterns, `UseRequestProcessor`, cache invalidation, and mutation error handling.\n- Do not create thin passthrough `useQuery` wrappers during refactoring; only extract a custom hook when it truly orchestrates multiple queries/mutations or shared derived state.\n- API hooks live in `controllers/API/queries/{domain}/`.\n\n**Langflow Examples**:\n- `src/frontend/src/controllers/API/queries/flows/use-post-add-flow.ts`\n- `src/frontend/src/controllers/API/queries/variables/use-get-global-variables.ts`\n- `src/frontend/src/controllers/API/queries/folders/use-get-folders.ts`\n\n### Pattern 5: Extract Modal/Dialog Management\n\n**When**: Component manages multiple modals with complex open/close states.\n\n**Langflow Convention**: Modals should be extracted with their state management.\n\n```typescript\n// Before: Multiple modal states in component\nconst FlowToolbar = () => {\n  const [showExportModal, setShowExportModal] = useState(false)\n  const [showShareModal, setShowShareModal] = useState(false)\n  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)\n  const [showApiModal, setShowApiModal] = useState(false)\n  // 5+ more modal states...\n}\n\n// After: Extract to modal management hook\ntype ModalType = \"export\" | \"share\" | \"delete\" | \"api\" | null\n\nconst useFlowToolbarModals = () => {\n  const [activeModal, setActiveModal] = useState<ModalType>(null)\n\n  const openModal = useCallback((type: ModalType) => setActiveModal(type), [])\n  const closeModal = useCallback(() => setActiveModal(null), [])\n\n  return {\n    activeModal,\n    openModal,\n    closeModal,\n    isOpen: (type: ModalType) => activeModal === type,\n  }\n}\n```\n\n### Pattern 6: Extract Form Logic\n\n**When**: Complex form validation, submission handling, or field transformation.\n\n**Langflow Convention**: Extract form state and validation into hooks.\n\n```typescript\n// Extract form validation and submission\nconst useFlowSettingsForm = (initialValues: FlowSettings) => {\n  const [values, setValues] = useState(initialValues)\n  const [errors, setErrors] = useState<Record<string, string>>({})\n\n  const validate = useCallback(() => {\n    const newErrors: Record<string, string> = {}\n    if (!values.name?.trim()) newErrors.name = \"Name is required\"\n    if (values.endpoint_name && !/^[a-z0-9-]+$/.test(values.endpoint_name)) {\n      newErrors.endpoint_name = \"Must be lowercase alphanumeric with hyphens\"\n    }\n    setErrors(newErrors)\n    return Object.keys(newErrors).length === 0\n  }, [values])\n\n  const handleChange = useCallback((field: string, value: any) => {\n    setValues((prev) => ({ ...prev, [field]: value }))\n  }, [])\n\n  return { values, errors, validate, handleChange }\n}\n```\n\n## Langflow-Specific Refactoring Guidelines\n\n### 1. Zustand Store Selectors\n\n**When**: Component reads many values from a Zustand store, causing unnecessary re-renders.\n\n```typescript\n// Before: Selecting too many values\nconst Component = () => {\n  const flowStore = useFlowStore()\n  // Component re-renders on ANY store change\n}\n\n// After: Use individual selectors\nconst Component = () => {\n  const nodes = useFlowStore((state) => state.nodes)\n  const edges = useFlowStore((state) => state.edges)\n  // Component only re-renders when nodes or edges change\n}\n```\n\n**Langflow Reference**: All stores in `src/frontend/src/stores/` follow this selector pattern.\n\n### 2. Custom Node Components\n\n**When**: Refactoring flow node components (`CustomNodes/GenericNode/`).\n\n**Conventions**:\n- Keep node logic in custom hooks\n- Extract parameter rendering to separate components\n- Use the existing `components/` subdirectory for sub-components\n\n```\nCustomNodes/GenericNode/\n  generic-node.tsx             # Node registration and main render (kebab-case, NOT index.tsx)\n  components/\n    handle-render.tsx           # Handle rendering\n    node-description.tsx        # Node description display\n    node-input-field.tsx        # Input field rendering\n    node-name.tsx               # Node name display\n    node-output-field.tsx       # Output field rendering\n    node-status.tsx             # Build status display\n```\n\n### 3. Flow Canvas Components\n\n**When**: Refactoring components related to the flow editor canvas.\n\n**Conventions**:\n- `@xyflow/react` v12 is the canvas library\n- Keep canvas event handlers separate from UI rendering\n- Use `useFlowStore` for flow state management\n- Use `useFlowsManagerStore` for multi-flow management\n\n### 4. API Query Hook Components\n\n**When**: Refactoring components that consume API data.\n\n**Conventions**:\n- Use existing query hooks from `controllers/API/queries/`\n- Access `UseRequestProcessor` for new queries/mutations\n- Use query key arrays like `[\"useGetGlobalVariables\"]` for cache management\n- Cache invalidation belongs in mutation `onSettled` callbacks\n\n## Refactoring Workflow\n\n### Step 1: Assess Complexity\n\nManually count:\n- Total conditionals (if/else, switch, ternary, &&/||)\n- Maximum nesting depth\n- Total lines of code\n- Number of useState/useEffect hooks\n- Number of distinct UI sections\n\n### Step 2: Plan\n\nCreate a refactoring plan based on detected features:\n\n| Detected Feature | Refactoring Action |\n|------------------|-------------------|\n| 5+ `useState` hooks with related state | Extract custom hook |\n| API calls in component body | Extract to query hook |\n| 3+ event handlers with logic | Extract event handlers to hook |\n| 300+ lines | Split into sub-components |\n| Deep conditional nesting (>3) | Simplify conditional logic |\n| Multiple modal states | Extract modal management |\n\n### Step 3: Execute Incrementally\n\n1. **Extract one piece at a time**\n2. **Run lint, type-check, and tests after each extraction**\n3. **Verify functionality before next step**\n\n```\nFor each extraction:\n  1. Extract code\n  2. Run: npm run lint\n  3. Run: npm run type-check\n  4. Run: npm test\n  5. Test functionality manually\n  6. PASS? -> Next extraction\n     FAIL? -> Fix before continuing\n```\n\n### Step 4: Verify\n\nAfter refactoring, re-assess complexity manually:\n\n- Target complexity < 50\n- Target line count < 300\n- Target max nesting depth <= 3\n- Target max function length < 30 lines\n\n## Common Mistakes to Avoid\n\n### Over-Engineering\n\n```typescript\n// Too many tiny hooks\nconst useButtonText = () => useState(\"Click\")\nconst useButtonDisabled = () => useState(false)\nconst useButtonLoading = () => useState(false)\n\n// Cohesive hook with related state\nconst useButtonState = () => {\n  const [text, setText] = useState(\"Click\")\n  const [disabled, setDisabled] = useState(false)\n  const [loading, setLoading] = useState(false)\n  return { text, setText, disabled, setDisabled, loading, setLoading }\n}\n```\n\n### Breaking Existing Patterns\n\n- Follow existing directory structures in `components/ui/`, `components/core/`, `components/common/`\n- Maintain naming conventions (kebab-case files, PascalCase components)\n- Preserve export patterns for compatibility\n- Keep Zustand store selector patterns consistent\n\n### Premature Abstraction\n\n- Only extract when there is clear complexity benefit\n- Do not create abstractions for single-use code\n- Keep refactored code in the same domain area\n\n### Bypassing UseRequestProcessor\n\n- Do not call `useQuery` or `useMutation` directly for API calls\n- Always use the `UseRequestProcessor` pattern for consistency with retry and invalidation logic\n- See `frontend-query-mutation` skill for API hook patterns\n\n## References\n\n### Langflow Codebase Examples\n\n- **Hook extraction**: `src/frontend/src/hooks/`\n- **Component splitting**: `src/frontend/src/CustomNodes/GenericNode/components/`\n- **UI components**: `src/frontend/src/components/ui/`\n- **Core components**: `src/frontend/src/components/core/`\n- **Common components**: `src/frontend/src/components/common/`\n- **API query hooks**: `src/frontend/src/controllers/API/queries/`\n- **Zustand stores**: `src/frontend/src/stores/`\n\n### Related Skills\n\n- `frontend-query-mutation` - For API query and mutation patterns\n- `frontend-testing` - For testing refactored components\n","frontmatter":{"name":"component-refactoring","description":"Refactor high-complexity React components in Langflow frontend. Use when manual complexity assessment shows complexity > 50 or lineCount > 300, when the user asks for code splitting, hook extraction, or complexity reduction; avoid for simple/well-structured components, third-party wrappers, or when the user explicitly wants testing without refactoring."},"isInternal":false,"tokens":3319,"sizeBytes":14117},{"name":"fixtures.md","path":".agents/skills/e2e-testing/references/fixtures.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/fixtures.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/fixtures.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/fixtures.md)","tokens":300,"sizeBytes":218},{"name":"helpers.md","path":".agents/skills/e2e-testing/references/helpers.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/helpers.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/helpers.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/helpers.md)","tokens":300,"sizeBytes":216},{"name":"selectors.md","path":".agents/skills/e2e-testing/references/selectors.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/selectors.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/e2e-testing/references/selectors.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/selectors.md)","tokens":300,"sizeBytes":220},{"name":"SKILL.md","path":".agents/skills/e2e-testing/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/SKILL.md","title":"e2e-testing","category":"anthropic-skill","format":"markdown","content":"---\nname: e2e-testing\ndescription: Write and review Playwright E2E tests for Langflow. Trigger when the user asks to write, fix, or review E2E tests, spec files, Playwright tests, or integration tests that exercise the full UI. Also trigger when modifying data-testid attributes, test helpers in tests/utils/, or fixture configuration.\n---\n\n# Langflow E2E Testing (Playwright)\n\n## When to Apply\n\n- User asks to **write E2E tests** for a feature or flow\n- User asks to **fix a failing E2E test**\n- User asks to **review E2E test coverage**\n- User modifies `data-testid` attributes in components (may break existing tests)\n- User changes test utilities in `src/frontend/tests/utils/`\n\n**Do NOT apply** when:\n- User asks about unit tests (use `frontend-testing` skill for Jest)\n- User asks about backend tests (use `backend-code-review` skill for pytest)\n\n## Tech Stack\n\n| Tool | Version | Purpose |\n|------|---------|---------|\n| Playwright | 1.59.1 | E2E test runner + browser automation |\n| Chromium | (bundled) | Default browser (Firefox/Safari disabled) |\n| Custom fixtures | `tests/fixtures.ts` | Auto-detects API errors and flow execution failures |\n\n## Key Commands\n\n```bash\n# Run all E2E tests\nnpx playwright test\n\n# Run tests filtered by tag\nnpx playwright test --grep \"@release\"\nnpx playwright test --grep \"@workspace\"\nnpx playwright test --grep \"@starter-projects\"\n\n# Run a specific test file\nnpx playwright test tests/core/features/run-flow.spec.ts\n\n# Debug mode (headed browser + step through)\nnpx playwright test --debug\n\n# Show HTML report after run\nnpx playwright show-report\n\n# Update snapshots (if used)\nnpx playwright test --update-snapshots\n```\n\n## Configuration\n\n**File**: `src/frontend/playwright.config.ts`\n\n| Setting | Value | Why |\n|---------|-------|-----|\n| `fullyParallel` | `true` | Tests run in parallel for speed |\n| `timeout` | 5 minutes | Flow builds can be slow; prevents false timeouts |\n| `retries` | 3 (local), 2 (CI) | Flaky network/rendering issues; retries catch them |\n| `workers` | 2 | Balances speed and resource usage |\n| `actionTimeout` | 20s | Individual action timeout (click, fill, etc.) |\n| `trace` | `on-first-retry` | Captures trace on failures for debugging |\n| `baseURL` | `http://localhost:3000` | Vite dev server |\n\n**WebServer**: Playwright auto-starts backend (uvicorn on 7860) + frontend (npm start on 3000).\n\n## Directory Structure\n\n```\nsrc/frontend/tests/\n├── fixtures.ts                     # Custom test fixture with error detection\n├── globalTeardown.ts               # Cleanup (removes temp DB after tests)\n├── core/\n│   ├── features/                   # Main feature tests (run-flow, playground, etc.)\n│   ├── integrations/               # Starter project / template tests\n│   ├── regression/                 # Bug regression tests\n│   └── unit/                       # Component-level Playwright tests\n├── extended/\n│   ├── features/                   # Extended features (MCP, auto-save, etc.)\n│   ├── integrations/               # Extended integrations\n│   └── regression/                 # Extended regressions\n└── utils/                          # 37+ shared helper functions\n```\n\n## File Naming\n\n- **kebab-case** with `.spec.ts` suffix: `run-flow.spec.ts`, `playground.spec.ts`, `flow-lock.spec.ts`\n- Template tests may use spaces: `Document QA.spec.ts`, `Social Media Agent.spec.ts`\n- Sharded tests for parallelization: `chatInputOutputUser-shard-0.spec.ts`\n\n> **Note**: E2E tests use `.spec.ts` (Playwright convention). Unit tests use `.test.tsx` (Jest convention). Do not mix them.\n\n## Test Anatomy\n\n### Basic Test\n\n```typescript\nimport { expect, test } from \"../../fixtures\";\nimport { awaitBootstrapTest } from \"../../utils/await-bootstrap-test\";\n\ntest(\n  \"user should be able to run a flow successfully\",\n  { tag: [\"@release\", \"@workspace\"] },\n  async ({ page }) => {\n    await awaitBootstrapTest(page);\n\n    // Arrange: Create a flow\n    await page.getByTestId(\"blank-flow\").click();\n\n    // Act: Add components and run\n    await page.getByTestId(\"sidebar-search-input\").fill(\"Chat Output\");\n    // ... setup ...\n\n    // Assert: Verify result\n    await expect(page.getByTestId(\"build-status-success\")).toBeVisible({ timeout: 30000 });\n  },\n);\n```\n\n### With test.describe\n\n```typescript\ntest.describe(\"Flow Lock Feature\", () => {\n  test(\n    \"should lock and unlock a flow\",\n    { tag: [\"@release\", \"@api\"] },\n    async ({ page }) => {\n      // ...\n    },\n  );\n\n  test(\n    \"should prevent editing when locked\",\n    { tag: [\"@release\"] },\n    async ({ page }) => {\n      // ...\n    },\n  );\n});\n```\n\n### With Serial Mode (tests that depend on order)\n\n```typescript\ntest.describe.configure({ mode: \"serial\" });\n\ntest(\"step 1: create flow\", async ({ page }) => { /* ... */ });\ntest(\"step 2: edit flow\", async ({ page }) => { /* ... */ });\ntest(\"step 3: delete flow\", async ({ page }) => { /* ... */ });\n```\n\n### With Event Delivery Modes (streaming/polling/direct)\n\n```typescript\nimport { withEventDeliveryModes } from \"../../utils/withEventDeliveryModes\";\n\nwithEventDeliveryModes(\n  \"Document Q&A should work\",\n  { tag: [\"@release\", \"@starter-projects\"] },\n  async ({ page }) => {\n    // This test runs 3 times: streaming, polling, direct\n    // Each mode is configured automatically via route interception\n  },\n);\n```\n\n## Tags System\n\nEvery test MUST be tagged with `@release` — the release run greps for it, so an\nuntagged or wrongly-tagged spec silently drops out of release coverage. Add the\ndomain tag(s) below on top of `@release` (a test can have more than one). These\nsix are the only allowed tags; do not invent new ones.\n\n| Tag | Purpose | When to Use |\n|-----|---------|-------------|\n| `@release` | Part of the release run (required on every spec) | All tests |\n| `@workspace` | Workspace/flow management | Creating, editing, deleting flows |\n| `@api` | API-dependent features | Tests that call backend endpoints |\n| `@database` | Database operations | Tests involving persistence |\n| `@components` | Component-level tests | Individual component behavior |\n| `@starter-projects` | Template/starter project tests | Pre-built flow templates |\n\n```typescript\n// Right: tag your test\ntest(\"my feature test\", { tag: [\"@release\", \"@workspace\"] }, async ({ page }) => { ... });\n\n// Wrong: no tags — test can't be filtered\ntest(\"my feature test\", async ({ page }) => { ... });\n```\n\n## Custom Fixtures: Error Detection\n\n**Always import `test` and `expect` from `../../fixtures`, NOT from `@playwright/test`.**\n\n```typescript\n// Right\nimport { expect, test } from \"../../fixtures\";\n\n// Wrong — bypasses error detection\nimport { expect, test } from \"@playwright/test\";\n```\n\nWhy: The custom fixture automatically monitors all `/api/` responses and fails the test if:\n- HTTP 400, 404, 422, or 500 errors occur\n- Flow execution returns `error: true` in event streams\n- Python exceptions appear in streamed responses\n\nTo opt-in to expected errors (e.g., testing error handling):\n```typescript\ntest(\"should show error on invalid input\", { tag: [\"@release\"] }, async ({ page }) => {\n  page.allowFlowErrors();  // Allow flow errors for this test\n  // ... test that expects errors ...\n});\n```\n\n## Selector Strategy\n\n### Priority (in order of preference)\n\n1. **`getByTestId`** — Most stable, used 95% of the time in Langflow\n2. **`getByRole`** — For buttons, headings, and form elements\n3. **`getByText`** — For visible text content\n4. **`waitForSelector`** — For CSS selectors and dynamic elements\n5. **`locator`** — For complex selectors (CSS, XPath)\n\n### Common data-testid Patterns\n\n**Canvas & Navigation:**\n- `blank-flow` — New blank flow button\n- `sidebar-search-input` — Component search\n- `canvas_controls_dropdown` — Canvas controls menu\n- `fit_view`, `zoom_out`, `zoom_in` — Canvas controls\n- `react-flow-id` — ReactFlow canvas container\n\n**Component Fields:**\n- `popover-anchor-input-{fieldname}` — Input field for a component parameter\n- `input-chat-playground` — Playground chat input\n- `div-chat-message` — Chat message in playground\n\n**Actions:**\n- `add-component-button-{component}` — Add component to canvas\n- `button-send` — Send chat message\n- `button_run_{component}` — Run specific component\n- `publish-button`, `save-flow-button` — Flow actions\n- `edit-fields-button` — Toggle inspection panel field editor\n\n**Modals & Panels:**\n- `modal-title` — Modal heading\n- `icon-Globe` — Global variables\n- `icon-Lock` — Flow lock toggle\n- `session-selector` — Playground session switcher\n\n### Important: Global Variables and Badges\n\nWhen a component field has a global variable selected (`load_from_db: true` + `value: \"OPENAI_API_KEY\"`), the field renders a **badge** instead of an `<input>` element. This means `getByTestId(\"popover-anchor-input-api_key\")` will NOT find the element — it doesn't exist in the DOM.\n\nTemplates with global variables pre-selected: Market Research, Price Deal Finder, Research Agent.\nTemplates without (input IS rendered): Instagram Copywriter.\n\n## Core Helper Functions\n\nLocated in `src/frontend/tests/utils/`:\n\n| Function | What it Does | When to Use |\n|----------|-------------|-------------|\n| `awaitBootstrapTest(page)` | Waits for app to fully load | **Start of every test** |\n| `initialGPTsetup(page)` | Full setup: adjustView → updateComponents → selectModel → addKey → adjustView → unselectNodes | Tests that need OpenAI configured |\n| `adjustScreenView(page, opts?)` | Fit view + zoom out | After adding components to canvas |\n| `zoomOut(page, times)` | Zoom out N times | When components are too small |\n| `selectGptModel(page)` | Selects gpt-4o-mini for all Language Model nodes | GPT-dependent tests |\n| `addOpenAiInputKey(page)` | Fills OPENAI_API_KEY for all openai_api_key fields | Tests requiring API key |\n| `enableInspectPanel(page)` | Toggles inspection panel ON | **MUST call before `edit-fields-button`** |\n| `disableInspectPanel(page)` | Toggles inspection panel OFF | Cleanup after inspection |\n| `updateOldComponents(page)` | Clicks \"Update all\" if outdated components exist | After loading saved flows |\n| `unselectNodes(page)` | Clicks empty canvas area to deselect all nodes | After node operations |\n| `renameFlow(page, { flowName })` | Renames the current flow | Flow management tests |\n| `uploadFile(page, filename)` | Uploads a file from test assets | File upload tests |\n| `withEventDeliveryModes(...)` | Runs test 3x: streaming, polling, direct | Starter project tests |\n\n### initialGPTsetup Options\n\n```typescript\nawait initialGPTsetup(page);  // All steps\n\nawait initialGPTsetup(page, {\n  skipAdjustScreenView: true,\n  skipUpdateOldComponents: true,\n  skipSelectGptModel: true,\n});\n```\n\n### Inspection Panel Pattern (CRITICAL)\n\n```typescript\n// MUST enable inspection panel FIRST\nawait enableInspectPanel(page);\n\n// Click a node to select it\nawait page.getByTestId(\"title-OpenAI\").click();\n\n// Open field editor\nawait page.getByTestId(\"edit-fields-button\").click();\n\n// Toggle field visibility\nawait page.getByTestId(\"showmodel_name\").click();\n\n// Close field editor\nawait page.getByTestId(\"edit-fields-button\").click();\n```\n\n**If you skip `enableInspectPanel(page)`, the `edit-fields-button` will NOT be visible.**\n\n## Skip Patterns\n\n```typescript\n// Skip test if env var missing\ntest.skip(!process?.env?.OPENAI_API_KEY, \"OPENAI_API_KEY required to run this test\");\n\n// Skip test unconditionally with reason\ntest.skip(true, \"Feature not yet implemented with new designs\");\n```\n\n## Writing Good E2E Tests\n\n### Do:\n- **Tag every test** with `@release` (plus any domain tags that apply)\n- **Import from `../../fixtures`**, not `@playwright/test`\n- **Start with `awaitBootstrapTest(page)`** — always\n- **Use `getByTestId`** for stable selectors\n- **Set explicit timeouts** on `waitForSelector` and `expect(...).toBeVisible()` for async operations\n- **Test the complete user flow**: setup → action → verification\n- **Use `withEventDeliveryModes`** for tests that involve flow execution (chat, build)\n\n### Don't:\n- **Don't use `page.waitForTimeout()`** unless absolutely necessary — prefer `waitForSelector` or `expect().toBeVisible()`\n- **Don't hardcode API keys** — read from `process.env.OPENAI_API_KEY`\n- **Don't skip tests without a reason** — always provide the second argument to `test.skip()`\n- **Don't import from `@playwright/test`** — use the custom fixtures\n- **Don't forget `enableInspectPanel(page)`** before accessing `edit-fields-button`\n- **Don't assume input fields exist** when global variables are selected (badge renders instead)\n\n### Challenge Tests (Apply Here Too)\n\nE2E tests should also cover adversarial scenarios:\n- **Invalid input**: paste 10K characters, special characters (`<script>alert(1)</script>`), empty submissions\n- **Network interruption**: what happens if the user loses connection mid-build?\n- **Permission boundaries**: can a user access another user's flow via direct URL?\n- **Concurrent actions**: double-click delete, rapid chat messages\n- **Error recovery**: does the UI recover gracefully from a 500 error?\n\n## References\n\n- [Selector Patterns](references/selectors.md) — Complete data-testid catalog\n- [Helper Functions](references/helpers.md) — Detailed documentation of all 37+ utility functions\n- [Test Fixtures](references/fixtures.md) — Custom fixture error detection behavior\n","frontmatter":{"name":"e2e-testing","description":"Write and review Playwright E2E tests for Langflow. Trigger when the user asks to write, fix, or review E2E tests, spec files, Playwright tests, or integration tests that exercise the full UI. Also trigger when modifying data-testid attributes, test helpers in tests/utils/, or fixture configuration."},"isInternal":false,"tokens":3214,"sizeBytes":13437},{"name":"business-logic.md","path":".agents/skills/frontend-code-review/references/business-logic.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/business-logic.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/business-logic.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/business-logic.md)","tokens":300,"sizeBytes":248},{"name":"code-quality.md","path":".agents/skills/frontend-code-review/references/code-quality.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/code-quality.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/code-quality.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/code-quality.md)","tokens":300,"sizeBytes":244},{"name":"performance.md","path":".agents/skills/frontend-code-review/references/performance.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/performance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/performance.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/performance.md)","tokens":300,"sizeBytes":242},{"name":"SKILL.md","path":".agents/skills/frontend-code-review/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/SKILL.md","title":"frontend-code-review","category":"anthropic-skill","format":"markdown","content":"---\nname: frontend-code-review\ndescription: \"Review frontend code (.tsx, .ts, .js files) for quality, performance, and correctness against Langflow's frontend conventions. Supports pending-change reviews and file-targeted reviews.\"\n---\n\n# Frontend Code Review\n\n## When to use this skill\n\nUse this skill whenever the user asks to **review, analyze, or improve** frontend code (`.tsx`, `.ts`, `.js` files) under the `src/frontend/` directory. Supports the following review modes:\n\n1. **Pending-change review** -- inspect staged or working-tree files slated for commit and flag checklist violations before submission.\n2. **File-targeted review** -- review the specific file(s) the user names and report the relevant checklist findings.\n\nDo NOT use this skill when:\n\n- The request is about backend code (`.py` files under `src/backend/`).\n- The user is not asking for a review/analysis/improvement of frontend code.\n- The scope is outside `src/frontend/` (unless the user explicitly asks to review frontend-related changes elsewhere).\n\n## How to use this skill\n\nFollow these steps when using this skill:\n\n1. **Identify the review mode** (pending-change vs file-targeted) based on the user's input. Keep the scope tight: review only what the user provided or explicitly referenced.\n2. Follow the rules defined in the **Checklist** to perform the review. If no checklist rule matches, apply **General Review Rules** as a fallback.\n3. Compose the final output strictly following the **Required Output Format**.\n\nNotes when using this skill:\n\n- Always include actionable fixes or suggestions (including possible code snippets).\n- Use `File:Line` references when a file path and line numbers are available; otherwise, use the most specific identifier you can.\n- The Langflow frontend uses React 19, TypeScript 5.4, Vite 7 with SWC, Zustand for state management, TanStack React Query for server state, @xyflow/react v12 for graph visualization, Radix UI + shadcn-ui components, Tailwind CSS v3, and Biome for linting/formatting.\n\n## Checklist\n\n- **Code quality**: For any reviewed file, follow [references/code-quality.md](references/code-quality.md) to check styling conventions, TypeScript usage, Biome compliance, and component patterns.\n- **Performance**: If the review scope involves React components, hooks, Zustand stores, React Query usage, or @xyflow/react node rendering, follow [references/performance.md](references/performance.md) to check for re-render issues, memoization, and data flow patterns.\n- **Business logic**: If the review scope involves custom nodes (GenericNode), flow state, API calls, the component system, global variables, or the inspection panel, follow [references/business-logic.md](references/business-logic.md) to check for Langflow-specific correctness.\n- **i18n**: If the diff adds or changes user-facing text (labels, buttons, tooltips, modals, toasts, errors, placeholders, aria-labels), follow the [frontend-i18n skill](../frontend-i18n/SKILL.md): no hardcoded UI strings — everything through `t(...)` — and every new key present in **all** locale files (`src/frontend/src/locales/*.json`).\n\n## General Review Rules\n\n### 1. Security Review\n\nCheck for:\n- XSS vulnerabilities (dangerouslySetInnerHTML, unescaped user input)\n- Sensitive data exposure in client-side code\n- Insecure direct object references in API calls\n- Hardcoded secrets, tokens, or API keys\n\n### 2. Accessibility Review\n\nCheck for:\n- Missing aria labels on interactive elements\n- Keyboard navigation support\n- Proper use of semantic HTML elements\n- Color contrast issues in custom styling\n\n### 3. Code Quality Review\n\nCheck for:\n- Code duplication (DRY violations — extract at 3+ identical usages)\n- Functions/components doing too much (SRP violations — if you need \"and\" to describe it, split it)\n- Deep nesting or complex conditionals (prefer early returns and guard clauses)\n- Magic numbers/strings without named constants\n- Poor naming: generic names (`data`, `result`, `temp`), missing verb prefixes on functions, missing `is`/`has`/`can`/`should` prefixes on booleans\n- Missing error handling or error boundaries\n- Incomplete TypeScript type coverage (no `any`, no `as any` casts)\n- Comments that explain WHAT instead of WHY\n- Commented-out code (use version control)\n- Boolean parameters that switch component behavior (use two components instead)\n- Mutable patterns where `const` or immutable alternatives exist\n- Production files exceeding ~500 lines (red flag at 600+)\n- `console.log` in production code (Biome flags this)\n\n### 4. Testing Impact Review\n\nCheck for:\n- Changes to data-testid attributes that may break E2E tests\n- Modified component interfaces that require test updates\n- New interactive elements missing data-testid attributes\n\n### 5. Pre-Commit Verification\n\nFor pending-change reviews, verify:\n- `npm run format` (Biome formatter) — zero diffs\n- `npm run lint` (Biome linter) — zero errors\n- `npm test` (Jest) — zero failures\n\n## Required Output Format\n\nWhen this skill is invoked, the response must exactly follow one of the two templates:\n\n### Template A (any findings)\n\n```markdown\n# Code Review\n\nFound <N> urgent issues that need to be fixed:\n\n## 1. <brief description of issue>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n\n### Suggested fix\n\n<brief description of suggested fix with code example>\n\n---\n\n... (repeat for each urgent issue) ...\n\nFound <M> suggestions for improvement:\n\n## 1. <brief description of suggestion>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n\n### Suggested fix\n\n<brief description of suggested fix with code example>\n\n---\n\n... (repeat for each suggestion) ...\n```\n\n- If there are no urgent issues, omit that section. If there are no suggestions, omit that section.\n- If the issue count exceeds 10, summarize as \"10+ urgent issues\" or \"10+ suggestions\" and output only the first 10 items.\n- Do not compress the blank lines between sections; keep them as-is for readability.\n- If Template A is used (there are issues to fix) and at least one issue requires code changes, append a brief follow-up question after the structured output asking whether the user wants the suggested fixes applied. For example: \"Would you like me to apply the suggested fixes to address these issues?\"\n\n### Template B (no issues)\n\n```markdown\n## Code Review\n\nNo issues found.\n```\n","frontmatter":{"name":"frontend-code-review","description":"Review frontend code (.tsx, .ts, .js files) for quality, performance, and correctness against Langflow's frontend conventions. Supports pending-change reviews and file-targeted reviews."},"isInternal":false,"tokens":1417,"sizeBytes":6368},{"name":"SKILL.md","path":".agents/skills/frontend-i18n/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-i18n/SKILL.md","title":"frontend-i18n","category":"anthropic-skill","format":"markdown","content":"---\nname: frontend-i18n\ndescription: Add, change, or review user-facing text in the Langflow frontend using the i18n system (i18next / react-i18next). Use whenever a change adds or edits UI strings — labels, buttons, tooltips, modals, toasts, error messages, empty states — or when reviewing a diff that contains user-facing text. Every user-facing string must go through the translation system and every new key must exist in ALL locale files. Do NOT use for backend strings, log messages, or code identifiers.\n---\n\n# Frontend i18n\n\nThe Langflow frontend is internationalized with **i18next + react-i18next**. Locales live in `src/frontend/src/locales/` — currently `en`, `de`, `es`, `fr`, `ja`, `pt`, `zh-Hans`. A hardcoded user-facing string, or a key missing from one locale, ships broken UI for part of the user base.\n\n## The two rules\n\n1. **Every user-facing string goes through `t(...)`** — never hardcoded JSX text for labels, buttons, tooltips, modals, toasts, errors, placeholders, `aria-label`s, or empty states.\n2. **Every new key is added to ALL locale files in the same PR** (`en.json`, `de.json`, `es.json`, `fr.json`, `ja.json`, `pt.json`, `zh-Hans.json`). `fallbackLng: \"en\"` means a missing key silently shows English — it won't crash, which is exactly why reviews must catch it.\n\n## How it works here\n\n- Config: `src/frontend/src/i18n.ts` — a custom i18next instance. `en` is bundled statically; other languages are **lazy-loaded** by `loadLanguage()` via dynamic import. Language preference comes from `localStorage.getItem(\"languagePreference\")`, normalized (e.g. `zh-CN` → `zh-Hans`, unknown → `en`).\n- Keys are **flat, dot-namespaced by feature**: `deleteModal.title`, `errors.fileTooLarge`, `crash.restartButton`. Follow the existing namespace of the area you're touching; create a new prefix only for a genuinely new surface.\n- Interpolation uses `{{variable}}` in the string and an options object in the call.\n\n## Adding a string (the pattern)\n\n```tsx\nimport { useTranslation } from \"react-i18next\";\n\nconst { t } = useTranslation();\n\n<span>{t(\"deleteModal.title\")}</span>\n<p>{t(\"errors.fileTooLarge\", { maxSizeMB: \"10MB\" })}</p>\n```\n\nIn `locales/en.json` (and **every** other locale file):\n\n```json\n\"errors.fileTooLarge\": \"The file size is too large. Please select a file smaller than {{maxSizeMB}}.\"\n```\n\nFor the non-English locales, write a real translation when confident; if not, add the key with the English value and flag it in the PR — a present-but-untranslated key is visible and searchable, a missing key is invisible.\n\n## Rules that prevent real bugs\n\n- **Don't concatenate translated fragments** (`t(\"a\") + name + t(\"b\")`) — word order differs across languages. Use one key with interpolation: `t(\"greeting\", { name })`.\n- **Don't put JSX/HTML inside translation strings**; compose in JSX around `t()` calls (see how `crash.descriptionBefore` / `crash.githubIssues` / `crash.descriptionAfter` split around a link).\n- **Plurals**: use i18next plural forms (`_one` / `_other` suffixes), not `count === 1 ? ... : ...`.\n- **Keys are code**: renaming or deleting a key means updating **all seven** locale files — an orphan key in one file is dead weight; a rename missed in one file is a regression.\n- Dates/numbers/currency: format with `Intl.*` using the active locale, never hardcoded formats.\n\n## Review checklist (apply to any diff with UI text)\n\n- [ ] No hardcoded user-facing string in JSX (including `placeholder`, `title`, `aria-label`, toast/error text).\n- [ ] Every new key exists in **all** locale files (grep the key across `src/frontend/src/locales/*.json`).\n- [ ] No concatenation of translated fragments; interpolation used instead.\n- [ ] Removed/renamed keys cleaned up in all locales (no orphans).\n- [ ] Key follows the existing dot-namespace of the feature area.\n\nQuick check for a key across locales:\n\n```bash\nfor f in src/frontend/src/locales/*.json; do grep -L '\"my.new.key\"' \"$f\"; done\n```\n\n(prints the locale files where the key is **missing** — should print nothing)\n","frontmatter":{"name":"frontend-i18n","description":"Add, change, or review user-facing text in the Langflow frontend using the i18n system (i18next / react-i18next). Use whenever a change adds or edits UI strings — labels, buttons, tooltips, modals, toasts, error messages, empty states — or when reviewing a diff that contains user-facing text. Every user-facing string must go through the translation system and every new key must exist in ALL locale files. Do NOT use for backend strings, log messages, or code identifiers."},"isInternal":false,"tokens":1003,"sizeBytes":4049},{"name":"query-patterns.md","path":".agents/skills/frontend-query-mutation/references/query-patterns.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/query-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-query-mutation/references/query-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/query-patterns.md)","tokens":300,"sizeBytes":254},{"name":"runtime-rules.md","path":".agents/skills/frontend-query-mutation/references/runtime-rules.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/runtime-rules.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-query-mutation/references/runtime-rules.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/runtime-rules.md)","tokens":300,"sizeBytes":252},{"name":"SKILL.md","path":".agents/skills/frontend-query-mutation/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/SKILL.md","title":"frontend-query-mutation","category":"anthropic-skill","format":"markdown","content":"---\nname: frontend-query-mutation\ndescription: Guide for implementing Langflow frontend query and mutation patterns with Axios and TanStack React Query v5. Trigger when creating or updating API hooks in controllers/API/queries, consuming UseRequestProcessor in components, deciding whether to use useQuery or useMutation, handling conditional queries, cache invalidation, mutation error handling, or migrating legacy API calls to the query hook pattern.\n---\n\n# Frontend Query & Mutation\n\n## Intent\n\n- Keep API hooks in `controllers/API/queries/{domain}/` as the organized source of truth.\n- Use `UseRequestProcessor` for consistent retry, invalidation, and error handling.\n- Use Axios `api` instance for all HTTP calls (never raw `fetch` for REST).\n- Use `performStreamingRequest()` for streaming operations (build, chat).\n- Keep cache invalidation in mutation `onSettled` callbacks.\n\n## Architecture Overview\n\nLangflow uses **Axios + TanStack React Query v5** for data fetching. There is no oRPC or contract layer. The architecture is:\n\n```\nComponent\n  -> API hook (controllers/API/queries/{domain}/use-{verb}-{resource}.ts)\n    -> UseRequestProcessor (controllers/API/services/request-processor.ts)\n      -> useQuery / useMutation (TanStack React Query)\n        -> queryFn / mutationFn\n          -> api.get/post/patch/delete (Axios instance from controllers/API/api.tsx)\n```\n\n### Key Files\n\n| File | Purpose |\n|------|---------|\n| `controllers/API/api.tsx` | Axios instance, `ApiInterceptor` component, `performStreamingRequest()` |\n| `controllers/API/services/request-processor.ts` | `UseRequestProcessor` hook wrapping `useQuery`/`useMutation` with retry defaults |\n| `controllers/API/helpers/constants.ts` | URL constants (`URLs`) and `getURL()` helper |\n| `controllers/API/queries/{domain}/` | Domain-organized query and mutation hooks |\n| `types/api/index.ts` | `useQueryFunctionType`, `useMutationFunctionType` type helpers |\n| `stores/` | Zustand stores for client-side state |\n\n## Workflow\n\n1. Identify the change surface.\n   - Read `references/query-patterns.md` for hook structure, naming, URL patterns, and query/mutation call-site shape.\n   - Read `references/runtime-rules.md` for conditional queries, invalidation, error handling, and streaming.\n   - Read both references when a task spans hook creation and runtime behavior.\n2. Implement the hook following existing patterns.\n   - Use `UseRequestProcessor` for `query()` and `mutate()` wrappers.\n   - Use the `api` Axios instance for all HTTP calls.\n   - Use `getURL()` for constructing API paths.\n   - Type the hook with `useQueryFunctionType` or `useMutationFunctionType`.\n3. Preserve Langflow conventions.\n   - Query keys are arrays with the hook name: `[\"useGetFlows\"]`.\n   - Mutations invalidate related queries in `onSettled`.\n   - Zustand store updates happen in the query/mutation function, not in components.\n   - `UseRequestProcessor` provides default retry (5 for queries, 3 for mutations) with exponential backoff.\n\n## Files Commonly Touched\n\n- `controllers/API/queries/{domain}/use-get-*.ts` (query hooks)\n- `controllers/API/queries/{domain}/use-post-*.ts` (create mutations)\n- `controllers/API/queries/{domain}/use-patch-*.ts` (update mutations)\n- `controllers/API/queries/{domain}/use-delete-*.ts` (delete mutations)\n- `controllers/API/helpers/constants.ts` (URL constants)\n- `types/api/index.ts` (API type definitions)\n- `stores/` (Zustand store updates triggered by queries)\n\n## References\n\n- Use `references/query-patterns.md` for hook structure, naming conventions, directory layout, and anti-patterns.\n- Use `references/runtime-rules.md` for conditional queries, invalidation, `mutate` versus `mutateAsync`, error handling, and streaming requests.\n\nTreat this skill as the single query and mutation entry point for Langflow frontend work. Keep detailed rules in the reference files instead of duplicating them in project docs.\n","frontmatter":{"name":"frontend-query-mutation","description":"Guide for implementing Langflow frontend query and mutation patterns with Axios and TanStack React Query v5. Trigger when creating or updating API hooks in controllers/API/queries, consuming UseRequestProcessor in components, deciding whether to use useQuery or useMutation, handling conditional queries, cache invalidation, mutation error handling, or migrating legacy API calls to the query hook pattern."},"isInternal":false,"tokens":893,"sizeBytes":3914},{"name":"async-testing.md","path":".agents/skills/frontend-testing/references/async-testing.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/async-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/async-testing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/async-testing.md)","tokens":300,"sizeBytes":238},{"name":"checklist.md","path":".agents/skills/frontend-testing/references/checklist.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/checklist.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/checklist.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/checklist.md)","tokens":300,"sizeBytes":230},{"name":"common-patterns.md","path":".agents/skills/frontend-testing/references/common-patterns.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/common-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/common-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/common-patterns.md)","tokens":300,"sizeBytes":242},{"name":"domain-components.md","path":".agents/skills/frontend-testing/references/domain-components.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/domain-components.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/domain-components.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/domain-components.md)","tokens":300,"sizeBytes":246},{"name":"mocking.md","path":".agents/skills/frontend-testing/references/mocking.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/mocking.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/mocking.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/mocking.md)","tokens":300,"sizeBytes":226},{"name":"workflow.md","path":".agents/skills/frontend-testing/references/workflow.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/workflow.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/frontend-testing/references/workflow.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/workflow.md)","tokens":300,"sizeBytes":228},{"name":"SKILL.md","path":".agents/skills/frontend-testing/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/SKILL.md","title":"frontend-testing","category":"anthropic-skill","format":"markdown","content":"# Frontend Testing Skill - Langflow\n\n## When to Apply\n\nActivate this skill when:\n- Writing new unit or integration tests for React components, hooks, utilities, or Zustand stores\n- Reviewing existing tests for correctness, coverage, or best practices\n- Improving test coverage for under-tested modules\n- Debugging flaky or failing tests\n- Refactoring test code for maintainability\n\n## Tech Stack\n\n| Technology | Version | Purpose |\n|---|---|---|\n| Jest | 30.x | Test runner and assertion framework |\n| ts-jest | 29.x | TypeScript transform for Jest |\n| React Testing Library | 16.x | Component rendering and DOM queries |\n| @testing-library/user-event | 14.x | Realistic user interaction simulation |\n| @testing-library/jest-dom | 6.x | Extended DOM matchers |\n| jsdom | (via jest-environment-jsdom 30.x) | Browser environment simulation |\n| React | 19.x | UI framework |\n| TypeScript | 5.4 | Type safety |\n| Zustand | 4.x | State management |\n| React Router DOM | 6.x | Client-side routing |\n| @tanstack/react-query | 5.x | Server state management |\n| Axios | 1.x | HTTP client |\n\n## Project Configuration\n\n- **Jest config**: `src/frontend/jest.config.js`\n- **Setup files**: `src/frontend/jest.setup.js` (globals/mocks) and `src/frontend/src/setupTests.ts` (DOM matchers, ResizeObserver, IntersectionObserver, matchMedia)\n- **Path alias**: `@/` maps to `<rootDir>/src/`\n- **Test match patterns**: `src/**/__tests__/**/*.{test,spec}.{ts,tsx}` and `src/**/*.{test,spec}.{ts,tsx}`\n- **Transform**: Custom `transform-import-meta.js` handles `import.meta` for Jest compatibility\n- **Global mocks** (in `jest.setup.js`): `@radix-ui/react-form`, `react-markdown`, `remark-gfm`, `remark-math`, `rehype-mathjax/browser`, `lucide-react/dynamicIconImports`, `@/components/common/genericIconComponent`, `@/icons/BotMessageSquare`, `@/stores/darkStore`, `localStorage`, `sessionStorage`, `crypto`\n\n## Key Commands\n\n```bash\n# Run all tests\nnpm test\n\n# Run a specific test file\nnpm test -- path/to/file.test.tsx\n\n# Run tests matching a pattern\nnpm test -- --testPathPattern=\"alertStore\"\n\n# Run tests in watch mode\nnpm run test:watch\n\n# Run tests with coverage\nnpm run test:coverage\n\n# Run a single test file with coverage\nnpm test -- --coverage --collectCoverageFrom='src/path/to/source.ts' path/to/__tests__/source.test.ts\n```\n\n## File Naming and Location\n\nTest files follow one of two patterns:\n\n1. **Dedicated `__tests__` directory** (preferred for components and modules):\n   ```\n   src/components/core/my-component/\n   ├── my-component.tsx\n   └── __tests__/\n       └── my-component.test.tsx\n   ```\n\n2. **Co-located test file** (acceptable for utilities and simple modules):\n   ```\n   src/utils/\n   ├── myUtil.ts\n   └── myUtil.test.ts\n   ```\n\nNaming convention: `ComponentName.test.tsx` for components, `hook-name.test.ts` for hooks, `util-name.test.ts` for utilities.\n\n**Do NOT use `.spec.tsx`** -- while technically matched, the project convention is `.test.tsx`.\n\n## Test Structure Template\n\n```tsx\nimport { render, screen } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport MyComponent from \"../MyComponent\";\n\n// Mock dependencies (use jest.mock, NOT vi.mock)\njest.mock(\"@/controllers/API/api\", () => ({\n  get: jest.fn(),\n  post: jest.fn(),\n}));\n\ndescribe(\"MyComponent\", () => {\n  beforeEach(() => {\n    jest.clearAllMocks();\n  });\n\n  describe(\"rendering\", () => {\n    it(\"should render the component with default props\", () => {\n      // Arrange\n      render(<MyComponent />);\n\n      // Act - (none for render test)\n\n      // Assert\n      expect(screen.getByRole(\"button\", { name: /submit/i })).toBeInTheDocument();\n    });\n  });\n\n  describe(\"user interactions\", () => {\n    it(\"should call onSubmit when the form is submitted\", async () => {\n      // Arrange\n      const user = userEvent.setup();\n      const onSubmit = jest.fn();\n      render(<MyComponent onSubmit={onSubmit} />);\n\n      // Act\n      await user.click(screen.getByRole(\"button\", { name: /submit/i }));\n\n      // Assert\n      expect(onSubmit).toHaveBeenCalledTimes(1);\n    });\n  });\n});\n```\n\n## Incremental Testing Workflow\n\nWhen testing a directory with multiple files, follow this order:\n\n1. **Identify all source files** in the target directory\n2. **Order by complexity** (simplest first):\n   - Pure utility functions (no React, no side effects)\n   - Constants and configuration objects\n   - Custom hooks (no UI rendering)\n   - Simple presentational components (no state, no side effects)\n   - Stateful components with local state\n   - Components using Zustand stores\n   - Components with API calls or complex async behavior\n   - Integration-level components that compose many children\n3. **For each file**:\n   a. Read the source file completely\n   b. Identify all exported functions, components, and types\n   c. Write tests covering all branches and edge cases\n   d. Run the tests and fix any failures\n   e. Check coverage and add tests for uncovered lines\n   f. Move to the next file\n4. **Run full directory coverage** at the end to verify\n\n## Complexity-Based Test Ordering\n\nWithin a single test file, order test cases from simplest to most complex:\n\n1. Default rendering / initial state\n2. Props variations and conditional rendering\n3. User interactions (clicks, typing, form submission)\n4. Async operations (API calls, timers)\n5. Error states and edge cases\n6. Integration with stores or context\n7. Cleanup and unmount behavior\n\n## Core Principles\n\n### Arrange-Act-Assert (AAA)\nEvery test should have a clear three-phase structure. Use blank lines to separate each phase for readability.\n\n### Black-Box Testing\nTest the component from the user's perspective. Query by role, label, text, or `data-testid` -- never by CSS class, internal state variable, or implementation detail.\n\n### Single Behavior Per Test\nEach `it()` block should verify exactly one behavior. If you need to write \"and\" in the test name, split it into two tests.\n\n### Semantic Test Names\nUse descriptive names that explain the expected behavior:\n- Good: `\"should disable the submit button when the form is invalid\"`\n- Bad: `\"button test\"` or `\"test 1\"`\n\nFormat: `\"should [expected behavior] when [condition]\"`\n\n## Required Test Scenarios\n\nFor every component, cover at minimum:\n\n### Rendering\n- Default render with no optional props\n- Render with all optional props provided\n- Conditional rendering branches (if/else in JSX)\n\n### Props and State\n- Each prop variation that changes rendered output\n- Default prop values\n- State transitions triggered by user actions\n\n### User Interactions\n- Click handlers\n- Form input and submission\n- Keyboard navigation (if applicable)\n- Hover/focus states (if applicable)\n\n### Challenge Tests (MANDATORY — not optional)\n\n**Happy path tests alone are NOT enough.** They only confirm the code works when everything is perfect. Real bugs hide in the cracks. You MUST write tests that actively TRY TO BREAK the code:\n\n**Unexpected inputs:**\n- `null`, `undefined`, `\"\"`, `[]`, `{}`, `0`, `-1`, `NaN`, `Infinity`\n- What happens when a required prop is missing?\n- What happens when data from the API comes back with missing fields?\n\n**Boundary values:**\n- Max length strings (paste 10,000 chars in an input)\n- Exactly at the limit, one past the limit\n- Zero items, one item, maximum items\n- First page, last page, out-of-range page\n\n**Malformed data:**\n- API returns `{ data: null }` instead of `{ data: [] }`\n- JSON with extra unexpected fields\n- Dates in wrong format, numbers as strings\n\n**Error states:**\n- Network failure (API rejects with 500)\n- Authentication expired mid-action (401)\n- Resource not found (404)\n- Permission denied (403)\n- Timeout\n\n**What should NOT happen:**\n- Verify that deleting a flow does NOT delete flows from other users\n- Verify that a read-only user CANNOT trigger write mutations\n- Verify that XSS payloads in user input are sanitized\n\n**Rapid/concurrent actions:**\n- Double-click on submit button\n- Rapid repeated API calls\n- Unmount component while async operation is in flight\n\n**Write tests based on REQUIREMENTS, not on what the source code does.** This is how you catch bugs where the code diverges from expected behavior.\n\n**When a test fails:** first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.\n\n### Async Behavior\n- Loading states\n- Success states\n- Error states\n- Timeout/retry behavior\n\n## Coverage Goals\n\nPer source file:\n- **Function coverage**: 100%\n- **Branch coverage**: > 95%\n- **Line coverage**: > 95%\n- **Statement coverage**: > 95%\n\nRun coverage for a specific file:\n```bash\nnpm test -- --coverage --collectCoverageFrom='src/path/to/file.ts' src/path/to/__tests__/file.test.ts\n```\n\n## Important Rules\n\n1. **Never use Vitest APIs**: Use `jest.fn()`, `jest.mock()`, `jest.spyOn()`, `jest.mocked()` -- never `vi.*` equivalents.\n2. **Never mock base UI components** from `@/components/ui/` -- render them as-is.\n3. **Check `jest.setup.js` before mocking**: Many modules are already globally mocked (darkStore, genericIconComponent, react-markdown, radix-form, etc.). Do not re-mock them.\n4. **Use `@testing-library/user-event`** over `fireEvent` for user interactions.\n5. **Wrap state updates in `act()`** when testing Zustand stores or React state changes.\n6. **Clean up after each test**: Use `beforeEach(() => jest.clearAllMocks())` and `afterEach` for timers.\n7. **Always write both happy path AND adversarial tests** (null, undefined, empty values, boundary conditions, error states).\n8. **Minimum coverage: 75%** (target 80%). Below 75% the task is not complete.\n\n## Forbidden Test Anti-Patterns\n\n| Pattern | Problem | How to Detect |\n|---------|---------|---------------|\n| **The Liar** | Test passes but doesn't verify the behavior it claims to test | Assertions don't match the test name |\n| **The Mirror** | Test reads source code and asserts exactly what the code does — finds zero bugs | Test would never fail even if logic changes |\n| **The Giant** | 50+ lines of setup, multiple acts, dozens of assertions | Should be 5+ separate tests |\n| **The Mockery** | So many mocks that the test only tests the mock setup | Count mocks — if > 3 deep, rethink |\n| **The Inspector** | Coupled to implementation details, breaks on any refactor | Tests internal state instead of behavior |\n| **The Chain Gang** | Tests depend on execution order or share mutable state | Tests fail when run in isolation |\n| **The Flaky** | Sometimes passes, sometimes fails with no code changes | Non-deterministic assertions or timing issues |\n\n## References\n\n- [Incremental Testing Workflow](references/workflow.md) - Step-by-step process for testing directories\n- [Mocking Guide](references/mocking.md) - Patterns for mocking APIs, stores, routers, and context\n- [Async Testing](references/async-testing.md) - Patterns for async operations, timers, and waitFor\n- [Common Patterns](references/common-patterns.md) - Query priority, events, forms, modals, data-driven tests\n- [Test Checklist](references/checklist.md) - Pre-submission verification checklist\n- [Domain Components](references/domain-components.md) - Langflow-specific component testing patterns\n- [Component Template](assets/component-test.template.tsx) - Starter template for component tests\n- [Hook Template](assets/hook-test.template.ts) - Starter template for hook tests\n- [Utility Template](assets/utility-test.template.ts) - Starter template for utility tests\n","isInternal":false,"tokens":2711,"sizeBytes":11502},{"name":"ibm-level1-criteria.md","path":".agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/references/ibm-level1-criteria.md)","tokens":300,"sizeBytes":260},{"name":"report-template.md","path":".agents/skills/ibm-a11y-level1-audit/references/report-template.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/references/report-template.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/ibm-a11y-level1-audit/references/report-template.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/references/report-template.md)","tokens":300,"sizeBytes":252},{"name":"SKILL.md","path":".agents/skills/ibm-a11y-level1-audit/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-level1-audit/SKILL.md","title":"ibm-a11y-level1-audit","category":"anthropic-skill","format":"markdown","content":"---\nname: ibm-a11y-level1-audit\ndescription: Perform a scoped IBM Equal Access Level 1 compliance audit of a chosen Langflow frontend surface (routes, components, or a PR) and produce a findings report mapped to WCAG/IBM Level 1 criteria. Default behavior is audit and report only — fixes are applied only when the user explicitly asks for remediation in the same request. Use when the user asks for an IBM Level 1 audit, a Level 1 compliance report, or to find/report Level 1 WCAG issues on a specific surface. For scanning a batch of routes without a report, see ibm-a11y-route-scan. For scanning and fixing an entire PR/branch end-to-end by default, see ibm-a11y-pr-remediation.\n---\n\n# IBM Accessibility Level 1 Audit\n\nAudit → report → (fix only if asked) → verify. Scope is **IBM Equal Access Toolkit v7.3 Level 1 only**. Do not expand into Level 2/3 unless the user asks.\n\nThis skill's default deliverable is a **report**, not a diff. If the user wants a full scan-and-fix pass across an entire PR/branch, use `ibm-a11y-pr-remediation` instead — it is fix-by-default. This skill fixes only when the user explicitly asks for remediation of the audited surface in the same request.\n\n## Related skills\n\n| Skill | Use for |\n|-------|---------|\n| [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) | How to write/run axe + IBM scans, baselines, component gotchas |\n| [ibm-a11y-route-scan](../ibm-a11y-route-scan/SKILL.md) | Python route scanner + Markdown/HTML report generation |\n| [ibm-a11y-pr-remediation](../ibm-a11y-pr-remediation/SKILL.md) | Fix-by-default scan of an entire PR/branch |\n| [frontend-i18n](../frontend-i18n/SKILL.md) | Any new/changed accessible names or UI strings |\n\n## Sources of truth\n\n1. **Level 1 criteria (engineering guide):** [references/ibm-level1-criteria.md](references/ibm-level1-criteria.md) — full checklist, deferred L2/L3 list, common failures, implementation patterns.\n2. **Langflow captured IBM Level 1 filter:** `src/frontend/tests/a11y/ibm-able-level-1-requirements.md` — 21 requirements from the IBM Able UI Level 1 filter. Prefer the engineering guide when the two disagree on pace; still map findings to both IDs when useful.\n3. **Verification hosts:** `src/frontend/tests/a11y/` — Playwright specs, baselines, README. Confirm this path exists before claiming coverage.\n\n## Workflow\n\nCopy and track:\n\n```\nLevel 1 Audit Progress:\n- [ ] 1. Scope the surface\n- [ ] 2. Scan (IBM + axe as applicable)\n- [ ] 3. Map findings to Level 1 criteria\n- [ ] 4. Write the report\n- [ ] 5. Fix violations (only if the user asked for remediation)\n- [ ] 6. Verify with tests/a11y (only if step 5 ran)\n- [ ] 7. Re-scan and update report status (only if step 5 ran)\n```\n\n### 1. Scope the surface\n\nIdentify what to audit from the user request:\n\n- Specific route(s), page(s), or component(s)\n- Pending git changes under `src/frontend`\n- Default: static routes + any stateful specs that touch the changed surface\n\nRead `src/frontend/tests/a11y/README.md` and list existing specs that already cover the surface.\n\n### 2. Scan\n\nRun **both** engines when the surface is interactive UI (see `ibm-a11y-testing-guide`):\n\n```bash\n# Playwright IBM scans (live DOM / stateful)\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# Optional: HTML triage report\nnpm run a11y:html-report --silent\n# → coverage/accessibility-reports/index.html\n\n# Ad-hoc route batch (default-loaded page only) — see ibm-a11y-route-scan for details\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y.json \\\n  --markdown /tmp/langflow-a11y.md \\\n  --html /tmp/langflow-a11y.html \\\n  --timeout-ms 45000\n```\n\nFor component-only changes, also run Jest axe where a `__tests__/*.a11y.test.tsx` exists.\n\nDo not invent findings. Prefer scanner output + manual Level 1 checks scanners miss (keyboard trap both directions, focus restore, reflow at 320px, color-not-only cues).\n\n### 3. Map findings to Level 1\n\nFor each issue, assign:\n\n- **WCAG / IBM ID** from [references/ibm-level1-criteria.md](references/ibm-level1-criteria.md) (e.g. `2.1.1`, `4.1.2`)\n- **IBM ruleId** when from Equal Access (`aria_accessiblename_exists`, `element_tabbable_role_valid`, …)\n- **Severity:** `violation` | `potentialviolation` | `manual`\n- **In scope?** Drop or defer anything listed under **Deferred to Level 2 & Level 3** in the criteria doc unless the user expands scope.\n\n### 4. Write the report\n\nUse [references/report-template.md](references/report-template.md). Deliver the report in the chat (and write a file only if the user asks for a path).\n\nRequired sections: Summary, Scope, Findings table, Fixes applied / proposed, Verification, Remaining risk / baselines.\n\n**Stop here by default.** List proposed fixes in the report without editing files, unless the user's request explicitly also asked for remediation (e.g. \"audit and fix\", \"fix what you find\"). If they only asked for an audit or report, do not proceed to step 5.\n\n### 5. Fix violations (only when explicitly requested)\n\nOnly continue past the report if the user's request explicitly asked for fixes alongside the audit. Otherwise stop after step 4.\n\n- Prefer semantic HTML over ARIA.\n- Follow Langflow patterns in `ibm-a11y-testing-guide` (AG Grid, Radix `asChild`, focus restore, icon-only `aria-label`).\n- Route new UI strings / `aria-label`s through `frontend-i18n`.\n- Do **not** silently disable scans. Use IBM baselines under `src/frontend/tests/a11y/baselines/` only for documented framework debt (see `ibm-a11y-testing-guide`).\n- Keep fixes minimal; do not refactor unrelated UI.\n\n### 6. Verify with `tests/a11y`\n\nConfirm path: `src/frontend/tests/a11y/`.\n\n| Surface | Spec to run / update |\n|---------|----------------------|\n| Static routes | `static-routes.a11y.spec.ts` (+ `scripts/a11y/a11y_routes.json` if new route) |\n| Auth | `auth-pages.a11y.spec.ts` |\n| Core pages | `core-pages.a11y.spec.ts` |\n| Data-rich (files, API keys, globals) | `files.a11y.spec.ts`, `api-keys.a11y.spec.ts`, `global-variables.a11y.spec.ts` |\n| Other data-rich | `data-rich-routes.a11y.spec.ts` |\n| Baselines | `baselines/*.json` |\n\nCommands:\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<relevant>.a11y.spec.ts --project=chromium --workers=5\n```\n\nIf coverage is missing for a fixed state, add a scan (and keyboard test when custom keyboard behavior was fixed) following `files.a11y.spec.ts` / `api-keys.a11y.spec.ts` patterns.\n\n### 7. Close the loop\n\nRe-run the same scans. Update the report: each finding → `fixed` | `baselined` | `open`. State commands run and whether IBM assert mode passed.\n\n## Manual Level 1 spot checks\n\nScanners miss some Level 1 tasks — spot-check when relevant:\n\n- **2.1.1 / 2.1.2:** Tab and Shift+Tab through the surface; Escape closes overlays; no trap.\n- **2.4.3 / 2.4.7:** Focus order matches visual order; focus ring visible.\n- **1.4.10:** 320px width / ~400% zoom — no essential horizontal scroll.\n- **1.4.1:** Status/errors not color-only.\n- **3.3.1 / 3.3.2:** Errors named in text and tied to fields; inputs labeled.\n\n## Best practices (data grids + modals)\n\nWhen auditing **settings tables** (especially `/settings/global-variables`) and similar AG Grid + modal flows, treat these as Level 1 best practices (2.1.1 / 2.4.3) — not edge-case gotchas.\n\n### Selectable-row keyboard map\n\nReference implementation: `GlobalVariablesPage` + `tests/a11y/global-variables.a11y.spec.ts`.\n\n| Key | Behavior |\n|-----|----------|\n| **Space** | Toggle the row selection checkbox (do **not** open edit) |\n| **Enter** | Open the Update Variable modal for the focused row |\n\nImplementation notes:\n\n- Handle in page-level `onCellKeyDown` only (do not change shared `TableComponent` defaults for other grids unless the product asks for the same map).\n- Add `suppressKeyboardEvent` on that page’s column defs for Enter/Space so AG Grid’s built-in Space selection does not fight the custom handler.\n- Sync React selection state after `node.setSelected` so toolbar delete enablement updates (`TableOptions.hasSelection` is read at render time).\n\n### Modal open/close — retain last position (focus restore)\n\n- Opening edit from a row/cell must remember the focused cell (`rowIndex` + `colId`).\n- Closing the edit modal (Escape, Cancel, or successful save) must restore focus to that same cell via `api.setFocusedCell` + DOM `.focus()`, using a few `requestAnimationFrame`s to outlast Radix dialog focus cleanup.\n- Create modal opened from **Add New** should restore to that trigger (Radix default when a real `DialogTrigger` exists).\n- Verify with a Playwright keyboard test: open from a cell → Escape → `document.activeElement` is still that cell (or its `col-id`), then Enter can open again without a manual mouse re-focus.\n\nFor AG Grid pagination/tab traps, Radix `asChild`, and popover Esc restore details, see [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) (Gotchas vs Best practices sections).\n\n## Out of scope (unless asked)\n\n- IBM Level 2/3 criteria listed as deferred in the criteria reference\n- Section 508 software-only rows (web UI covered via 4.1.2)\n- Media captions (1.2.x) when the surface has no audio/video\n- Fixing violations without an explicit remediation request (report only by default)\n","frontmatter":{"name":"ibm-a11y-level1-audit","description":"Perform a scoped IBM Equal Access Level 1 compliance audit of a chosen Langflow frontend surface (routes, components, or a PR) and produce a findings report mapped to WCAG/IBM Level 1 criteria. Default behavior is audit and report only — fixes are applied only when the user explicitly asks for remediation in the same request. Use when the user asks for an IBM Level 1 audit, a Level 1 compliance report, or to find/report Level 1 WCAG issues on a specific surface. For scanning a batch of routes without a report, see ibm-a11y-route-scan. For scanning and fixing an entire PR/branch end-to-end by default, see ibm-a11y-pr-remediation."},"isInternal":false,"tokens":2476,"sizeBytes":9481},{"name":"SKILL.md","path":".agents/skills/ibm-a11y-pr-remediation/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-pr-remediation/SKILL.md","title":"ibm-a11y-pr-remediation","category":"anthropic-skill","format":"markdown","content":"---\nname: ibm-a11y-pr-remediation\ndescription: Scan every frontend surface touched by the current PR/branch for IBM Equal Access Level 1 accessibility issues and fix all in-scope violations by default. Discovers changed files, maps them to routes/components/states, runs both axe and IBM engines, remediates until both are green (or only documented baselines remain), and reports back. Use when the user asks to check, scan, or clean up accessibility for \"this PR\", \"my branch\", or \"my changes\" and wants fixes applied, not just a report. For a single scoped audit report without a default fix pass, use ibm-a11y-level1-audit; for a route batch scan only, use ibm-a11y-route-scan.\ndisable-model-invocation: true\n---\n\n# IBM Level 1 PR Accessibility Remediation\n\nScope is **IBM Equal Access Level 1 only**. **Default mode is fix, not report-only.** Scan every frontend surface touched by the PR/branch and remediate all in-scope issues until both engines are green (or only documented baselines remain).\n\nThis skill is a PR-scoped orchestrator. It does not duplicate detailed engine/pattern guidance — read the linked skills for that:\n\n- [ibm-a11y-testing-guide](../ibm-a11y-testing-guide/SKILL.md) — which engine/test layer to use, POUR checklist, axe-vs-IBM gaps, Radix/AG-Grid gotchas, baselines.\n- [ibm-a11y-route-scan](../ibm-a11y-route-scan/SKILL.md) — Python scanner options for ad-hoc route batches.\n- [ibm-a11y-level1-audit](../ibm-a11y-level1-audit/SKILL.md) — Level 1 criteria references and report template, useful when the user wants a formal audit report for the PR instead of (or in addition to) fixes.\n- [frontend-i18n](../frontend-i18n/SKILL.md) — accessible names / UI strings must go through i18n.\n\n## Mandate\n\n1. Diff the PR (or current branch vs its merge base) for `src/frontend/**` changes.\n2. Map changed files → UI surfaces → routes / components / states to scan.\n3. Run **both** axe (Jest where applicable) and IBM Equal Access (Playwright `page.runA11yScan` and/or `scripts/a11y/a11y_scan.py` — see `ibm-a11y-testing-guide` / `ibm-a11y-route-scan`).\n4. **Fix every in-scope Level 1 violation** in the changed surfaces. Do not stop at a findings list unless the user says **report only** (in that case, hand off to `ibm-a11y-level1-audit`).\n5. Re-scan until assert mode passes. Add/update a11y specs when coverage is missing.\n6. Report what changed, commands run, and any baselined/deferred debt.\n\nDo **not** invent new tag names, silently disable scans, or expand into IBM Level 2/3 unless asked.\n\n## Progress checklist\n\n```\nIBM L1 PR A11y:\n- [ ] 1. Collect changed frontend files\n- [ ] 2. Map files → surfaces / routes / states\n- [ ] 3. Scan (IBM + axe)\n- [ ] 4. Fix all in-scope violations\n- [ ] 5. Add/update tests if needed\n- [ ] 6. Re-scan assert-green\n- [ ] 7. Report back\n```\n\n## 1. Collect changed frontend files\n\nPrefer the PR merge base when a PR exists; otherwise the branch merge base vs `main`/`master`.\n\n```bash\n# PR number known\ngh pr diff <n> --name-only | grep -E '^src/frontend/' || true\n\n# Current branch vs upstream default\nBASE=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD origin/master)\ngit diff --name-only \"$BASE\"...HEAD -- 'src/frontend/**'\n\n# Include uncommitted work when the user is mid-change\ngit diff --name-only HEAD -- 'src/frontend/**'\ngit diff --name-only --cached -- 'src/frontend/**'\ngit ls-files --others --exclude-standard 'src/frontend/**'\n```\n\nInclude:\n- `src/frontend/src/**/*.{tsx,ts,jsx,js,css}` (UI)\n- `src/frontend/tests/a11y/**` (existing coverage)\n- Locale files only when they change accessible names / labels\n\nSkip pure non-UI churn unless it affects a11y (e.g. test helpers that change focus / ARIA). If **no** frontend files changed, say so and stop.\n\n## 2. Map files → surfaces\n\nFor each changed file, identify:\n\n| Change type | Scan target |\n|-------------|-------------|\n| Page / route | That route + meaningful states (empty/populated/modal/mobile) |\n| Shared component (`TableComponent`, dialogs, menus) | **Every** consumer page that uses it — not only the file you touched |\n| Primitive | Jest axe on the primitive + any Playwright surface that embeds it |\n| Spec / baseline only | Re-run that spec; no product fix unless it fails |\n| `a11y_routes.json` | Update static coverage; run static or route scan |\n\nList interactive controls and states (default, empty, populated, open modal / menu, selected row, error, mobile). Prefer existing specs under `src/frontend/tests/a11y/`.\n\n## 3. Scan (both engines must pass)\n\nAutomated a11y is not one tool — see `ibm-a11y-testing-guide` for the full engine comparison and gotchas. Summary:\n\n- **axe-core** — Jest `axe()` (`@/utils/a11y-test`), jsdom-only.\n- **IBM Equal Access** — stricter on ARIA structure and keyboard semantics. Playwright `page.runA11yScan(label)` for stateful surfaces (modals/menus/selected/editing); `scripts/a11y/a11y_scan.py` for default-loaded page only.\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# Python scanner playwright deps are NOT in default uv sync.\n# One-time: uv run --with playwright playwright install chromium\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes /settings/<route> \\\n  --out /tmp/a11y.json --markdown /tmp/a11y.md --timeout-ms 45000\n```\n\n`RUN_A11Y=true` runs the scan; `RUN_A11Y_ASSERT=true` fails on new violations. After changing a shared component, re-scan every page that uses it.\n\nFor component-only changes:\n\n```bash\ncd src/frontend\nnpx jest path/to/<name>.a11y.test.tsx --runInBand\n```\n\nDo not invent findings — prefer scanner output plus manual Level 1 spot checks scanners miss (keyboard trap both ways, focus restore, 320px reflow, color-not-only).\n\n## 4. Fix all in-scope Level 1 violations\n\nDefault: **fix**. Only list proposed fixes without editing if the user asked **report only** — then hand off to `ibm-a11y-level1-audit` for the formal report format.\n\nRules:\n- Prefer semantic HTML over ARIA.\n- Follow the Langflow patterns in `ibm-a11y-testing-guide` (AG Grid, Radix `asChild`, focus restore, icon-only `aria-label`).\n- Route new UI strings / `aria-label`s through i18n (`t(...)`, all locale files) per `frontend-i18n`.\n- Keep fixes minimal; do not refactor unrelated UI.\n- Do **not** silently disable scans. Use IBM baselines under `src/frontend/tests/a11y/baselines/` only for documented framework debt.\n- Map each issue to a Level 1 WCAG/IBM id; defer anything listed as Level 2/3 in the criteria guide (`ibm-a11y-level1-audit/references/ibm-level1-criteria.md`) unless the user expands scope.\n\n### Manual Level 1 spot checks (when relevant)\n\n- **2.1.1 / 2.1.2:** Tab and Shift+Tab; Escape closes overlays; no trap.\n- **2.4.3 / 2.4.7:** Focus order matches visual order; focus ring visible.\n- **1.4.10:** 320px / ~400% zoom — no essential horizontal scroll.\n- **1.4.1:** Status/errors not color-only.\n- **3.3.1 / 3.3.2:** Errors in text and tied to fields; inputs labeled.\n\n## 5. Tests / coverage\n\n| Surface | Spec |\n|---------|------|\n| Static routes | `static-routes.a11y.spec.ts` (+ `scripts/a11y/a11y_routes.json`) |\n| Auth | `auth-pages.a11y.spec.ts` |\n| Core pages | `core-pages.a11y.spec.ts` |\n| Data-rich | `files.a11y.spec.ts`, `api-keys.a11y.spec.ts`, `global-variables.a11y.spec.ts` |\n| Other data-rich | `data-rich-routes.a11y.spec.ts` |\n\nIf you fixed a state with no scan, add one (and keyboard tests for custom keyboard behavior) following `files.a11y.spec.ts` / `api-keys.a11y.spec.ts` patterns.\n\nTag every Playwright a11y test `@release` plus a domain tag (`@workspace` / `@api` / `@database` / `@components` / `@starter-projects`). Import `test`/`expect` from `../fixtures`.\n\n## 6. Re-scan and report\n\nRe-run the same scans with `RUN_A11Y_ASSERT=true`. Each finding → `fixed` | `baselined` | `open`.\n\nWhen done, state:\n- Changed frontend files considered\n- Surfaces / states scanned\n- Fixes applied (files + what)\n- Commands run — whether **both** axe and IBM ran and both reported zero\n- Specs / baselines added or updated\n- States skipped and why\n- Remaining risk or accepted limitation\n","frontmatter":{"name":"ibm-a11y-pr-remediation","description":"Scan every frontend surface touched by the current PR/branch for IBM Equal Access Level 1 accessibility issues and fix all in-scope violations by default. Discovers changed files, maps them to routes/components/states, runs both axe and IBM engines, remediates until both are green (or only documented baselines remain), and reports back. Use when the user asks to check, scan, or clean up accessibility for \"this PR\", \"my branch\", or \"my changes\" and wants fixes applied, not just a report. For a single scoped audit report without a default fix pass, use ibm-a11y-level1-audit; for a route batch scan only, use ibm-a11y-route-scan.","disable-model-invocation":true,"disableModelInvocation":true},"isInternal":false,"tokens":2213,"sizeBytes":8224},{"name":"SKILL.md","path":".agents/skills/ibm-a11y-route-scan/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-route-scan/SKILL.md","title":"ibm-a11y-route-scan","category":"anthropic-skill","format":"markdown","content":"---\nname: ibm-a11y-route-scan\ndescription: Batch-scan Langflow frontend routes for accessibility issues using the Python IBM Equal Access scanner (scripts/a11y/a11y_scan.py) and produce JSON/Markdown/HTML reports. Scans the default-loaded state of each route only (use explicit state files for modals). Use when asked to scan one or more routes, produce an accessibility report for pages, or batch-check static routes. Reports findings only — does not fix code, run Playwright/axe tests, or perform a formal Level 1 audit; see ibm-a11y-testing-guide, ibm-a11y-level1-audit, and ibm-a11y-pr-remediation for those.\n---\n\n# IBM Route Scanner\n\nUse this skill when asked to scan Langflow frontend pages for accessibility issues with the local Python scanner. This skill only reports — it does not modify code. For test-writing guidance, formal audits, or PR-wide fixes, see `ibm-a11y-testing-guide`, `ibm-a11y-level1-audit`, and `ibm-a11y-pr-remediation`.\n\n## Scanner\n\nUse the Python script:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-report.json \\\n  --markdown /tmp/langflow-a11y-report.md \\\n  --html /tmp/langflow-a11y-report.html \\\n  --timeout-ms 45000\n```\n\nScript options:\n\n- `--url`: base app URL, usually `http://localhost:3000`.\n- `--routes-file`: route manifest JSON file. Prefer `scripts/a11y/a11y_routes.json`.\n- `--route-group`: manifest group to scan. Default: `static`.\n- `--routes`: comma-separated route paths to scan.\n- `--route`: one route path; can be repeated instead of `--routes`.\n- `--levels`: comma-separated issue levels. Default: `violation`.\n- `--out`: JSON report path.\n- `--markdown`: optional Markdown report path.\n- `--html`: optional self-contained HTML report path.\n- `--timeout-ms`: per-route timeout.\n- `--quiet-ms`: network quiet window before scanning. Default: `1000`.\n- `--states-file`: JSON file with explicit modal/state actions.\n- `--headed`: show browser while scanning.\n\n## Route Selection\n\nUse `scripts/a11y/a11y_routes.json` as the source of truth for route selection.\n\nThe normal CI/local batch is the manifest `static` group. Prefer that unless the user asks for custom, dynamic, or gated routes.\n\nCommon manifest-backed command:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-static.json \\\n  --markdown /tmp/langflow-a11y-static.md \\\n  --html /tmp/langflow-a11y-static.html\n```\n\nDynamic routes need real IDs before scanning:\n\n- `/flow/:id/`\n- `/flow/:id/view`\n- `/playground/:id/`\n- `/assets/knowledge-bases/:sourceId/chunks`\n\nFor dynamic routes, get IDs from the loaded app, API responses, or existing test data before replacing placeholders.\n\n## Examples\n\nScan one route:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --route /flows \\\n  --out /tmp/langflow-a11y-flows.json\n```\n\nScan multiple routes:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --out /tmp/langflow-a11y-report.json \\\n  --markdown /tmp/langflow-a11y-report.md \\\n  --html /tmp/langflow-a11y-report.html\n```\n\nScan more than violations:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes-file scripts/a11y/a11y_routes.json \\\n  --route-group static \\\n  --levels violation,potentialviolation,recommendation \\\n  --out /tmp/langflow-a11y-expanded.json\n```\n\nScan route plus modal states:\n\n```bash\nuv run python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --states-file /tmp/langflow-a11y-states.json \\\n  --out /tmp/langflow-a11y-modal-report.json \\\n  --markdown /tmp/langflow-a11y-modal-report.md \\\n  --html /tmp/langflow-a11y-modal-report.html \\\n  --timeout-ms 45000\n```\n\nState file shape:\n\n```json\n[\n  {\n    \"route\": \"/settings/global-variables\",\n    \"states\": [\n      {\n        \"name\": \"new-global-variable-modal\",\n        \"open\": [\n          { \"click\": \"[data-testid='api-key-button-store']\" },\n          { \"waitFor\": \"[role='dialog']\" }\n        ],\n        \"close\": [\n          { \"press\": \"Escape\" },\n          { \"waitForHidden\": \"[role='dialog']\" }\n        ]\n      }\n    ]\n  }\n]\n```\n\nSupported state actions:\n\n- `{ \"click\": \"<css selector>\" }`\n- `{ \"clickText\": \"<visible text>\" }`\n- `{ \"clickRole\": { \"role\": \"button\", \"name\": \"Create\" } }`\n- `{ \"fill\": { \"selector\": \"<css selector>\", \"value\": \"text\" } }`\n- `{ \"press\": \"Escape\" }`\n- `{ \"press\": { \"selector\": \"<css selector>\", \"key\": \"Enter\" } }`\n- `{ \"waitFor\": \"<css selector>\" }`\n- `{ \"waitForHidden\": \"<css selector>\" }`\n- `{ \"waitForText\": \"<visible text>\" }`\n- `{ \"wait\": 500 }`\n- `{ \"scroll\": { \"selector\": \"<css selector>\", \"to\": \"bottom\" } }` — `to` is `top` or\n  `bottom`; use `\"by\": <pixels>` for a relative scroll, and omit `selector` to scroll the\n  page. Needed for virtualised grids whose tabbable rows only disappear after scrolling.\n\n## Report\n\nThe scanner always writes JSON. It can also write Markdown and HTML for presentation.\n\nUse JSON for exact data. Use Markdown for PR comments or issues. Use HTML when the user wants a browsable report.\n\nSummarize:\n\n- report path\n- Markdown/HTML report paths, when generated\n- total issue count\n- per-route issue count\n- per-route API request count\n- per-route request failure count\n- top rule IDs\n\nUse report fields directly:\n\n- `totalIssues`\n- `results[].route`\n- `results[].state`\n- `results[].phase`\n- `results[].apiRequests`\n- `results[].requestFailures`\n- `results[].diagnostics`\n- `results[].issues[].ruleId`\n\n## Rules\n\n- Use only scanner output for findings.\n- Do not invent route names. Read `routes.tsx`.\n- Do not auto-click arbitrary buttons to find modals. Use explicit state actions.\n- Avoid destructive modal actions unless the user explicitly asks and data is safe.\n- If a route has zero API requests, mention that scan quality may be limited.\n- This skill reports only; do not edit files. If the user also wants fixes applied, hand off to `ibm-a11y-pr-remediation` or `ibm-a11y-level1-audit`.\n","frontmatter":{"name":"ibm-a11y-route-scan","description":"Batch-scan Langflow frontend routes for accessibility issues using the Python IBM Equal Access scanner (scripts/a11y/a11y_scan.py) and produce JSON/Markdown/HTML reports. Scans the default-loaded state of each route only (use explicit state files for modals). Use when asked to scan one or more routes, produce an accessibility report for pages, or batch-check static routes. Reports findings only — does not fix code, run Playwright/axe tests, or perform a formal Level 1 audit; see ibm-a11y-testing-guide, ibm-a11y-level1-audit, and ibm-a11y-pr-remediation for those."},"isInternal":false,"tokens":1661,"sizeBytes":6224},{"name":"SKILL.md","path":".agents/skills/ibm-a11y-testing-guide/SKILL.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/ibm-a11y-testing-guide/SKILL.md","title":"ibm-a11y-testing-guide","category":"anthropic-skill","format":"markdown","content":"---\nname: ibm-a11y-testing-guide\ndescription: Reference guide for writing and running Langflow frontend accessibility tests with axe (Jest) and IBM Equal Access (Playwright `page.runA11yScan`). Covers which engine/test layer to pick, the POUR checklist, axe-vs-IBM rule gaps, Radix/AG-Grid component gotchas, and IBM baselines. Use when writing or reviewing a11y tests, debugging a specific axe/IBM violation, or deciding which test layer fits a UI surface. Does not run scans, audit a whole surface, or fix a PR end-to-end — see ibm-a11y-route-scan, ibm-a11y-level1-audit, and ibm-a11y-pr-remediation for those.\n---\n\n# IBM/axe Accessibility Testing Guide\n\nReference material for **how** to test a given UI surface. This skill does not decide what to scan or drive a fix loop by itself — use it while writing tests, debugging a violation, or implementing a fix identified by `ibm-a11y-route-scan`, `ibm-a11y-level1-audit`, or `ibm-a11y-pr-remediation`.\n\n## When To Use\n\nUse this skill for frontend work under `src/frontend` when the change affects:\n\n- UI components, primitives, pages, routes, dialogs, drawers, popovers, dropdowns, tabs, accordions, tables, forms, navigation, or canvas controls.\n- ARIA attributes, labels, roles, focus management, tab order, keyboard interaction, disabled states, loading states, or error states.\n- A request to write, fix, or review an axe/IBM accessibility test.\n\nUse alongside:\n\n- `frontend-testing` for Jest/React Testing Library patterns.\n- `e2e-testing` for Playwright patterns.\n- `frontend-i18n` when adding or changing user-facing labels, accessible names, `aria-label`, tooltips, or visible strings.\n- `ibm-a11y-route-scan` to batch-scan routes with the Python scanner and produce Markdown/HTML reports.\n- `ibm-a11y-level1-audit` for a scoped IBM Level 1 compliance audit and report.\n- `ibm-a11y-pr-remediation` to scan and fix an entire PR/branch end-to-end.\n\n## Goal\n\nDo not stop at \"run axe on default render.\" Inspect the changed UI and cover every meaningful user-visible state, for both keyboard and screen-reader users, that can expose accessibility bugs.\n\n## The Bar: two engines, both must pass\n\nAutomated a11y is not one tool. Run BOTH — they catch different classes of bug:\n\n- **axe-core** — Jest `axe()` (`@/utils/a11y-test`), jsdom-only. Fast; strong on contrast, labels, roles, ARIA basics.\n- **IBM Equal Access** — the stricter engine on ARIA structure and keyboard semantics; catches real WCAG Level-1 issues axe silently passes. Two entry points, **same engine** (`aChecker`):\n  - Playwright **`page.runA11yScan(label)`** — runs `aChecker.getCompliance` in-browser on the **live DOM**, so it scans open modals / menus / selected / editing states. This is IBM, **not** axe (despite the name).\n  - `scripts/a11y/a11y_scan.py` (see `ibm-a11y-route-scan`) — route scanner for the default-loaded page only.\n\nA page that is green on axe is NOT done. When the change touches a table/tree/listbox/menu/composite widget, IBM is mandatory. Prefer `page.runA11yScan` for stateful surfaces (it sees the DOM after your interactions); use the Python scanner for a quick default-load route check.\n\nVerify commands:\n\n```bash\ncd src/frontend\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\n\n# The Python scanner's playwright deps are NOT in the default `uv sync`.\n# One-time: `uv run --with playwright playwright install chromium`\nuv run --with playwright python scripts/a11y/a11y_scan.py \\\n  --url http://localhost:3000 \\\n  --routes /settings/<route> \\\n  --out /tmp/a11y.json --markdown /tmp/a11y.md --timeout-ms 45000\n```\n\n`RUN_A11Y=true` runs the scan; add `RUN_A11Y_ASSERT=true` to actually fail on new violations (without it the scan is informational). Both engines must report zero. After changing a shared component, re-scan every page that uses it, not just the one you touched.\n\n## First Pass\n\n1. Read the changed files and nearby tests.\n2. Identify the UI surface:\n   - Primitive/component only\n   - Composed component\n   - Page/route\n   - Stateful workflow\n   - Shared helper that changes labels, roles, tab order, or focus behavior\n3. List interactive surfaces:\n   - Buttons, links, inputs, checkboxes, switches, radios, selects, comboboxes\n   - Dropdown menus, popovers, dialogs, drawers, tooltips\n   - Tabs, accordions, tables/treegrids, row actions, pagination\n   - Keyboard-only interactions and focus traps\n4. List states worth scanning:\n   - Default, populated, empty, loading, error/validation visible\n   - Disabled/read-only, selected/expanded/open\n   - Modal/dropdown/popover open\n   - Mobile viewport when layout changes\n\n## What To Check (POUR checklist)\n\n### Perceivable\n- Non-text content has a text alternative; decorative images are hidden from AT.\n- Text contrast ≥ 4.5:1; non-text (icons, borders, focus rings) ≥ 3:1.\n- Layout reflows at 320px / 400% zoom without horizontal scroll or loss of content.\n\n### Operable (keyboard is the big one)\n- Every control is reachable AND operable by keyboard (Enter/Space activate; arrows where expected).\n- Focus is always visible.\n- Focus order is logical and matches visual order.\n- **No keyboard trap — test Shift+Tab too, not just Tab.** Focus can always move out of a widget both ways.\n- Disabled controls are not in the tab order.\n- Composite widgets (grid/tree/listbox/menu/tablist) are ONE logical tab stop with a roving `tabindex`; arrows move within.\n- After async transitions (route change, panel open/close) focus lands somewhere useful, never silently on `<body>`.\n\n### Understandable\n- Inputs have programmatic labels; visible label matches accessible name.\n- Errors are identified in text and associated with their field; give a suggestion when possible.\n- The same control is identified consistently across the app.\n\n### Robust (name / role / value)\n- Interactive elements expose a correct role, name, and state.\n- Valid ARIA parent/child (e.g. `rowgroup` owns `row`, `list` owns `listitem`).\n- No tabbable element with a non-widget role (`presentation`/`none`).\n- `aria-hidden=\"true\"` never wraps a focusable element.\n\n## axe vs IBM: known gaps\n\nThings axe passes but IBM flags (all real WCAG Level 1):\n\n| IBM rule | WCAG | Typical cause |\n| --- | --- | --- |\n| `element_tabbable_role_valid` | 4.1.2 | tabbable element with `role=\"presentation\"` / no widget role (focus sentinels, wrappers) |\n| `aria_child_valid` | 1.3.1 | `rowgroup`/`list`/`tablist` owning no valid child role |\n| `aria_child_tabbable` | 2.1.1 / 4.1.2 | composite widget with no tabbable descendant (needs roving `tabindex`) |\n| `aria_hidden_focus_misuse` | 4.1.2 | focusable element inside `aria-hidden=\"true\"` |\n| `aria_accessiblename_exists` | 4.1.2 | element with a widget role but no accessible name — e.g. an AG Grid `columnheader` with neither `field` nor `headerName` (icon-only action column) |\n| `aria_content_in_landmark` | 1.3.1 | content outside any landmark — e.g. a Radix menu/popover portaled to `<body>`. `role=\"menu\"` is NOT landmark-exempt; only `aria-modal` dialogs are. Often unfixable app-wide → baseline it (see below) |\n\nRule of thumb: touch a table/tree/listbox/menu → scan with IBM.\n\n## Component Gotchas\n\n- **Data grids (AG Grid — the shared `TableComponent`)**: the grid's own tab guards + pagination are the fragile part.\n  - Disabled paging buttons must be `tabindex=\"-1\"`, but NEVER `inert`/`disabled` — that breaks AG Grid's tab guards and traps reverse (Shift+Tab) entry (WCAG 2.1.2).\n  - AG Grid still *programmatically* focuses disabled paging on tab-out; handle with its `tabToNextCell` hook plus a container-scoped `focusin` redirect (defer the redirect to `requestAnimationFrame` — focus changes inside a `focusin` handler are ignored, and AG Grid restores the last-focused cell on the next tick).\n  - Empty rowgroups need `role=\"presentation\"`; the grid needs one roving-tabindex row.\n  - **Icon-only / action column header**: AG Grid derives the `columnheader` accessible name from `field`; a column with neither `field` nor `headerName` is nameless (`aria_accessiblename_exists`, 4.1.2). Give it a `headerName` and hide it visually with a `headerClass` (sr-only clip on `.ag-header-cell-text`) so the header stays blank but named.\n  - **Interactive control inside a cell** (menu trigger, button): AG Grid navigates **cell-by-cell** and never tab-focuses the inner control, so it's keyboard-inoperable. Activate it from the grid's `onCellKeyDown` on Enter/Space (give the column a `colId` to target it). A **Radix trigger opens on keydown/pointerdown, not a synthetic `.click()`** — focus the trigger and **re-dispatch the key** (`new KeyboardEvent(\"keydown\", {key, bubbles:true})`), guarding against re-entry (skip when `target` is already the button). `TableComponent` spreads `{...props}` to AG Grid, so pass `onCellKeyDown` from the page — no shared-component change.\n  - **Focus visible on borderless grids** (`.ag-no-border`): the theme suppresses the cell focus outline (`outline:none`), hiding keyboard focus (2.4.7). Restore it with a `:focus-visible` ring scoped to `.ag-no-border .ag-cell:focus-visible` (+ header/row). `:focus-visible` **distinguishes modality on AG cells** — mouse click resolves to `:focus`, keyboard nav to `:focus-visible` — so the ring shows for keyboard only and mouse stays quiet. CAVEAT when verifying: probe with a **clean, first-interaction** page; a session that already used the keyboard makes `:focus-visible` report `true` for a later mouse click (heuristic contamination).\n  - Set `ensureDomOrder: true` so DOM order matches visual order for tab navigation.\n  - `TableComponent` is shared across settings/traces — fix once in the grid patch, then re-scan every grid page. Its pagination/tab-out rework lives in LE-1720 / PR #13953; don't duplicate it per-page.\n- **Modals/drawers**: title + description, focus trapped inside while open, focus restored to the trigger on close, Escape closes.\n- **Dropdowns/popovers**: reachable by keyboard, Escape closes, focus returns to the trigger.\n- **Radix (shadcn) primitives** — three recurring traps:\n  - **`Trigger asChild` double tab stop**: a `DialogTrigger`/`DropdownMenuTrigger` wrapping a real `<button>` **without `asChild`** makes Radix render its *own* `<button>` around it → nested buttons → two consecutive tab stops (2.4.3). Always pass `asChild` when the child is already an interactive element. (Watch the trigger wrapper's own `asChild={cond}` default — `DeleteConfirmationModal` needs an explicit `asChild`.)\n  - **Focus-restore race**: on close, Radix restores focus to its trigger **once, asynchronously**. If the menu action programmatically moves focus elsewhere (e.g. `startEditingCell` opens an inline editor), Radix steals it back. Re-assert focus on your target across a few `requestAnimationFrame`s to outlast the one-time restore. For rename-style flows, `.select()` the input too.\n  - **Portaled menu landmark**: `DropdownMenu`/`Popover` content portals to `<body>`, tripping `aria_content_in_landmark`. Portaling into `<main>` is not an option when `<main>` is `overflow-hidden` (clips the menu). Treat as app-wide debt → **baseline it** (below); the menu's real a11y (role, named trigger, keyboard open/close, focus restore) is covered by a keyboard test instead.\n- **Disabled controls**: keep them out of the tab order. Only use `aria-disabled` (instead of native `disabled`) when the control must stay discoverable, and keep it non-operable.\n- **Icon-only buttons**: real `<button>` with an `aria-label`.\n- **Custom clickable divs/cells**: prefer a semantic element; if a cell/card must open something it needs Enter/Space handling, not just `onClick`.\n\n## Best practices (settings grids + modals)\n\nApply these when building or reviewing AG Grid tables that open an edit modal (reference: `GlobalVariablesPage`, `tests/a11y/global-variables.a11y.spec.ts`):\n\n- **Keyboard map on selectable rows:** **Space** toggles the row checkbox; **Enter** opens the row’s edit/detail UI. Do not use Space to open the modal.\n- **Scope the map on the page:** implement with page-level `onCellKeyDown` and per-column `suppressKeyboardEvent` for Enter/Space so AG Grid’s default Space handling does not compete. Prefer not changing shared `TableComponent` defaults unless the same map is product-wide.\n- **Keep selection UI in sync:** after `node.setSelected`, update React selection state so toolbar actions (e.g. delete) enable/disable correctly (`TableOptions.hasSelection` is read at render time).\n- **Retain place on modal close (2.4.3):** remember the focused cell (`rowIndex` + `colId`) when opening edit; on close (Escape, Cancel, or save) restore with `api.setFocusedCell` + DOM `.focus()`, using a few `requestAnimationFrame`s to outlast Radix dialog cleanup. Controlled edit dialogs without a Radix trigger need this explicitly. Create flows that use a real `DialogTrigger` (e.g. Add New) should restore to that trigger.\n- **Verify with keyboard tests:** open from a cell → Escape → focus is still on that cell; Enter can open again without a mouse re-click.\n\n## Choose Test Layer\n\n### Jest + axe\n\nUse Jest when the changed surface is a primitive or component that renders in jsdom without full app routing.\n\n```tsx\nimport { render, screen } from \"@testing-library/react\";\nimport { axe } from \"@/utils/a11y-test\";\n\nit(\"should_have_no_axe_violations\", async () => {\n  const { container } = render(<Component />);\n  expect(await axe(container)).toHaveNoViolations();\n});\n```\n\nNotes: `a11y-test.ts` disables `color-contrast` (jsdom can't measure layout). Prefer role/label/text queries. Assert accessible names explicitly for icon-only / visually-hidden / generated labels. Put tests at `__tests__/<name>.a11y.test.tsx`.\n\n```bash\ncd src/frontend\nnpx jest path/to/<name>.a11y.test.tsx --runInBand\n```\n\n### Playwright + `page.runA11yScan`\n\nUse Playwright when the surface needs real browser layout, routing, app state, mocking, modals, tables, focus order, keyboard behavior, or full-page interactions.\n\n```ts\nimport { expect, test } from \"../fixtures\";\nimport { awaitBootstrapTest } from \"../utils/await-bootstrap-test\";\n\ntest(\"scans populated state\", { tag: [\"@release\", \"@api\"] }, async ({ page }) => {\n  await awaitBootstrapTest(page, { skipModal: true });\n  await page.goto(\"/settings/example\");\n  await expect(page.getByText(\"Example\")).toBeVisible();\n  await page.runA11yScan(\"settings-example-populated\");\n});\n```\n\nRules: put specs under `tests/a11y/<feature>.a11y.spec.ts`; import `test`/`expect` from `../fixtures`; tag every test `@release` plus a domain tag (`@workspace`/`@api`/`@database`/`@components`/`@starter-projects`); stable scan names (lowercase, feature-first, state-specific); mock API for deterministic states; disable animations for focus assertions; use explicit interactions (no random clicking of destructive controls). If the route belongs in static coverage, update `scripts/a11y/a11y_routes.json`.\n\n```bash\ncd src/frontend\nRUN_A11Y=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\nRUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5\nnpm run a11y:html-report --silent\n```\n\n## IBM baselines (accepting known debt)\n\nSome IBM violations are real but framework-level and can't be fixed per-page (e.g. `aria_content_in_landmark` on a Radix menu portal). Track them with an IBM baseline so the scan stays green while the debt is recorded — do NOT silently drop the scan or the assertion.\n\nHow it works (`fixtures.ts` + `.achecker.yml`):\n\n- `baselineFolder: tests/a11y/baselines`. The baseline file name is the **scan label**: `{project}__{label}.json`, e.g. `chromium__assets-files-actions-menu.json` (dashes preserved; the first scan in a test has no index suffix).\n- On each run IBM's `filterReport` loads that file and marks any result matching a baseline entry by **`path.dom` + `ruleId` + `reasonId`** as `ignored: true`. `countNewA11yViolations` counts `violation && !ignored`, so baselined issues don't fail.\n\nCreate one:\n\n1. Run the scan once — it writes the full report to `coverage/accessibility-reports/{label}.json`.\n2. Copy the offending result(s) into `tests/a11y/baselines/{label}.json` as `{ \"results\": [ { \"ruleId\", \"reasonId\", \"path\": { \"dom\": … } } ] }`. A **minimal** baseline (only the entries to ignore) is clearest; add a `description` field noting why and how to resurface it.\n3. Re-run → the scan passes. Commit the baseline so the debt is tracked; **deleting it resurfaces the violation**. Point at the baseline from a comment in the spec.\n\nMatching is exact-DOM-path, so baselines can go stale if unrelated `<body>`-level portals (toasts) shift the portal index — scan the state with no other overlays open.\n\n## Route/Page Coverage Matrix\n\nFor route/page changes, cover the smallest matrix representing real user states:\n\n- Loaded/default, empty data, populated data\n- Primary modal open, dropdown/popover open\n- Validation or error visible\n- Selected/expanded table row or bulk-action state\n- Mobile viewport when responsive layout changes\n\nDon't force states the page can't enter; briefly explain any skipped state.\n\n## Data-Rich Route Pattern\n\nUse `tests/a11y/api-keys.a11y.spec.ts` and `tests/a11y/files.a11y.spec.ts` as the models for route-level work: mock API data; scan populated table; scan empty table; open create modal and scan; submit to the generated-result modal and scan; open a text-cell modal and scan; select a row and scan the selected state; set a mobile viewport and scan. Add keyboard-interaction tests (not scans) for tab-out, reverse re-entry, Enter-to-open, focus restore, and focus-visible where the surface has custom keyboard behavior. This is the expected bar for data-rich routes.\n\n**Driving grid row states**: client-rendered states (upload progress %, upload-failed/error, disabled rows) are usually driven by fields the cell renderer reads off the row (`params.data.*`). If the list query returns the response untransformed, just inject the field into the mocked rows (e.g. `{ ...file, progress: -1 }`) to scan the error/progress state — no need to simulate the real flow.\n\n## Report Back\n\nWhen done, state:\n\n- Test files added or updated, and states scanned.\n- Commands run — including whether BOTH axe and IBM were run and both reported zero.\n- Any states not covered and why.\n- Any remaining a11y risk or accepted limitation.\n","frontmatter":{"name":"ibm-a11y-testing-guide","description":"Reference guide for writing and running Langflow frontend accessibility tests with axe (Jest) and IBM Equal Access (Playwright `page.runA11yScan`). Covers which engine/test layer to pick, the POUR checklist, axe-vs-IBM rule gaps, Radix/AG-Grid component gotchas, and IBM baselines. Use when writing or reviewing a11y tests, debugging a specific axe/IBM violation, or deciding which test layer fits a UI surface. Does not run scans, audit a whole surface, or fix a PR end-to-end — see ibm-a11y-route-scan, ibm-a11y-level1-audit, and ibm-a11y-pr-remediation for those."},"isInternal":false,"tokens":4571,"sizeBytes":18516},{"name":"docs_development.mdc","path":".cursor/rules/docs_development.mdc","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.cursor/rules/docs_development.mdc","title":"Cursor Rule: docs_development","category":"cursor-rule","format":"mdc","content":"# Cursor Rule: docs_development\n\nPath: `.cursor/rules/docs_development.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.cursor/rules/docs_development.mdc)","tokens":300,"sizeBytes":201},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Claude Agent Guidelines & System Prompt\n\nPath: `CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/CLAUDE.md)","tokens":300,"sizeBytes":161},{"name":"ANTI-PATTERNS.md","path":"docs/agents/ANTI-PATTERNS.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/ANTI-PATTERNS.md","title":"Subagent: anti-patterns","category":"subagent-persona","format":"markdown","content":"# Subagent: anti-patterns\n\nPath: `docs/agents/ANTI-PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/ANTI-PATTERNS.md)","tokens":300,"sizeBytes":183},{"name":"ARCHITECTURE.md","path":"docs/agents/ARCHITECTURE.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/ARCHITECTURE.md","title":"Subagent: architecture","category":"subagent-persona","format":"markdown","content":"# Subagent: architecture\n\nPath: `docs/agents/ARCHITECTURE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/ARCHITECTURE.md)","tokens":300,"sizeBytes":180},{"name":"COMPONENTS.md","path":"docs/agents/COMPONENTS.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/COMPONENTS.md","title":"Subagent: components","category":"subagent-persona","format":"markdown","content":"# Subagent: components\n\nPath: `docs/agents/COMPONENTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/COMPONENTS.md)","tokens":300,"sizeBytes":174},{"name":"CONTRACTS.md","path":"docs/agents/CONTRACTS.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/CONTRACTS.md","title":"Subagent: contracts","category":"subagent-persona","format":"markdown","content":"# Subagent: contracts\n\nPath: `docs/agents/CONTRACTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/CONTRACTS.md)","tokens":300,"sizeBytes":171},{"name":"PHILOSOPHY.md","path":"docs/agents/PHILOSOPHY.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/PHILOSOPHY.md","title":"Subagent: philosophy","category":"subagent-persona","format":"markdown","content":"# Subagent: philosophy\n\nPath: `docs/agents/PHILOSOPHY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/PHILOSOPHY.md)","tokens":300,"sizeBytes":174},{"name":"TESTING.md","path":"docs/agents/TESTING.md","rawUrl":"https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/TESTING.md","title":"Subagent: testing","category":"subagent-persona","format":"markdown","content":"# Subagent: testing\n\nPath: `docs/agents/TESTING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/docs/agents/TESTING.md)","tokens":300,"sizeBytes":165}],"systemPromptSnippet":"<yakaai_skills repo=\"langflow-ai/langflow\">\n<!-- File: AGENTS.md (Tokens: ~2792 | Category: root-instruction) -->\n# AGENTS.md\n\nThis file provides guidance to AI coding agents when working with code in this repository.\n\n## Project Overview\n\nLangflow is a visual workflow builder for AI-powered agents. It has a Python/FastAPI backend, React/TypeScript frontend, and a lightweight executor CLI (lfx).\n\n## Prerequisites\n\n- **Python:** 3.10-3.14\n- **uv:** >=0.4 (Python package manager)\n- **Node.js:** >=20.19.0 (v22.12 LTS recommended)\n- **npm:** v10.9+\n- **make:** For build coordination\n\n## Common Commands\n\n### Development Setup\n```bash\nmake init              # Install all dependencies + pre-commit hooks\nmake run_cli           # Build and run Langflow (http://localhost:7860)\nmake run_clic          # Clean build and run (use when frontend issues occur)\n```\n\n### Development Mode (Hot Reload)\n```bash\nmake backend           # FastAPI on port 7860 (terminal 1)\nmake frontend          # Vite dev server on port 3000 (terminal 2)\n```\n\nFor component development, enable dynamic loading:\n```bash\nLFX_DEV=1 make backend                    # Load all components dynamically\nLFX_DEV=mistral,openai make backend       # Load only specific modules\n```\n\n### Code Quality\n```bash\nmake format_backend    # Format Python (ruff) - run FIRST before lint\nmake format_frontend   # Format TypeScript (biome)\nmake format            # Both\nmake lint              # mypy type checking\n```\n\n### Testing\n```bash\nmake unit_tests                    # Backend unit tests (pytest, parallel)\nmake unit_tests async=false        # Sequential tests\nuv run pytest path/to/test.py      # Single test file\nuv run pytest path/to/test.py::test_name  # Single test\n\nmake test_frontend                 # Jest unit tests\nmake tests_frontend                # Playwright e2e tests\n```\n\n### Database Migrations\n```bash\nmake alembic-revision message=\"Description\"  # Create migration\nmake alembic-upgrade                         # Apply migrations\nmake alembic-downgrade                       # Rollback one version\n```\n\n## Architecture\n\n### Monorepo Structure\n```\nsrc/\n├── backend/\n│   ├── base/langflow/     # Core backend package (langflow-base)\n│   │   ├── api/           # FastAPI routes (v1/, v2/)\n│   │   ├── components/    # Built-in Langflow components\n│   │   ├── services/      # Service layer (auth, database, cache, etc.)\n│   │   ├── graph/         # Flow graph execution engine\n│   │   └── custom/        # Custom component framework\n│   └── tests/             # Backend tests\n├── frontend/              # React/TypeScript UI\n│   └── src/\n│       ├── components/    # UI components\n│       ├── stores/        # Zustand state management\n│       └── icons/         # Component icons\n├── langflow-core/         # Usable provider-free Langflow distribution\n├── bundles/               # Curated provider integrations\n└── lfx/                   # Lightweight executor and shared primitives\n```\n\n### Key Packages\n- **langflow**: Full end-user package; depends on `langflow-core` and curated provider bundles\n- **langflow-core**: Service-complete, provider-bundle-free distribution; owns the `langflow` CLI\n- **langflow-base**: Modular application platform (API, services, graph engine); extras add service integrations\n- **lfx**: Shared execution primitives and standalone CLI (`lfx serve`, `lfx run`)\n\nThe public dependency direction is `langflow → langflow-core → langflow-base → lfx`.\nProvider packages under `src/bundles/` are added only by the full `langflow` distribution.\n\n### Service Layer\nBackend services in `src/backend/base/langflow/services/`:\n- `auth/` - Authentication\n- `authorization/` - Authorization (RBAC) plugin layer — see below\n- `database/` - SQLAlchemy models and migrations\n- `cache/` - Caching layer\n- `storage/` - File storage\n- `tracing/` - Observability integrations\n\n### Authorization (RBAC)\n\nAuthorization is a pluggable layer separate from authentication:\n\n- **OSS** ships the interface (`BaseAuthorizationService` in `lfx`) + a pass-through implementation (`LangflowAuthorizationService`) + the `authz_*` and `casbin_rule` DB schema + route guards.\n- Implementations register via the `lfx.services` entry point `authorization_service` in `lfx.toml` (same pattern as the SSO `auth_service`). A registered plugin reads the `authz_*` admin tables and writes compiled rules to `casbin_rule`.\n\nDefault is **off**: `LANGFLOW_AUTHZ_ENABLED=false`. When enabled with only the OSS stub registered, every check returns allow — the stub is a no-op so routes stay wired and audit rows still flow. Real allow/deny requires a registered authorization plugin.\n\nRoute guards live in `langflow.services.authorization.guards` (the legacy `langflow.services.authorization.utils` path re-exports them for backward compatibility):\n- `ensure_flow_permission(user, FlowAction.*, flow_id=..., flow_user_id=..., workspace_id=..., folder_id=...)` — single-flow CRUD + execute\n- `ensure_deployment_permission(user, DeploymentAction.*, deployment_id=..., deployment_user_id=..., workspace_id=..., project_id=...)`\n- `ensure_project_permission(user, ProjectAction.*, project_id=..., project_user_id=..., workspace_id=...)`\n- `ensure_knowledge_base_permission(user, KnowledgeBaseAction.*, kb_name=..., kb_user_id=...)`\n- `ensure_variable_permission(user, VariableAction.*, variable_id=..., variable_user_id=...)`\n- `ensure_file_permission(user, FileAction.*, file_id=..., file_user_id=...)`\n- `ensure_share_permission(user, ShareAction.*, share_id=..., share_user_id=...)`\n- `filter_visible_resources(user, resource_type=..., candidates=..., act=...)` — list-endpoint filter; safe no-op in OSS\n\nThe enforcement request shape is `(subject, domain, object, action)`:\n- subject = `user:{uuid}`\n- domain = `project:{uuid}` → `workspace:{uuid}` → `*` (resolved by `_resolve_flow_domain`; the more specific domain wins so project-scoped grants match directly while workspace-scoped grants still flow down via plugin-side role inheritance)\n- object = `flow:{uuid}` / `deployment:{uuid}` / `project:{uuid}` / `flow:*` / etc.\n- action = `read` / `write` / `create` / `delete` / `execute` / `deploy`\n\n**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher, variable PATCH/DELETE in `variable.py`) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.\n\n**Share CRUD API (Phase 3):** `/api/v1/authz/shares` provides POST / GET / PATCH / DELETE on `authz_share` rows. The handler enforces an OSS floor (resource owner or superuser may administer shares for that resource) so the OSS pass-through cannot let a non-owner mint share rows. Each write fires `BaseAuthorizationService.invalidate_user` / `invalidate_all` so a registered enforcer can drop cached policy. Audit rows are written via `audit_decision` with `share:create` / `share:update` / `share:delete` actions.\n\n**Audit query API (Phase 4):** `GET /api/v1/authz/audit` (superuser-only) exposes a paginated, filterable view of `authz_audit_log`. Supports `user_id`, `resource_type`, `resource_id`, `action`, `result`, `since`, `until` filters; page size capped at 200.\n\n**Default role catalog (Phase 4):** the consolidated foundations migration `7c8d9e0f1a2b_authz_foundations` seeds the three built-in `is_system=True` roles (viewer / developer / admin) with `\"{resource}:{action}\"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.\n\n## Component Development\n\nComponents live in `src/backend/base/langflow/components/`. To add a new component:\n\n1. Create component class inheriting from `Component`\n2. Define `display_name`, `description`, `icon`, `inputs`, `outputs`\n3. Add to `__init__.py` (alphabetical order)\n4. Run with `LFX_DEV=1 make backend` for hot reload\n\n**IMPORTANT:** Changing a component's class name is a breaking change and should never be done. The class name serves as an identifier used to match components in saved flows and to flag them for updates in the UI. Renaming it will break existing flows that use that component.\n\n### Component Structure\n```python\nfrom langflow.custom import Component\nfrom langflow.io import MessageTextInput, Output\n\nclass MyComponent(Component):\n    display_name = \"My Component\"\n    description = \"What it does\"\n    icon = \"component-icon\"  # Lucide icon name or custom\n\n    inputs = [\n        MessageTextInput(name=\"input_value\", display_name=\"Input\"),\n    ]\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"process\"),\n    ]\n\n    def process(self) -> Message:\n        # Component logic\n        return Message(text=self.input_value)\n```\n\n### Component Testing\nTests go in `src/backend/tests/unit/components/`. Use base classes:\n- `ComponentTestBaseWithClient` - Components needing API access\n- `ComponentTestBaseWithoutClient` - Pure logic components\n\nRequired fixtures: `component_class`, `default_kwargs`, `file_names_mapping`\n\n## Frontend Development\n\n- **React 19** + TypeScript + Vite\n- **Zustand** for state management\n- **@xyflow/react** for graph visualization\n- **Tailwind CSS** for styling\n\n### Custom Icons\n1. Create SVG component in `src/frontend/src/icons/YourIcon/`\n2. Export with `forwardRef` and `isDark` prop support\n3. Add to `lazyIconImports.ts`\n4. Set `icon = \"YourIcon\"` in Python component\n\n## Testing Notes\n\n- `@pytest.mark.api_key_required` - Tests requiring external API keys\n- `@pytest.mark.no_blockbuster` - Skip blockbuster plugin\n- Database tests may fail in batch but pass individually\n- Pre-commit hooks require `uv run git commit`\n- Always use `uv run` when running Python commands\n- When running tests inside a sub-package (e.g. `langflow-base`, `lfx`), sync that package's dev group first: `uv sync --group dev --package langflow-base`. The default `uv sync` only resolves the top-level workspace and may leave dev-only test deps (e.g. `fakeredis`) uninstalled.\n\n### Graph Testing Pattern\n\nProper Graph tests follow this pattern:\n1. Build graph with connected components\n2. Connect them via `.set()` calls\n3. Call `async_start` and iterate over the results\n4. Validate the results\n\n### Testing Best Practices\n\n- Avoid mocking in tests when possible\n- Prefer real integrations for more reliable tests\n\n## Version Management\n```bash\nmake patch v=1.5.0  # Update version across all packages\n```\n\nThis updates: `pyproject.toml`, `src/backend/base/pyproject.toml`, `src/frontend/package.json`\n\n## Pre-commit Workflow\n\nPre-commit hooks run ruff and biome automatically on `git commit`, so manual\nformatting is not required. To avoid an extra commit cycle when you have many\nchanges:\n\n1. Run `make format_backend` once before staging - fixes most ruff issues up front.\n2. Run `uv run git commit` (the `uv run` ensures pre-commit finds the right Python).\n3. If you touched backend code, run `make unit_tests` locally for faster feedback than CI.\n\n## Pull Request Guidelines\n\n- Follow [semantic commit conventions](https://www.conventionalcommits.org/)\n- Reference any issues fixed (e.g., `Fixes #1234`)\n- Ensure all tests pass before submitting\n\n## Documentation\n\nDocumentation uses Docusaurus and lives in `docs/`:\n```bash\ncd docs\nyarn install\nyarn start        # Dev server on port 3000 (prompts for 3001 if 3000 is in use)\n```\n\n<!-- File: DESIGN.md (Tokens: ~300 | Category: project-spec) -->\n# UI Design System & Style Tokens\n\nPath: `DESIGN.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/DESIGN.md)\n\n<!-- File: .agents/skills/backend-code-review/references/architecture-rule.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/backend-code-review/references/architecture-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/architecture-rule.md)\n\n<!-- File: .agents/skills/backend-code-review/references/db-schema-rule.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/backend-code-review/references/db-schema-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/db-schema-rule.md)\n\n<!-- File: .agents/skills/backend-code-review/references/repositories-rule.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/backend-code-review/references/repositories-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/repositories-rule.md)\n\n<!-- File: .agents/skills/backend-code-review/references/sqlalchemy-rule.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/backend-code-review/references/sqlalchemy-rule.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/backend-code-review/references/sqlalchemy-rule.md)\n\n<!-- File: .agents/skills/backend-code-review/SKILL.md (Tokens: ~3088 | Category: anthropic-skill) -->\n---\nname: backend-code-review\ndescription: Review backend code for quality, security, maintainability, and best practices based on established checklist rules. Use when the user requests a review, analysis, or improvement of backend files (e.g., `.py`) under the `src/backend/` directory. Do NOT use for frontend files (e.g., `.tsx`, `.ts`, `.js`). Supports pending-change review, code snippets review, and file-focused review.\n---\n\n# Backend Code Review\n\n## When to use this skill\n\nUse this skill whenever the user asks to **review, analyze, or improve** backend code (e.g., `.py`) under the `src/backend/` directory. Supports the following review modes:\n\n- **Pending-change review**: when the user asks to review current changes (inspect staged/working-tree files slated for commit to get the changes).\n- **Code snippets review**: when the user pastes code snippets (e.g., a function/class/module excerpt) into the chat and asks for a review.\n- **File-focused review**: when the user points to specific files and asks for a review of those files (one file or a small, explicit set of files, e.g., `src/backend/base/langflow/api/v1/flows.py`).\n\nDo NOT use this skill when:\n\n- The request is about frontend code or UI (e.g., `.tsx`, `.ts`, `.js`, `src/frontend/`).\n- The user is not asking for a review/analysis/improvement of backend code.\n- The scope is not under `src/backend/` (unless the user explicitly asks to review backend-related changes outside `src/backend/`).\n\n## How to use this skill\n\nFollow these steps when using this skill:\n\n1. **Identify the review mode** (pending-change vs snippet vs file-focused) based on the user's input. Keep the scope tight: review only what the user provided or explicitly referenced.\n2. Follow the rules defined in **Checklist** to perform the review. If no Checklist rule matches, apply **General Review Rules** as a fallback to perform the best-effort review.\n3. Compose the final output strictly following the **Required Output Format**.\n\nNotes when using this skill:\n- Always include actionable fixes or suggestions (including possible code snippets).\n- Use best-effort `File:Line` references when a file path and line numbers are available; otherwise, use the most specific identifier you can.\n\n## Checklist\n\n- db schema design: if the review scope includes code/files under `src/backend/base/langflow/services/database/models/` or Alembic migrations under `src/backend/base/langflow/alembic/versions/`, follow [references/db-schema-rule.md](references/db-schema-rule.md) to perform the review\n- architecture: if the review scope involves route/service/model layering, dependency direction, or moving responsibilities across modules, follow [references/architecture-rule.md](references/architecture-rule.md) to perform the review\n- service abstraction: if the review scope contains table/model operations (e.g., `select(...)`, `session.execute(...)`, joins, CRUD) and is not already inside a service under `src/backend/base/langflow/services/`, follow [references/repositories-rule.md](references/repositories-rule.md) to perform the review\n- sqlalchemy patterns: if the review scope involves SQLAlchemy/SQLModel session/query usage, db transaction/crud usage, `session_scope()` usage, or raw SQL usage, follow [references/sqlalchemy-rule.md](references/sqlalchemy-rule.md) to perform the review\n\n## General Review Rules\n\n### 1. Security Review\n\nCheck for:\n- SQL injection vulnerabilities (especially raw `text()` queries with string interpolation). Consequence: attacker can read/modify/delete any data in the database.\n- Server-Side Request Forgery (SSRF) in component HTTP calls. Consequence: attacker uses the server to scan internal networks or access cloud metadata endpoints.\n- Command injection (especially in subprocess or shell-executing components). Consequence: attacker gains shell access to the server.\n- Insecure deserialization (pickle, yaml.load without SafeLoader). Consequence: arbitrary code execution on the server.\n- Hardcoded secrets/credentials. Consequence: secrets leak via git history and are impossible to fully revoke.\n- Improper authentication/authorization (missing `CurrentActiveUser` dependency). Consequence: unauthenticated users can access protected endpoints.\n- Insecure direct object references (missing `user_id` scoping on queries). Consequence: user A can read/modify user B's flows, variables, API keys.\n- Path traversal in file storage operations. Consequence: attacker reads arbitrary server files (e.g., `/etc/passwd`, `.env`).\n\n### 2. Performance Review\n\nCheck for:\n- N+1 queries (especially in loops calling `session.execute()`). Consequence: 100 flows = 101 DB queries instead of 2; page load goes from 50ms to 5s.\n- Missing database indexes on frequently queried columns. Consequence: full table scans on large datasets; queries degrade from O(log n) to O(n).\n- Memory leaks (unbounded caches, retained references in long-lived services). Consequence: server OOM after hours of operation; pods restart in production.\n- Blocking operations in async code (`time.sleep()`, synchronous I/O, CPU-bound work without `run_in_executor`). Consequence: entire event loop stalls; all concurrent requests hang until the blocking call completes.\n- Missing caching opportunities for expensive computations. Consequence: repeated computation of the same result on every request.\n- Large result sets loaded entirely into memory without pagination. Consequence: memory spike + slow response when user has 10K+ flows.\n\n### 3. Code Quality Review\n\nCheck for:\n- Code forward compatibility with Python 3.10-3.13\n- Code duplication (DRY violations — extract when the *exact same business rule* is duplicated in 3+ places)\n- Functions doing too much (SRP violations — if you need \"and\" to describe it, split it)\n- Deep nesting / complex conditionals (prefer early returns and guard clauses)\n- Magic numbers/strings (extract to named constants or enums)\n- Poor naming: unclear abbreviations, misleading names, generic names (`data`, `result`, `obj`, `temp`). Functions should use verbs (`get`, `create`, `validate`). Booleans should use prefixes (`is_`, `has_`, `can_`, `should_`).\n- Missing error handling (bare `except`, swallowed exceptions, silent failures)\n- Incomplete type coverage (use strong typing, avoid `Any` where a concrete type is known)\n- Use Python 3.10+ union syntax (`X | Y` not `Union[X, Y]`, `X | None` not `Optional[X]`)\n- Use `TYPE_CHECKING` guard for imports only needed for type annotations (prevents circular imports)\n- Use `Annotated[Type, Depends(...)]` with project aliases (`CurrentActiveUser`, `DbSession`, `DbSessionReadOnly`) for FastAPI DI\n- Google-style docstrings (enforced by Ruff): `Args:`, `Returns:`, `Raises:` sections for public functions\n- Violations of SOLID principles\n- YAGNI violations (code that anticipates future needs without a present requirement)\n- Line length exceeding 120 characters (project Ruff config)\n- Comments that explain WHAT instead of WHY (comments should only explain reasoning, not restate code)\n- Commented-out code (use version control instead)\n- Boolean parameters that switch function behavior (split into two named functions instead)\n- Mutable shared state where immutable alternatives exist (prefer returning new objects over mutation)\n\n### 4. File Structure Review\n\nCheck for:\n- Production files exceeding ~500 lines of code (excluding imports, types, and docstrings). Files above 600 lines are a red flag and should be split by responsibility. Why: Files above 500 lines have statistically higher defect rates and take longer to review. They signal multiple responsibilities (SRP violation). In Langflow, services like `DatabaseService` that grow beyond this limit should have their CRUD operations extracted to dedicated modules.\n- Test files exceeding ~1000 lines. Split by logical grouping if exceeded.\n- No more than 5 functions with different responsibilities in a single file (per AGENTS-example.md).\n- Each file has a single reason to exist and a single reason to change (SRP).\n- No generic file names: `utils.py`, `helpers.py`, `misc.py`, `common.py` as standalone files. Why: A file named `utils.py` becomes a dumping ground for unrelated functions. Within months it has 50+ functions covering formatting, validation, parsing, and HTTP calls — violating SRP. Each function group should be in a file named after its responsibility (`formatting.py`, `validation.py`).\n\n### 5. Testing Review\n\nCheck for:\n- Missing test coverage for new code paths\n- Tests that don't test behavior (testing implementation details)\n- Flaky test patterns (time-dependent, order-dependent, external-service-dependent)\n- Proper use of `pytest.mark.asyncio` for async tests\n- Excessive mocking (prefer real integrations per project conventions)\n- Coverage target: 80% (minimum acceptable: 75%)\n- Test anti-patterns: The Liar (passes but doesn't verify claimed behavior), The Mirror (asserts exactly what code does), The Giant (50+ lines setup), The Mockery (tests only mock setup), The Inspector (coupled to implementation), The Chain Gang (depends on execution order), The Flaky (inconsistent results)\n\n**Happy path tests are the foundation but are NOT enough.** Tests MUST also challenge the code to find real defects:\n\n- **Unexpected inputs**: `None`, `\"\"`, `[]`, `{}`, `0`, `-1`, `UUID(\"00000000-0000-0000-0000-000000000000\")`\n- **Boundary values**: max length strings, exactly at the limit, one past the limit, zero items, max items\n- **Malformed data**: missing required fields, extra unexpected fields, wrong types, invalid formats\n- **Error states**: what happens when the database is down? When an external API returns 500? When the user doesn't exist?\n- **What should NOT happen**: verify that user A CANNOT access user B's flows. Verify that a deleted flow returns 404. Verify that invalid `endpoint_name` is rejected with 422.\n- **Error messages and types**: not just that it fails, but that it fails with the RIGHT exception and the RIGHT message\n- **Concurrency**: what happens when two requests try to update the same flow simultaneously?\n\n**Write tests based on REQUIREMENTS/SPEC, not on what the source code currently does.** This is how you catch bugs where the code diverges from expected behavior.\n\n**When a test fails:** first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.\n\n### 6. Observability Review\n\nCheck for:\n- Use the async logger from `lfx.log.logger` with `a`-prefixed methods (`adebug`, `ainfo`, `awarning`, `aerror`, `aexception`). Never use `print()` or stdlib `logging`.\n- Log at key decision points and boundaries, not inside tight loops\n- Include: operation name, relevant IDs, outcome (success/failure), duration if relevant\n- Correct log levels: ERROR (broken, needs attention), WARN (degraded but recoverable), INFO (significant events), DEBUG (diagnostic, off in prod)\n- **ZERO PII TOLERANCE**: Never log email addresses, user names, phone numbers, tokens, passwords. Only approved identifiers: `user_id`, `flow_id`, `session_id`\n- No `print()` statements — these go to production logs\n- Use `{e!s}` for string representation of exceptions in log messages\n\n### 7. Pre-Commit Verification\n\nFor pending-change reviews, verify the author has run:\n- `make format_backend` (Ruff formatter) — inconsistent formatting creates noisy diffs that hide real changes in code review. Format first, review second.\n- `make lint` (MyPy type checking) — type errors caught at lint time are 10x cheaper to fix than runtime crashes in production. Langflow services use duck typing via `Service` base class; MyPy catches mismatches early.\n- `make unit_tests` (pytest) — a failing test means the change breaks existing behavior. Never merge with failing tests; investigate whether the code or the test is wrong.\n\n## Required Output Format\n\nWhen this skill is invoked, the response must exactly follow one of the two templates:\n\n### Template A (any findings)\n\n```markdown\n# Code Review Summary\n\nFound <X> critical issues need to be fixed:\n\n## 🔴 Critical (Must Fix)\n\n### 1. <brief description of the issue>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<detailed explanation and references of the issue>\n\n#### Suggested Fix\n\n1. <brief description of suggested fix>\n2. <code example> (optional, omit if not applicable)\n\n---\n... (repeat for each critical issue) ...\n\nFound <Y> suggestions for improvement:\n\n## 🟡 Suggestions (Should Consider)\n\n### 1. <brief description of the suggestion>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<detailed explanation and references of the suggestion>\n\n#### Suggested Fix\n\n1. <brief description of suggested fix>\n2. <code example> (optional, omit if not applicable)\n\n---\n... (repeat for each suggestion) ...\n\nFound <Z> optional nits:\n\n## 🟢 Nits (Optional)\n### 1. <brief description of the nit>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n#### Explanation\n\n<explanation and references of the optional nit>\n\n#### Suggested Fix\n\n- <minor suggestions>\n\n---\n... (repeat for each nits) ...\n\n## ✅ What's Good\n\n- <Positive feedback on good patterns>\n```\n\n- If there are no critical issues or suggestions or optional nits or good points, just omit that section.\n- If the issue number is more than 10, summarize as \"Found 10+ critical issues/suggestions/optional nits\" and only output the first 10 items.\n- Don't compress the blank lines between sections; keep them as-is for readability.\n- If there is any issue that requires code changes, append a brief follow-up question to ask whether the user wants to apply the fix(es) after the structured output. For example: \"Would you like me to use the Suggested fix(es) to address these issues?\"\n\n### Template B (no issues)\n\n```markdown\n## Code Review Summary\n✅ No issues found.\n```\n\n<!-- File: .agents/skills/component-refactoring/references/complexity-patterns.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/component-refactoring/references/complexity-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/complexity-patterns.md)\n\n<!-- File: .agents/skills/component-refactoring/references/component-splitting.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/component-refactoring/references/component-splitting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/component-splitting.md)\n\n<!-- File: .agents/skills/component-refactoring/references/hook-extraction.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/component-refactoring/references/hook-extraction.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/component-refactoring/references/hook-extraction.md)\n\n<!-- File: .agents/skills/component-refactoring/SKILL.md (Tokens: ~3319 | Category: anthropic-skill) -->\n---\nname: component-refactoring\ndescription: Refactor high-complexity React components in Langflow frontend. Use when manual complexity assessment shows complexity > 50 or lineCount > 300, when the user asks for code splitting, hook extraction, or complexity reduction; avoid for simple/well-structured components, third-party wrappers, or when the user explicitly wants testing without refactoring.\n---\n\n# Langflow Component Refactoring Skill\n\nRefactor high-complexity React components in the Langflow frontend codebase with the patterns and workflow below.\n\n> **Complexity Threshold**: Components with complexity > 50 (measured manually by counting conditionals, nesting levels, and lines) should be refactored before testing.\n\n## Quick Reference\n\n### Commands (run from `src/frontend/`)\n\n```bash\ncd src/frontend\n\n# Lint with Biome\nnpm run lint\n\n# Type checking\nnpm run type-check\n\n# Run tests\nnpm test\n```\n\n### Manual Complexity Assessment\n\nSince Langflow does not have automated complexity analysis tools, assess components manually:\n\n1. **Count conditionals**: Each `if/else`, `switch/case`, ternary, `&&`/`||` chain adds +1.\n2. **Count nesting levels**: Each level of nesting within conditionals or loops adds +1.\n3. **Count total lines**: Target < 300 lines per component file.\n4. **Count state hooks**: More than 5 `useState` calls suggests hook extraction.\n5. **Count effects**: More than 3 `useEffect` calls suggests effect consolidation.\n\n### Complexity Score Interpretation\n\n| Score | Level | Action |\n|-------|-------|--------|\n| 0-25 | Simple | Ready for testing |\n| 26-50 | Medium | Consider minor refactoring |\n| 51-75 | Complex | **Refactor before testing** |\n| 76-100 | Very Complex | **Must refactor** |\n\n## Core Refactoring Patterns\n\n### Pattern 1: Extract Custom Hooks\n\n**When**: Component has complex state management, multiple `useState`/`useEffect`, or business logic mixed with UI.\n\n**Langflow Convention**: Place hooks in a `hooks/` subdirectory or alongside the component as `use-<feature>.ts`. Langflow uses kebab-case filenames with `use-` prefix.\n\n```typescript\n// Before: Complex state logic in component\nconst FlowPage: FC = () => {\n  const [nodes, setNodes] = useState<Node[]>([])\n  const [edges, setEdges] = useState<Edge[]>([])\n  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)\n\n  // 50+ lines of state management logic...\n\n  return <div>...</div>\n}\n\n// After: Extract to custom hook\n// hooks/use-flow-state.ts\nexport const useFlowState = (flowId: string) => {\n  const [nodes, setNodes] = useState<Node[]>([])\n  const [edges, setEdges] = useState<Edge[]>([])\n  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)\n\n  // Related state management logic here\n\n  return { nodes, setNodes, edges, setEdges, buildStatus, setBuildStatus }\n}\n\n// Component becomes cleaner\nconst FlowPage: FC = () => {\n  const { nodes, setNodes, edges, setEdges } = useFlowState(flowId)\n  return <div>...</div>\n}\n```\n\n**Langflow Examples**:\n- `src/frontend/src/hooks/use-add-component.ts`\n- `src/frontend/src/hooks/use-unsaved-changes.ts`\n- `src/frontend/src/hooks/use-refresh-model-inputs.ts`\n\n### Pattern 2: Extract Sub-Components\n\n**When**: Single component has multiple UI sections, conditional rendering blocks, or repeated patterns.\n\n**Langflow Convention**: Place sub-components in subdirectories or as separate files in the same directory. UI primitives go in `components/ui/`, domain components in `components/core/`, reusable components in `components/common/`.\n\n```typescript\n// Before: Monolithic JSX with multiple sections\nconst GenericNode = () => {\n  return (\n    <div>\n      {/* 100 lines of header UI */}\n      {/* 100 lines of parameter fields */}\n      {/* 100 lines of output handles */}\n    </div>\n  )\n}\n\n// After: Split into focused components\n// CustomNodes/GenericNode/\n//   generic-node.tsx       (orchestration only — kebab-case, descriptive name)\n//   components/\n//     node-header.tsx\n//     node-parameters.tsx\n//     node-outputs.tsx\n\nconst GenericNode = () => {\n  return (\n    <div>\n      <NodeHeader nodeData={data} />\n      <NodeParameters fields={fields} />\n      <NodeOutputs outputs={outputs} />\n    </div>\n  )\n}\n```\n\n**Langflow Examples**:\n- `src/frontend/src/CustomNodes/GenericNode/components/`\n- `src/frontend/src/components/core/`\n- `src/frontend/src/components/ui/`\n\n### Pattern 3: Simplify Conditional Logic\n\n**When**: Deep nesting (> 3 levels), complex ternaries, or multiple `if/else` chains.\n\n```typescript\n// Before: Deeply nested conditionals\nconst getFieldComponent = (field: InputFieldType) => {\n  if (field.type === \"str\") {\n    if (field.multiline) {\n      return <TextAreaComponent />\n    } else if (field.password) {\n      return <PasswordInput />\n    } else if (field.options?.length) {\n      return <Dropdown options={field.options} />\n    } else {\n      return <InputComponent />\n    }\n  } else if (field.type === \"int\") {\n    return <IntComponent />\n  } else if (field.type === \"float\") {\n    return <FloatComponent />\n  }\n  return null\n}\n\n// After: Use lookup tables + early returns\nconst FIELD_COMPONENT_MAP: Record<string, FC<FieldProps>> = {\n  int: IntComponent,\n  float: FloatComponent,\n  bool: ToggleComponent,\n  code: CodeAreaComponent,\n}\n\nconst STR_VARIANT_MAP: Record<string, FC<FieldProps>> = {\n  multiline: TextAreaComponent,\n  password: PasswordInput,\n  options: Dropdown,\n}\n\nconst getFieldComponent = (field: InputFieldType) => {\n  if (field.type !== \"str\") {\n    const Component = FIELD_COMPONENT_MAP[field.type]\n    return Component ? <Component {...field} /> : null\n  }\n\n  const variant = field.multiline ? \"multiline\"\n    : field.password ? \"password\"\n    : field.options?.length ? \"options\"\n    : \"default\"\n\n  const Component = STR_VARIANT_MAP[variant] ?? InputComponent\n  return <Component {...field} />\n}\n```\n\n### Pattern 4: Extract API/Data Logic\n\n**When**: Component directly handles API calls, data transformation, or complex async operations.\n\n**Langflow Convention**:\n- This skill is for component decomposition, not query/mutation design.\n- When refactoring data fetching, use `frontend-query-mutation` for query patterns, `UseRequestProcessor`, cache invalidation, and mutation error handling.\n- Do not create thin passthrough `useQuery` wrappers during refactoring; only extract a custom hook when it truly orchestrates multiple queries/mutations or shared derived state.\n- API hooks live in `controllers/API/queries/{domain}/`.\n\n**Langflow Examples**:\n- `src/frontend/src/controllers/API/queries/flows/use-post-add-flow.ts`\n- `src/frontend/src/controllers/API/queries/variables/use-get-global-variables.ts`\n- `src/frontend/src/controllers/API/queries/folders/use-get-folders.ts`\n\n### Pattern 5: Extract Modal/Dialog Management\n\n**When**: Component manages multiple modals with complex open/close states.\n\n**Langflow Convention**: Modals should be extracted with their state management.\n\n```typescript\n// Before: Multiple modal states in component\nconst FlowToolbar = () => {\n  const [showExportModal, setShowExportModal] = useState(false)\n  const [showShareModal, setShowShareModal] = useState(false)\n  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)\n  const [showApiModal, setShowApiModal] = useState(false)\n  // 5+ more modal states...\n}\n\n// After: Extract to modal management hook\ntype ModalType = \"export\" | \"share\" | \"delete\" | \"api\" | null\n\nconst useFlowToolbarModals = () => {\n  const [activeModal, setActiveModal] = useState<ModalType>(null)\n\n  const openModal = useCallback((type: ModalType) => setActiveModal(type), [])\n  const closeModal = useCallback(() => setActiveModal(null), [])\n\n  return {\n    activeModal,\n    openModal,\n    closeModal,\n    isOpen: (type: ModalType) => activeModal === type,\n  }\n}\n```\n\n### Pattern 6: Extract Form Logic\n\n**When**: Complex form validation, submission handling, or field transformation.\n\n**Langflow Convention**: Extract form state and validation into hooks.\n\n```typescript\n// Extract form validation and submission\nconst useFlowSettingsForm = (initialValues: FlowSettings) => {\n  const [values, setValues] = useState(initialValues)\n  const [errors, setErrors] = useState<Record<string, string>>({})\n\n  const validate = useCallback(() => {\n    const newErrors: Record<string, string> = {}\n    if (!values.name?.trim()) newErrors.name = \"Name is required\"\n    if (values.endpoint_name && !/^[a-z0-9-]+$/.test(values.endpoint_name)) {\n      newErrors.endpoint_name = \"Must be lowercase alphanumeric with hyphens\"\n    }\n    setErrors(newErrors)\n    return Object.keys(newErrors).length === 0\n  }, [values])\n\n  const handleChange = useCallback((field: string, value: any) => {\n    setValues((prev) => ({ ...prev, [field]: value }))\n  }, [])\n\n  return { values, errors, validate, handleChange }\n}\n```\n\n## Langflow-Specific Refactoring Guidelines\n\n### 1. Zustand Store Selectors\n\n**When**: Component reads many values from a Zustand store, causing unnecessary re-renders.\n\n```typescript\n// Before: Selecting too many values\nconst Component = () => {\n  const flowStore = useFlowStore()\n  // Component re-renders on ANY store change\n}\n\n// After: Use individual selectors\nconst Component = () => {\n  const nodes = useFlowStore((state) => state.nodes)\n  const edges = useFlowStore((state) => state.edges)\n  // Component only re-renders when nodes or edges change\n}\n```\n\n**Langflow Reference**: All stores in `src/frontend/src/stores/` follow this selector pattern.\n\n### 2. Custom Node Components\n\n**When**: Refactoring flow node components (`CustomNodes/GenericNode/`).\n\n**Conventions**:\n- Keep node logic in custom hooks\n- Extract parameter rendering to separate components\n- Use the existing `components/` subdirectory for sub-components\n\n```\nCustomNodes/GenericNode/\n  generic-node.tsx             # Node registration and main render (kebab-case, NOT index.tsx)\n  components/\n    handle-render.tsx           # Handle rendering\n    node-description.tsx        # Node description display\n    node-input-field.tsx        # Input field rendering\n    node-name.tsx               # Node name display\n    node-output-field.tsx       # Output field rendering\n    node-status.tsx             # Build status display\n```\n\n### 3. Flow Canvas Components\n\n**When**: Refactoring components related to the flow editor canvas.\n\n**Conventions**:\n- `@xyflow/react` v12 is the canvas library\n- Keep canvas event handlers separate from UI rendering\n- Use `useFlowStore` for flow state management\n- Use `useFlowsManagerStore` for multi-flow management\n\n### 4. API Query Hook Components\n\n**When**: Refactoring components that consume API data.\n\n**Conventions**:\n- Use existing query hooks from `controllers/API/queries/`\n- Access `UseRequestProcessor` for new queries/mutations\n- Use query key arrays like `[\"useGetGlobalVariables\"]` for cache management\n- Cache invalidation belongs in mutation `onSettled` callbacks\n\n## Refactoring Workflow\n\n### Step 1: Assess Complexity\n\nManually count:\n- Total conditionals (if/else, switch, ternary, &&/||)\n- Maximum nesting depth\n- Total lines of code\n- Number of useState/useEffect hooks\n- Number of distinct UI sections\n\n### Step 2: Plan\n\nCreate a refactoring plan based on detected features:\n\n| Detected Feature | Refactoring Action |\n|------------------|-------------------|\n| 5+ `useState` hooks with related state | Extract custom hook |\n| API calls in component body | Extract to query hook |\n| 3+ event handlers with logic | Extract event handlers to hook |\n| 300+ lines | Split into sub-components |\n| Deep conditional nesting (>3) | Simplify conditional logic |\n| Multiple modal states | Extract modal management |\n\n### Step 3: Execute Incrementally\n\n1. **Extract one piece at a time**\n2. **Run lint, type-check, and tests after each extraction**\n3. **Verify functionality before next step**\n\n```\nFor each extraction:\n  1. Extract code\n  2. Run: npm run lint\n  3. Run: npm run type-check\n  4. Run: npm test\n  5. Test functionality manually\n  6. PASS? -> Next extraction\n     FAIL? -> Fix before continuing\n```\n\n### Step 4: Verify\n\nAfter refactoring, re-assess complexity manually:\n\n- Target complexity < 50\n- Target line count < 300\n- Target max nesting depth <= 3\n- Target max function length < 30 lines\n\n## Common Mistakes to Avoid\n\n### Over-Engineering\n\n```typescript\n// Too many tiny hooks\nconst useButtonText = () => useState(\"Click\")\nconst useButtonDisabled = () => useState(false)\nconst useButtonLoading = () => useState(false)\n\n// Cohesive hook with related state\nconst useButtonState = () => {\n  const [text, setText] = useState(\"Click\")\n  const [disabled, setDisabled] = useState(false)\n  const [loading, setLoading] = useState(false)\n  return { text, setText, disabled, setDisabled, loading, setLoading }\n}\n```\n\n### Breaking Existing Patterns\n\n- Follow existing directory structures in `components/ui/`, `components/core/`, `components/common/`\n- Maintain naming conventions (kebab-case files, PascalCase components)\n- Preserve export patterns for compatibility\n- Keep Zustand store selector patterns consistent\n\n### Premature Abstraction\n\n- Only extract when there is clear complexity benefit\n- Do not create abstractions for single-use code\n- Keep refactored code in the same domain area\n\n### Bypassing UseRequestProcessor\n\n- Do not call `useQuery` or `useMutation` directly for API calls\n- Always use the `UseRequestProcessor` pattern for consistency with retry and invalidation logic\n- See `frontend-query-mutation` skill for API hook patterns\n\n## References\n\n### Langflow Codebase Examples\n\n- **Hook extraction**: `src/frontend/src/hooks/`\n- **Component splitting**: `src/frontend/src/CustomNodes/GenericNode/components/`\n- **UI components**: `src/frontend/src/components/ui/`\n- **Core components**: `src/frontend/src/components/core/`\n- **Common components**: `src/frontend/src/components/common/`\n- **API query hooks**: `src/frontend/src/controllers/API/queries/`\n- **Zustand stores**: `src/frontend/src/stores/`\n\n### Related Skills\n\n- `frontend-query-mutation` - For API query and mutation patterns\n- `frontend-testing` - For testing refactored components\n\n<!-- File: .agents/skills/e2e-testing/references/fixtures.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/e2e-testing/references/fixtures.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/fixtures.md)\n\n<!-- File: .agents/skills/e2e-testing/references/helpers.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/e2e-testing/references/helpers.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/helpers.md)\n\n<!-- File: .agents/skills/e2e-testing/references/selectors.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/e2e-testing/references/selectors.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/e2e-testing/references/selectors.md)\n\n<!-- File: .agents/skills/e2e-testing/SKILL.md (Tokens: ~3214 | Category: anthropic-skill) -->\n---\nname: e2e-testing\ndescription: Write and review Playwright E2E tests for Langflow. Trigger when the user asks to write, fix, or review E2E tests, spec files, Playwright tests, or integration tests that exercise the full UI. Also trigger when modifying data-testid attributes, test helpers in tests/utils/, or fixture configuration.\n---\n\n# Langflow E2E Testing (Playwright)\n\n## When to Apply\n\n- User asks to **write E2E tests** for a feature or flow\n- User asks to **fix a failing E2E test**\n- User asks to **review E2E test coverage**\n- User modifies `data-testid` attributes in components (may break existing tests)\n- User changes test utilities in `src/frontend/tests/utils/`\n\n**Do NOT apply** when:\n- User asks about unit tests (use `frontend-testing` skill for Jest)\n- User asks about backend tests (use `backend-code-review` skill for pytest)\n\n## Tech Stack\n\n| Tool | Version | Purpose |\n|------|---------|---------|\n| Playwright | 1.59.1 | E2E test runner + browser automation |\n| Chromium | (bundled) | Default browser (Firefox/Safari disabled) |\n| Custom fixtures | `tests/fixtures.ts` | Auto-detects API errors and flow execution failures |\n\n## Key Commands\n\n```bash\n# Run all E2E tests\nnpx playwright test\n\n# Run tests filtered by tag\nnpx playwright test --grep \"@release\"\nnpx playwright test --grep \"@workspace\"\nnpx playwright test --grep \"@starter-projects\"\n\n# Run a specific test file\nnpx playwright test tests/core/features/run-flow.spec.ts\n\n# Debug mode (headed browser + step through)\nnpx playwright test --debug\n\n# Show HTML report after run\nnpx playwright show-report\n\n# Update snapshots (if used)\nnpx playwright test --update-snapshots\n```\n\n## Configuration\n\n**File**: `src/frontend/playwright.config.ts`\n\n| Setting | Value | Why |\n|---------|-------|-----|\n| `fullyParallel` | `true` | Tests run in parallel for speed |\n| `timeout` | 5 minutes | Flow builds can be slow; prevents false timeouts |\n| `retries` | 3 (local), 2 (CI) | Flaky network/rendering issues; retries catch them |\n| `workers` | 2 | Balances speed and resource usage |\n| `actionTimeout` | 20s | Individual action timeout (click, fill, etc.) |\n| `trace` | `on-first-retry` | Captures trace on failures for debugging |\n| `baseURL` | `http://localhost:3000` | Vite dev server |\n\n**WebServer**: Playwright auto-starts backend (uvicorn on 7860) + frontend (npm start on 3000).\n\n## Directory Structure\n\n```\nsrc/frontend/tests/\n├── fixtures.ts                     # Custom test fixture with error detection\n├── globalTeardown.ts               # Cleanup (removes temp DB after tests)\n├── core/\n│   ├── features/                   # Main feature tests (run-flow, playground, etc.)\n│   ├── integrations/               # Starter project / template tests\n│   ├── regression/                 # Bug regression tests\n│   └── unit/                       # Component-level Playwright tests\n├── extended/\n│   ├── features/                   # Extended features (MCP, auto-save, etc.)\n│   ├── integrations/               # Extended integrations\n│   └── regression/                 # Extended regressions\n└── utils/                          # 37+ shared helper functions\n```\n\n## File Naming\n\n- **kebab-case** with `.spec.ts` suffix: `run-flow.spec.ts`, `playground.spec.ts`, `flow-lock.spec.ts`\n- Template tests may use spaces: `Document QA.spec.ts`, `Social Media Agent.spec.ts`\n- Sharded tests for parallelization: `chatInputOutputUser-shard-0.spec.ts`\n\n> **Note**: E2E tests use `.spec.ts` (Playwright convention). Unit tests use `.test.tsx` (Jest convention). Do not mix them.\n\n## Test Anatomy\n\n### Basic Test\n\n```typescript\nimport { expect, test } from \"../../fixtures\";\nimport { awaitBootstrapTest } from \"../../utils/await-bootstrap-test\";\n\ntest(\n  \"user should be able to run a flow successfully\",\n  { tag: [\"@release\", \"@workspace\"] },\n  async ({ page }) => {\n    await awaitBootstrapTest(page);\n\n    // Arrange: Create a flow\n    await page.getByTestId(\"blank-flow\").click();\n\n    // Act: Add components and run\n    await page.getByTestId(\"sidebar-search-input\").fill(\"Chat Output\");\n    // ... setup ...\n\n    // Assert: Verify result\n    await expect(page.getByTestId(\"build-status-success\")).toBeVisible({ timeout: 30000 });\n  },\n);\n```\n\n### With test.describe\n\n```typescript\ntest.describe(\"Flow Lock Feature\", () => {\n  test(\n    \"should lock and unlock a flow\",\n    { tag: [\"@release\", \"@api\"] },\n    async ({ page }) => {\n      // ...\n    },\n  );\n\n  test(\n    \"should prevent editing when locked\",\n    { tag: [\"@release\"] },\n    async ({ page }) => {\n      // ...\n    },\n  );\n});\n```\n\n### With Serial Mode (tests that depend on order)\n\n```typescript\ntest.describe.configure({ mode: \"serial\" });\n\ntest(\"step 1: create flow\", async ({ page }) => { /* ... */ });\ntest(\"step 2: edit flow\", async ({ page }) => { /* ... */ });\ntest(\"step 3: delete flow\", async ({ page }) => { /* ... */ });\n```\n\n### With Event Delivery Modes (streaming/polling/direct)\n\n```typescript\nimport { withEventDeliveryModes } from \"../../utils/withEventDeliveryModes\";\n\nwithEventDeliveryModes(\n  \"Document Q&A should work\",\n  { tag: [\"@release\", \"@starter-projects\"] },\n  async ({ page }) => {\n    // This test runs 3 times: streaming, polling, direct\n    // Each mode is configured automatically via route interception\n  },\n);\n```\n\n## Tags System\n\nEvery test MUST be tagged with `@release` — the release run greps for it, so an\nuntagged or wrongly-tagged spec silently drops out of release coverage. Add the\ndomain tag(s) below on top of `@release` (a test can have more than one). These\nsix are the only allowed tags; do not invent new ones.\n\n| Tag | Purpose | When to Use |\n|-----|---------|-------------|\n| `@release` | Part of the release run (required on every spec) | All tests |\n| `@workspace` | Workspace/flow management | Creating, editing, deleting flows |\n| `@api` | API-dependent features | Tests that call backend endpoints |\n| `@database` | Database operations | Tests involving persistence |\n| `@components` | Component-level tests | Individual component behavior |\n| `@starter-projects` | Template/starter project tests | Pre-built flow templates |\n\n```typescript\n// Right: tag your test\ntest(\"my feature test\", { tag: [\"@release\", \"@workspace\"] }, async ({ page }) => { ... });\n\n// Wrong: no tags — test can't be filtered\ntest(\"my feature test\", async ({ page }) => { ... });\n```\n\n## Custom Fixtures: Error Detection\n\n**Always import `test` and `expect` from `../../fixtures`, NOT from `@playwright/test`.**\n\n```typescript\n// Right\nimport { expect, test } from \"../../fixtures\";\n\n// Wrong — bypasses error detection\nimport { expect, test } from \"@playwright/test\";\n```\n\nWhy: The custom fixture automatically monitors all `/api/` responses and fails the test if:\n- HTTP 400, 404, 422, or 500 errors occur\n- Flow execution returns `error: true` in event streams\n- Python exceptions appear in streamed responses\n\nTo opt-in to expected errors (e.g., testing error handling):\n```typescript\ntest(\"should show error on invalid input\", { tag: [\"@release\"] }, async ({ page }) => {\n  page.allowFlowErrors();  // Allow flow errors for this test\n  // ... test that expects errors ...\n});\n```\n\n## Selector Strategy\n\n### Priority (in order of preference)\n\n1. **`getByTestId`** — Most stable, used 95% of the time in Langflow\n2. **`getByRole`** — For buttons, headings, and form elements\n3. **`getByText`** — For visible text content\n4. **`waitForSelector`** — For CSS selectors and dynamic elements\n5. **`locator`** — For complex selectors (CSS, XPath)\n\n### Common data-testid Patterns\n\n**Canvas & Navigation:**\n- `blank-flow` — New blank flow button\n- `sidebar-search-input` — Component search\n- `canvas_controls_dropdown` — Canvas controls menu\n- `fit_view`, `zoom_out`, `zoom_in` — Canvas controls\n- `react-flow-id` — ReactFlow canvas container\n\n**Component Fields:**\n- `popover-anchor-input-{fieldname}` — Input field for a component parameter\n- `input-chat-playground` — Playground chat input\n- `div-chat-message` — Chat message in playground\n\n**Actions:**\n- `add-component-button-{component}` — Add component to canvas\n- `button-send` — Send chat message\n- `button_run_{component}` — Run specific component\n- `publish-button`, `save-flow-button` — Flow actions\n- `edit-fields-button` — Toggle inspection panel field editor\n\n**Modals & Panels:**\n- `modal-title` — Modal heading\n- `icon-Globe` — Global variables\n- `icon-Lock` — Flow lock toggle\n- `session-selector` — Playground session switcher\n\n### Important: Global Variables and Badges\n\nWhen a component field has a global variable selected (`load_from_db: true` + `value: \"OPENAI_API_KEY\"`), the field renders a **badge** instead of an `<input>` element. This means `getByTestId(\"popover-anchor-input-api_key\")` will NOT find the element — it doesn't exist in the DOM.\n\nTemplates with global variables pre-selected: Market Research, Price Deal Finder, Research Agent.\nTemplates without (input IS rendered): Instagram Copywriter.\n\n## Core Helper Functions\n\nLocated in `src/frontend/tests/utils/`:\n\n| Function | What it Does | When to Use |\n|----------|-------------|-------------|\n| `awaitBootstrapTest(page)` | Waits for app to fully load | **Start of every test** |\n| `initialGPTsetup(page)` | Full setup: adjustView → updateComponents → selectModel → addKey → adjustView → unselectNodes | Tests that need OpenAI configured |\n| `adjustScreenView(page, opts?)` | Fit view + zoom out | After adding components to canvas |\n| `zoomOut(page, times)` | Zoom out N times | When components are too small |\n| `selectGptModel(page)` | Selects gpt-4o-mini for all Language Model nodes | GPT-dependent tests |\n| `addOpenAiInputKey(page)` | Fills OPENAI_API_KEY for all openai_api_key fields | Tests requiring API key |\n| `enableInspectPanel(page)` | Toggles inspection panel ON | **MUST call before `edit-fields-button`** |\n| `disableInspectPanel(page)` | Toggles inspection panel OFF | Cleanup after inspection |\n| `updateOldComponents(page)` | Clicks \"Update all\" if outdated components exist | After loading saved flows |\n| `unselectNodes(page)` | Clicks empty canvas area to deselect all nodes | After node operations |\n| `renameFlow(page, { flowName })` | Renames the current flow | Flow management tests |\n| `uploadFile(page, filename)` | Uploads a file from test assets | File upload tests |\n| `withEventDeliveryModes(...)` | Runs test 3x: streaming, polling, direct | Starter project tests |\n\n### initialGPTsetup Options\n\n```typescript\nawait initialGPTsetup(page);  // All steps\n\nawait initialGPTsetup(page, {\n  skipAdjustScreenView: true,\n  skipUpdateOldComponents: true,\n  skipSelectGptModel: true,\n});\n```\n\n### Inspection Panel Pattern (CRITICAL)\n\n```typescript\n// MUST enable inspection panel FIRST\nawait enableInspectPanel(page);\n\n// Click a node to select it\nawait page.getByTestId(\"title-OpenAI\").click();\n\n// Open field editor\nawait page.getByTestId(\"edit-fields-button\").click();\n\n// Toggle field visibility\nawait page.getByTestId(\"showmodel_name\").click();\n\n// Close field editor\nawait page.getByTestId(\"edit-fields-button\").click();\n```\n\n**If you skip `enableInspectPanel(page)`, the `edit-fields-button` will NOT be visible.**\n\n## Skip Patterns\n\n```typescript\n// Skip test if env var missing\ntest.skip(!process?.env?.OPENAI_API_KEY, \"OPENAI_API_KEY required to run this test\");\n\n// Skip test unconditionally with reason\ntest.skip(true, \"Feature not yet implemented with new designs\");\n```\n\n## Writing Good E2E Tests\n\n### Do:\n- **Tag every test** with `@release` (plus any domain tags that apply)\n- **Import from `../../fixtures`**, not `@playwright/test`\n- **Start with `awaitBootstrapTest(page)`** — always\n- **Use `getByTestId`** for stable selectors\n- **Set explicit timeouts** on `waitForSelector` and `expect(...).toBeVisible()` for async operations\n- **Test the complete user flow**: setup → action → verification\n- **Use `withEventDeliveryModes`** for tests that involve flow execution (chat, build)\n\n### Don't:\n- **Don't use `page.waitForTimeout()`** unless absolutely necessary — prefer `waitForSelector` or `expect().toBeVisible()`\n- **Don't hardcode API keys** — read from `process.env.OPENAI_API_KEY`\n- **Don't skip tests without a reason** — always provide the second argument to `test.skip()`\n- **Don't import from `@playwright/test`** — use the custom fixtures\n- **Don't forget `enableInspectPanel(page)`** before accessing `edit-fields-button`\n- **Don't assume input fields exist** when global variables are selected (badge renders instead)\n\n### Challenge Tests (Apply Here Too)\n\nE2E tests should also cover adversarial scenarios:\n- **Invalid input**: paste 10K characters, special characters (`<script>alert(1)</script>`), empty submissions\n- **Network interruption**: what happens if the user loses connection mid-build?\n- **Permission boundaries**: can a user access another user's flow via direct URL?\n- **Concurrent actions**: double-click delete, rapid chat messages\n- **Error recovery**: does the UI recover gracefully from a 500 error?\n\n## References\n\n- [Selector Patterns](references/selectors.md) — Complete data-testid catalog\n- [Helper Functions](references/helpers.md) — Detailed documentation of all 37+ utility functions\n- [Test Fixtures](references/fixtures.md) — Custom fixture error detection behavior\n\n<!-- File: .agents/skills/frontend-code-review/references/business-logic.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/business-logic.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/business-logic.md)\n\n<!-- File: .agents/skills/frontend-code-review/references/code-quality.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/code-quality.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/code-quality.md)\n\n<!-- File: .agents/skills/frontend-code-review/references/performance.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-code-review/references/performance.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-code-review/references/performance.md)\n\n<!-- File: .agents/skills/frontend-code-review/SKILL.md (Tokens: ~1417 | Category: anthropic-skill) -->\n---\nname: frontend-code-review\ndescription: \"Review frontend code (.tsx, .ts, .js files) for quality, performance, and correctness against Langflow's frontend conventions. Supports pending-change reviews and file-targeted reviews.\"\n---\n\n# Frontend Code Review\n\n## When to use this skill\n\nUse this skill whenever the user asks to **review, analyze, or improve** frontend code (`.tsx`, `.ts`, `.js` files) under the `src/frontend/` directory. Supports the following review modes:\n\n1. **Pending-change review** -- inspect staged or working-tree files slated for commit and flag checklist violations before submission.\n2. **File-targeted review** -- review the specific file(s) the user names and report the relevant checklist findings.\n\nDo NOT use this skill when:\n\n- The request is about backend code (`.py` files under `src/backend/`).\n- The user is not asking for a review/analysis/improvement of frontend code.\n- The scope is outside `src/frontend/` (unless the user explicitly asks to review frontend-related changes elsewhere).\n\n## How to use this skill\n\nFollow these steps when using this skill:\n\n1. **Identify the review mode** (pending-change vs file-targeted) based on the user's input. Keep the scope tight: review only what the user provided or explicitly referenced.\n2. Follow the rules defined in the **Checklist** to perform the review. If no checklist rule matches, apply **General Review Rules** as a fallback.\n3. Compose the final output strictly following the **Required Output Format**.\n\nNotes when using this skill:\n\n- Always include actionable fixes or suggestions (including possible code snippets).\n- Use `File:Line` references when a file path and line numbers are available; otherwise, use the most specific identifier you can.\n- The Langflow frontend uses React 19, TypeScript 5.4, Vite 7 with SWC, Zustand for state management, TanStack React Query for server state, @xyflow/react v12 for graph visualization, Radix UI + shadcn-ui components, Tailwind CSS v3, and Biome for linting/formatting.\n\n## Checklist\n\n- **Code quality**: For any reviewed file, follow [references/code-quality.md](references/code-quality.md) to check styling conventions, TypeScript usage, Biome compliance, and component patterns.\n- **Performance**: If the review scope involves React components, hooks, Zustand stores, React Query usage, or @xyflow/react node rendering, follow [references/performance.md](references/performance.md) to check for re-render issues, memoization, and data flow patterns.\n- **Business logic**: If the review scope involves custom nodes (GenericNode), flow state, API calls, the component system, global variables, or the inspection panel, follow [references/business-logic.md](references/business-logic.md) to check for Langflow-specific correctness.\n- **i18n**: If the diff adds or changes user-facing text (labels, buttons, tooltips, modals, toasts, errors, placeholders, aria-labels), follow the [frontend-i18n skill](../frontend-i18n/SKILL.md): no hardcoded UI strings — everything through `t(...)` — and every new key present in **all** locale files (`src/frontend/src/locales/*.json`).\n\n## General Review Rules\n\n### 1. Security Review\n\nCheck for:\n- XSS vulnerabilities (dangerouslySetInnerHTML, unescaped user input)\n- Sensitive data exposure in client-side code\n- Insecure direct object references in API calls\n- Hardcoded secrets, tokens, or API keys\n\n### 2. Accessibility Review\n\nCheck for:\n- Missing aria labels on interactive elements\n- Keyboard navigation support\n- Proper use of semantic HTML elements\n- Color contrast issues in custom styling\n\n### 3. Code Quality Review\n\nCheck for:\n- Code duplication (DRY violations — extract at 3+ identical usages)\n- Functions/components doing too much (SRP violations — if you need \"and\" to describe it, split it)\n- Deep nesting or complex conditionals (prefer early returns and guard clauses)\n- Magic numbers/strings without named constants\n- Poor naming: generic names (`data`, `result`, `temp`), missing verb prefixes on functions, missing `is`/`has`/`can`/`should` prefixes on booleans\n- Missing error handling or error boundaries\n- Incomplete TypeScript type coverage (no `any`, no `as any` casts)\n- Comments that explain WHAT instead of WHY\n- Commented-out code (use version control)\n- Boolean parameters that switch component behavior (use two components instead)\n- Mutable patterns where `const` or immutable alternatives exist\n- Production files exceeding ~500 lines (red flag at 600+)\n- `console.log` in production code (Biome flags this)\n\n### 4. Testing Impact Review\n\nCheck for:\n- Changes to data-testid attributes that may break E2E tests\n- Modified component interfaces that require test updates\n- New interactive elements missing data-testid attributes\n\n### 5. Pre-Commit Verification\n\nFor pending-change reviews, verify:\n- `npm run format` (Biome formatter) — zero diffs\n- `npm run lint` (Biome linter) — zero errors\n- `npm test` (Jest) — zero failures\n\n## Required Output Format\n\nWhen this skill is invoked, the response must exactly follow one of the two templates:\n\n### Template A (any findings)\n\n```markdown\n# Code Review\n\nFound <N> urgent issues that need to be fixed:\n\n## 1. <brief description of issue>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n\n### Suggested fix\n\n<brief description of suggested fix with code example>\n\n---\n\n... (repeat for each urgent issue) ...\n\nFound <M> suggestions for improvement:\n\n## 1. <brief description of suggestion>\n\nFilePath: <path> line <line>\n<relevant code snippet or pointer>\n\n\n### Suggested fix\n\n<brief description of suggested fix with code example>\n\n---\n\n... (repeat for each suggestion) ...\n```\n\n- If there are no urgent issues, omit that section. If there are no suggestions, omit that section.\n- If the issue count exceeds 10, summarize as \"10+ urgent issues\" or \"10+ suggestions\" and output only the first 10 items.\n- Do not compress the blank lines between sections; keep them as-is for readability.\n- If Template A is used (there are issues to fix) and at least one issue requires code changes, append a brief follow-up question after the structured output asking whether the user wants the suggested fixes applied. For example: \"Would you like me to apply the suggested fixes to address these issues?\"\n\n### Template B (no issues)\n\n```markdown\n## Code Review\n\nNo issues found.\n```\n\n<!-- File: .agents/skills/frontend-i18n/SKILL.md (Tokens: ~1003 | Category: anthropic-skill) -->\n---\nname: frontend-i18n\ndescription: Add, change, or review user-facing text in the Langflow frontend using the i18n system (i18next / react-i18next). Use whenever a change adds or edits UI strings — labels, buttons, tooltips, modals, toasts, error messages, empty states — or when reviewing a diff that contains user-facing text. Every user-facing string must go through the translation system and every new key must exist in ALL locale files. Do NOT use for backend strings, log messages, or code identifiers.\n---\n\n# Frontend i18n\n\nThe Langflow frontend is internationalized with **i18next + react-i18next**. Locales live in `src/frontend/src/locales/` — currently `en`, `de`, `es`, `fr`, `ja`, `pt`, `zh-Hans`. A hardcoded user-facing string, or a key missing from one locale, ships broken UI for part of the user base.\n\n## The two rules\n\n1. **Every user-facing string goes through `t(...)`** — never hardcoded JSX text for labels, buttons, tooltips, modals, toasts, errors, placeholders, `aria-label`s, or empty states.\n2. **Every new key is added to ALL locale files in the same PR** (`en.json`, `de.json`, `es.json`, `fr.json`, `ja.json`, `pt.json`, `zh-Hans.json`). `fallbackLng: \"en\"` means a missing key silently shows English — it won't crash, which is exactly why reviews must catch it.\n\n## How it works here\n\n- Config: `src/frontend/src/i18n.ts` — a custom i18next instance. `en` is bundled statically; other languages are **lazy-loaded** by `loadLanguage()` via dynamic import. Language preference comes from `localStorage.getItem(\"languagePreference\")`, normalized (e.g. `zh-CN` → `zh-Hans`, unknown → `en`).\n- Keys are **flat, dot-namespaced by feature**: `deleteModal.title`, `errors.fileTooLarge`, `crash.restartButton`. Follow the existing namespace of the area you're touching; create a new prefix only for a genuinely new surface.\n- Interpolation uses `{{variable}}` in the string and an options object in the call.\n\n## Adding a string (the pattern)\n\n```tsx\nimport { useTranslation } from \"react-i18next\";\n\nconst { t } = useTranslation();\n\n<span>{t(\"deleteModal.title\")}</span>\n<p>{t(\"errors.fileTooLarge\", { maxSizeMB: \"10MB\" })}</p>\n```\n\nIn `locales/en.json` (and **every** other locale file):\n\n```json\n\"errors.fileTooLarge\": \"The file size is too large. Please select a file smaller than {{maxSizeMB}}.\"\n```\n\nFor the non-English locales, write a real translation when confident; if not, add the key with the English value and flag it in the PR — a present-but-untranslated key is visible and searchable, a missing key is invisible.\n\n## Rules that prevent real bugs\n\n- **Don't concatenate translated fragments** (`t(\"a\") + name + t(\"b\")`) — word order differs across languages. Use one key with interpolation: `t(\"greeting\", { name })`.\n- **Don't put JSX/HTML inside translation strings**; compose in JSX around `t()` calls (see how `crash.descriptionBefore` / `crash.githubIssues` / `crash.descriptionAfter` split around a link).\n- **Plurals**: use i18next plural forms (`_one` / `_other` suffixes), not `count === 1 ? ... : ...`.\n- **Keys are code**: renaming or deleting a key means updating **all seven** locale files — an orphan key in one file is dead weight; a rename missed in one file is a regression.\n- Dates/numbers/currency: format with `Intl.*` using the active locale, never hardcoded formats.\n\n## Review checklist (apply to any diff with UI text)\n\n- [ ] No hardcoded user-facing string in JSX (including `placeholder`, `title`, `aria-label`, toast/error text).\n- [ ] Every new key exists in **all** locale files (grep the key across `src/frontend/src/locales/*.json`).\n- [ ] No concatenation of translated fragments; interpolation used instead.\n- [ ] Removed/renamed keys cleaned up in all locales (no orphans).\n- [ ] Key follows the existing dot-namespace of the feature area.\n\nQuick check for a key across locales:\n\n```bash\nfor f in src/frontend/src/locales/*.json; do grep -L '\"my.new.key\"' \"$f\"; done\n```\n\n(prints the locale files where the key is **missing** — should print nothing)\n\n<!-- File: .agents/skills/frontend-query-mutation/references/query-patterns.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-query-mutation/references/query-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/query-patterns.md)\n\n<!-- File: .agents/skills/frontend-query-mutation/references/runtime-rules.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-query-mutation/references/runtime-rules.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-query-mutation/references/runtime-rules.md)\n\n<!-- File: .agents/skills/frontend-query-mutation/SKILL.md (Tokens: ~893 | Category: anthropic-skill) -->\n---\nname: frontend-query-mutation\ndescription: Guide for implementing Langflow frontend query and mutation patterns with Axios and TanStack React Query v5. Trigger when creating or updating API hooks in controllers/API/queries, consuming UseRequestProcessor in components, deciding whether to use useQuery or useMutation, handling conditional queries, cache invalidation, mutation error handling, or migrating legacy API calls to the query hook pattern.\n---\n\n# Frontend Query & Mutation\n\n## Intent\n\n- Keep API hooks in `controllers/API/queries/{domain}/` as the organized source of truth.\n- Use `UseRequestProcessor` for consistent retry, invalidation, and error handling.\n- Use Axios `api` instance for all HTTP calls (never raw `fetch` for REST).\n- Use `performStreamingRequest()` for streaming operations (build, chat).\n- Keep cache invalidation in mutation `onSettled` callbacks.\n\n## Architecture Overview\n\nLangflow uses **Axios + TanStack React Query v5** for data fetching. There is no oRPC or contract layer. The architecture is:\n\n```\nComponent\n  -> API hook (controllers/API/queries/{domain}/use-{verb}-{resource}.ts)\n    -> UseRequestProcessor (controllers/API/services/request-processor.ts)\n      -> useQuery / useMutation (TanStack React Query)\n        -> queryFn / mutationFn\n          -> api.get/post/patch/delete (Axios instance from controllers/API/api.tsx)\n```\n\n### Key Files\n\n| File | Purpose |\n|------|---------|\n| `controllers/API/api.tsx` | Axios instance, `ApiInterceptor` component, `performStreamingRequest()` |\n| `controllers/API/services/request-processor.ts` | `UseRequestProcessor` hook wrapping `useQuery`/`useMutation` with retry defaults |\n| `controllers/API/helpers/constants.ts` | URL constants (`URLs`) and `getURL()` helper |\n| `controllers/API/queries/{domain}/` | Domain-organized query and mutation hooks |\n| `types/api/index.ts` | `useQueryFunctionType`, `useMutationFunctionType` type helpers |\n| `stores/` | Zustand stores for client-side state |\n\n## Workflow\n\n1. Identify the change surface.\n   - Read `references/query-patterns.md` for hook structure, naming, URL patterns, and query/mutation call-site shape.\n   - Read `references/runtime-rules.md` for conditional queries, invalidation, error handling, and streaming.\n   - Read both references when a task spans hook creation and runtime behavior.\n2. Implement the hook following existing patterns.\n   - Use `UseRequestProcessor` for `query()` and `mutate()` wrappers.\n   - Use the `api` Axios instance for all HTTP calls.\n   - Use `getURL()` for constructing API paths.\n   - Type the hook with `useQueryFunctionType` or `useMutationFunctionType`.\n3. Preserve Langflow conventions.\n   - Query keys are arrays with the hook name: `[\"useGetFlows\"]`.\n   - Mutations invalidate related queries in `onSettled`.\n   - Zustand store updates happen in the query/mutation function, not in components.\n   - `UseRequestProcessor` provides default retry (5 for queries, 3 for mutations) with exponential backoff.\n\n## Files Commonly Touched\n\n- `controllers/API/queries/{domain}/use-get-*.ts` (query hooks)\n- `controllers/API/queries/{domain}/use-post-*.ts` (create mutations)\n- `controllers/API/queries/{domain}/use-patch-*.ts` (update mutations)\n- `controllers/API/queries/{domain}/use-delete-*.ts` (delete mutations)\n- `controllers/API/helpers/constants.ts` (URL constants)\n- `types/api/index.ts` (API type definitions)\n- `stores/` (Zustand store updates triggered by queries)\n\n## References\n\n- Use `references/query-patterns.md` for hook structure, naming conventions, directory layout, and anti-patterns.\n- Use `references/runtime-rules.md` for conditional queries, invalidation, `mutate` versus `mutateAsync`, error handling, and streaming requests.\n\nTreat this skill as the single query and mutation entry point for Langflow frontend work. Keep detailed rules in the reference files instead of duplicating them in project docs.\n\n<!-- File: .agents/skills/frontend-testing/references/async-testing.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-testing/references/async-testing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/async-testing.md)\n\n<!-- File: .agents/skills/frontend-testing/references/checklist.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-testing/references/checklist.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/checklist.md)\n\n<!-- File: .agents/skills/frontend-testing/references/common-patterns.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-testing/references/common-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/common-patterns.md)\n\n<!-- File: .agents/skills/frontend-testing/references/domain-components.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-testing/references/domain-components.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/domain-components.md)\n\n<!-- File: .agents/skills/frontend-testing/references/mocking.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-testing/references/mocking.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/mocking.md)\n\n<!-- File: .agents/skills/frontend-testing/references/workflow.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/frontend-testing/references/workflow.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/langflow-ai/langflow/HEAD/.agents/skills/frontend-testing/references/workflow.md)\n\n<!-- File: .agents/skills/frontend-testing/SKILL.md (Tokens: ~2711 | Category: anthropic-skill) -->\n# Frontend Testing Skill - Langflow\n\n## When to Apply\n\nActivate this skill when:\n- Writing new unit or integration tests for React components, hooks, utilities, or Zustand stores\n- Reviewing existing tests for correctness, coverage, or best practices\n- Improving test coverage for under-tested modules\n- Debugging flaky or failing tests\n- Refactoring test code for maintainability\n\n## Tech Stack\n\n| Technology | Version | Purpose |\n|---|---|---|\n| Jest | 30.x | Test runner and assertion framework |\n| ts-jest | 29.x | TypeScript transform for Jest |\n| React Testing Library | 16.x | Component rendering and DOM queries |\n| @testing-library/user-event | 14.x | Realistic user interaction simulation |\n| @testing-library/jest-dom | 6.x | Extended DOM matchers |\n| jsdom | (via jest-environment-jsdom 30.x) | Browser environment simulation |\n| React | 19.x | UI framework |\n| TypeScript | 5.4 | Type safety |\n| Zustand | 4.x | State management |\n| React Router DOM | 6.x | Client-side routing |\n| @tanstack/react-query | 5.x | Server state management |\n| Axios | 1.x | HTTP client |\n\n## Project Configuration\n\n- **Jest config**: `src/frontend/jest.config.js`\n- **Setup files**: `src/frontend/jest.setup.js` (globals/mocks) and `src/frontend/src/setupTests.ts` (DOM matchers, ResizeObserver, IntersectionObserver, matchMedia)\n- **Path alias**: `@/` maps to `<rootDir>/src/`\n- **Test match patterns**: `src/**/__tests__/**/*.{test,spec}.{ts,tsx}` and `src/**/*.{test,spec}.{ts,tsx}`\n- **Transform**: Custom `transform-import-meta.js` handles `import.meta` for Jest compatibility\n- **Global mocks** (in `jest.setup.js`): `@radix-ui/react-form`, `react-markdown`, `remark-gfm`, `remark-math`, `rehype-mathjax/browser`, `lucide-react/dynamicIconImports`, `@/components/common/genericIconComponent`, `@/icons/BotMessageSquare`, `@/stores/darkStore`, `localStorage`, `sessionStorage`, `crypto`\n\n## Key Commands\n\n```bash\n# Run all tests\nnpm test\n\n# Run a specific test file\nnpm test -- path/to/file.test.tsx\n\n# Run tests matching a pattern\nnpm test -- --testPathPattern=\"alertStore\"\n\n# Run tests in watch mode\nnpm run test:watch\n\n# Run tests with coverage\nnpm run test:coverage\n\n# Run a single test file with coverage\nnpm test -- --coverage --collectCoverageFrom='src/path/to/source.ts' path/to/__tests__/source.test.ts\n```\n\n## File Naming and Location\n\nTest files follow one of two patterns:\n\n1. **Dedicated `__tests__` directory** (preferred for components and modules):\n   ```\n   src/components/core/my-component/\n   ├── my-component.tsx\n   └── __tests__/\n       └── my-component.test.tsx\n   ```\n\n2. **Co-located test file** (acceptable for utilities and simple modules):\n   ```\n   src/utils/\n   ├── myUtil.ts\n   └── myUtil.test.ts\n   ```\n\nNaming convention: `ComponentName.test.tsx` for components, `hook-name.test.ts` for hooks, `util-name.test.ts` for utilities.\n\n**Do NOT use `.spec.tsx`** -- while technically matched, the project convention is `.test.tsx`.\n\n## Test Structure Template\n\n```tsx\nimport { render, screen } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport MyComponent from \"../MyComponent\";\n\n// Mock dependencies (use jest.mock, NOT vi.mock)\njest.mock(\"@/controllers/API/api\", () => ({\n  get: jest.fn(),\n  post: jest.fn(),\n}));\n\ndescribe(\"MyComponent\", () => {\n  beforeEach(() => {\n    jest.clearAllMocks();\n  });\n\n  describe(\"rendering\", () => {\n    it(\"should render the component with default props\", () => {\n      // Arrange\n      render(<MyComponent />);\n\n      // Act - (none for render test)\n\n      // Assert\n      expect(screen.getByRole(\"button\", { name: /submit/i })).toBeInTheDocument();\n    });\n  });\n\n  describe(\"user interactions\", () => {\n    it(\"should call onSubmit when the form is submitted\", async () => {\n      // Arrange\n      const user = userEvent.setup();\n      const onSubmit = jest.fn();\n      render(<MyComponent onSubmit={onSubmit} />);\n\n      // Act\n      await user.click(screen.getByRole(\"button\", { name: /submit/i }));\n\n      // Assert\n      expect(onSubmit).toHaveBeenCalledTimes(1);\n    });\n  });\n});\n```\n\n## Incremental Testing Workflow\n\nWhen testing a directory with multiple files, follow this order:\n\n1. **Identify all source files** in the target directory\n2. **Order by complexity** (simplest first):\n   - Pure utility functions (no React, no side effects)\n   - Constants and configuration objects\n   - Custom hooks (no UI rendering)\n   - Simple presentational components (no state, no side effects)\n   - Stateful components with local state\n   - Components using Zustand stores\n   - Components with API calls or complex async behavior\n   - Integration-level components that compose many children\n3. **For each file**:\n   a. Read the source file completely\n   b. Identify all exported functions, components, and types\n   c. Write tests covering all branches and edge cases\n   d. Run the tests and fix any failures\n   e. Check coverage and add tests for uncovered lines\n   f. Move to the next file\n4. **Run full directory coverage** at the end to verify\n\n## Complexity-Based Test Ordering\n\nWithin a single test file, order test cases from simplest to most complex:\n\n1. Default rendering / initial state\n2. Props variations and conditional rendering\n3. User interactions (clicks, typing, form submission)\n4. Async operations (API calls, timers)\n5. Error states and edge cases\n6. Integration with stores or context\n7. Cleanup and unmount behavior\n\n## Core Principles\n\n### Arrange-Act-Assert (AAA)\nEvery test should have a clear three-phase structure. Use blank lines to separate each phase for readability.\n\n### Black-Box Testing\nTest the component from the user's perspective. Query by role, label, text, or `data-testid` -- never by CSS class, internal state variable, or implementation detail.\n\n### Single Behavior Per Test\nEach `it()` block should verify exactly one behavior. If you need to write \"and\" in the test name, split it into two tests.\n\n### Semantic Test Names\nUse descriptive names that explain the expected behavior:\n- Good: `\"should disable the submit button when the form is invalid\"`\n- Bad: `\"button test\"` or `\"test 1\"`\n\nFormat: `\"should [expected behavior] when [condition]\"`\n\n## Required Test Scenarios\n\nFor every component, cover at minimum:\n\n### Rendering\n- Default render with no optional props\n- Render with all optional props provided\n- Conditional rendering branches (if/else in JSX)\n\n### Props and State\n- Each prop variation that changes rendered output\n- Default prop values\n- State transitions triggered by user actions\n\n### User Interactions\n- Click handlers\n- Form input and submission\n- Keyboard navigation (if applicable)\n- Hover/focus states (if applicable)\n\n### Challenge Tests (MANDATORY — not optional)\n\n**Happy path tests alone are NOT enough.** They only confirm the code works when everything is perfect. Real bugs hide in the cracks. You MUST write tests that actively TRY TO BREAK the code:\n\n**Unexpected inputs:**\n- `null`, `undefined`, `\"\"`, `[]`, `{}`, `0`, `-1`, `NaN`, `Infinity`\n- What happens when a required prop is missing?\n- What happens when data from the API comes back with missing fields?\n\n**Boundary values:**\n- Max length strings (paste 10,000 chars in an input)\n- Exactly at the limit, one past the limit\n- Zero items, one item, maximum items\n- First page, last page, out-of-range page\n\n**Malformed data:**\n- API returns `{ data: null }` instead of `{ data: [] }`\n- JSON with extra unexpected fields\n- Dates in wrong format, numbers as strings\n\n**Error states:**\n- Network failure (API rejects with 500)\n- Authentication expired mid-action (401)\n- Resource not found (404)\n- Permission denied (403)\n- Timeout\n\n**What should NOT happen:**\n- Verify that deleting a flow does NOT delete flows from other users\n- Verify that a read-only user CANNOT trigger write mutations\n- Verify that XSS payloads in user input are sanitized\n\n**Rapid/concurrent actions:**\n- Double-click on submit button\n- Rapid repeated API calls\n- Unmount component while async operation is in flight\n\n**Write tests based on REQUIREMENTS, not on what the source code does.** This is how you catch bugs where the code diverges from expected behavior.\n\n**When a test fails:** first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.\n\n### Async Behavior\n- Loading states\n- Success states\n- Error states\n- Timeout/retry behavior\n\n## Coverage Goals\n\nPer source file:\n- **Function coverage**: 100%\n- **Branch coverage**: > 95%\n- **Line coverage**: > 95%\n- **Statement coverage**: > 95%\n\nRun coverage for a specific file:\n```bash\nnpm test -- --coverage --collectCoverageFrom='src/path/to/file.ts' src/path/to/__tests__/file.test.ts\n```\n\n## Important Rules\n\n1. **Never use Vitest APIs**: Use `jest.fn()`, `jest.mock()`, `jest.spyOn()`, `jest.mocked()` -- never `vi.*` equivalents.\n2. **Never mock base UI components** from `@/components/ui/` -- render them as-is.\n3. **Check `jest.setup.js` before mocking**: Many modules are already globally mocked (darkStore, genericIconComponent, react-markdown, radix-form, etc.). Do not re-mock them.\n4. **Use `@testing-library/user-event`** over `fireEvent` for user interactions.\n5. **Wrap state updates in `act()`** when testing Zustand stores or React state changes.\n6. **Clean up after each test**: Use `beforeEach(() => jest.clearAllMocks())` and `afterEach` for timers.\n7. **Always write both happy path AND adversarial tests** (null, undefined, empty values, boundary conditions, error states).\n8. **Minimum coverage: 75%** (target 80%). Below 75% the task is not complete.\n\n## Forbidden Test Anti-Patterns\n\n| Pattern | Problem | How to Detect |\n|---------|---------|---------------|\n| **The Liar** | Test passes but doesn't verify the behavior it claims to test | Assertions don't match the test name |\n| **The Mirror** | Test reads source code and asserts exactly what the code does — finds zero bugs | Test would never fail even if logic changes |\n| **The Giant** | 50+ lines of setup, multiple acts, dozens of assertions | Should be 5+ separate tests |\n| **The Mockery** | So many mocks that the test only tests the mock setup | Count mocks — if > 3 deep, rethink |\n| **The Inspector** | Coupled to implementation details, breaks on any refactor | Tests internal state instead of behavior |\n| **The Chain Gang** | Tests depend on execution order or share mutable state | Tests fail when run in isolation |\n| **The Flaky** | Sometimes passes, sometimes fails with no code changes | Non-deterministic assertions or timing issues |\n\n## References\n\n- [Incremental Testing Workflow](references/workflow.md) - Step-by-step process for testing directories\n- [Mocking Guide](references/mocking.md) - Patterns for mocking APIs, stores, routers, and context\n- [Async Testing](references/async-testing.md) - Patterns for async operations, timers, and waitFor\n- [Common Patterns](references/common-patterns.md) - Query priority, events, forms, modals, data-driven tests\n- [Test Checklist](references/checklist.md) - Pre-submission verification checklist\n- [Domain Components](references/domain-components.md) - Langflow-specific component testing patterns\n- [Component Template](assets/component-test.template.tsx) - Starter template for component tests\n- [Hook Template](assets/hook-test.template.ts) - Starter template for hook tests\n- [Utility Template](assets/utility-test.template.ts) - Starter template for utility tests\n\n<!-- ... +14 more modular skills available via /api/skills/langflow-ai/langflow?path={path} -->\n</yakaai_skills>"}