lfx-preflight/SKILL.md
Pre-PR validation for any LFX repo — Phase 1 auto-fixes (license, format, lint, build), Phase 2 code review (15 report-only checks for Angular). Adapts to repo type. Use before submitting any PR. Pass --skip-review to skip Phase 2.
npx skillsauth add linuxfoundation/lfx-skills lfx-preflightInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are running a comprehensive validation before the contributor submits a pull request. Adapt checks based on the repo type.
Two phases:
Modes:
--skip-review: Run Phase 1 only, skip Phase 2. Useful during development when you just want to validate build/lint.if [ -f apps/lfx-one/angular.json ] || [ -f turbo.json ]; then
echo "REPO_TYPE=angular"
elif [ -f go.mod ]; then
echo "REPO_TYPE=go"
fi
Mechanical checks that can be auto-fixed. Runs for all repo types.
git status
git diff --stat origin/main...HEAD
git log --format="%h %s%n%b" origin/main...HEAD
Evaluate:
LFXV2- references.--signoff? — Flag any commits without Signed-off-by: lines.Angular repos:
./check-headers.sh
Every source file (.ts, .html, .scss) must have the license header.
If missing headers are found (auto-fix mode):
.ts files, prepend: // Copyright The Linux Foundation and each contributor to LFX.\n// SPDX-License-Identifier: MIT\n\n.html files, prepend: <!-- Copyright The Linux Foundation and each contributor to LFX. -->\n<!-- SPDX-License-Identifier: MIT -->\n\n.scss files, prepend: // Copyright The Linux Foundation and each contributor to LFX.\n// SPDX-License-Identifier: MIT\n\nGo repos:
Check for license headers in .go files. The standard Go license header format varies by repo — check existing files for the pattern.
Angular repos:
yarn format
This auto-fixes formatting. If files changed, report which ones were formatted.
Go repos:
gofmt -l .
# If files need formatting (auto-fix mode):
gofmt -w .
Angular repos:
yarn lint
Go repos:
go vet ./...
# If golangci-lint is available:
golangci-lint run ./...
If lint errors are found (auto-fix mode):
If fixes were applied in Checks 2-4, re-run lint to confirm:
Angular: yarn lint
Go: go vet ./...
Angular repos:
yarn build
Go repos:
go build ./...
# If Goa design was modified:
make apigen
go build ./...
If build fails:
Angular repos:
# Check if tests exist for modified files
git diff --name-only origin/main...HEAD | grep '\.spec\.ts$'
# If test files exist:
# yarn test --watch=false (only if the user confirms — tests can be slow)
Go repos:
# Run tests for packages with changes
go test ./...
Report test results but don't block on test failures unless the user asks.
git diff --name-only origin/main...HEAD
Angular repos — flag changes to:
apps/lfx-one/src/server/server.tsapps/lfx-one/src/server/server-logger.tsapps/lfx-one/src/server/middleware/*apps/lfx-one/src/server/services/logger.service.tsapps/lfx-one/src/server/services/microservice-proxy.service.tsapps/lfx-one/src/server/services/nats.service.tsapps/lfx-one/src/server/services/snowflake.service.tsapps/lfx-one/src/server/services/supabase.service.tsapps/lfx-one/src/server/services/ai.service.tsapps/lfx-one/src/server/services/project.service.tsapps/lfx-one/src/server/services/etag.service.tsapps/lfx-one/src/server/helpers/error-serializer.tsapps/lfx-one/src/app/app.routes.ts.husky/*eslint.config.*.prettierrc*turbo.jsonapps/lfx-one/angular.jsonCLAUDE.mdcheck-headers.shpackage.json / */package.jsonyarn.lockGo repos — flag changes to:
gen/ (should only change via make apigen)charts/ (deployment config — review carefully)go.mod / go.sum (dependency changes need review)Makefile (build system changes)git status
git log --format="%h %s%n%b" origin/main...HEAD
type(scope): description format--signoff on all commits?git diff --stat origin/main...HEAD
List:
charts/)Skip this entire phase for Go repos or if --skip-review was passed.
These checks are always report-only — they never auto-fix. The transformations are not safe to do mechanically (wrapper replacements need FormGroup wiring, ! in .ts can be definite assignment, BehaviorSubject → signal needs cross-file changes, ChangeDetectorRef removal isn't always safe).
Scope all checks to changed files only (git diff --name-only origin/main...HEAD). These catch the patterns most commonly flagged by reviewers across 20+ LFX PRs.
Search changed .html files for raw form elements that must use LFX wrappers:
| Raw Element | Required Wrapper |
| --- | --- |
| <input | lfx-input-text (or other lfx-input-* variant) |
| <select | lfx-select |
| <textarea | lfx-textarea |
| <div with animate-pulse class | <p-skeleton> from PrimeNG |
Exceptions: Elements inside comments, or <input type="hidden"> are acceptable.
Note: LFX wrappers require FormGroup + FormControl — ngModel is not supported.
Severity: BLOCKER — reviewers will always flag this.
For TypeScript-only items, search all changed .ts files for:
providers: [...] entries in component metadata where the service is never injected via inject() or constructorFor unbound component outputs, search changed .html templates (and, as needed, the referenced child component's .ts definition to see its @Output()s or output()s) for cases where a template uses a child component (e.g., <lfx-votes-table>) but does not bind its emitted outputs (e.g., viewVote, rowClick, refresh). Missing output bindings mean user interactions silently do nothing.
Severity: BLOCKER for unused providers/imports and unbound outputs. DISCUSS for unused methods (may be used externally).
Search changed *.component.ts files for service injection count (count inject() calls and constructor injections):
Severity: DISCUSS — guideline, not a hard rule.
Search changed .html templates and .ts files for:
{{ count() }} or {{ stats().total }} without a surrounding @if (loading()) guard. These show 0 during loading instead of a placeholder.@if (loading()) / @else pattern.@for loops rendering data without a loading skeleton before data arrives.loading signal set to false after a fetch completes, but never set back to true when a new fetch starts (e.g., inside switchMap when input changes). Fix: set loading.set(true) at the start of each switchMap callback.Every data display that starts empty and populates asynchronously needs an explicit loading branch showing —, <p-skeleton>, or equivalent.
Severity: BLOCKER for showing 0 during load. DISCUSS for missing re-fetch reset.
Search changed .html templates for:
!) — patterns like data()!.field or item!.property in templates. These cause runtime crashes when the value is null/undefined. Use ?. and @if (data(); as d) guards instead.Search changed .ts files for:
!) — in .ts files, ! is also used for definite assignment (foo!: T) and may already be runtime-guarded. Report for manual review only — do not suggest automatic replacement.|| vs nullish ?? — using || where ?? is needed. value || null treats 0, "", and false as falsy — hiding valid zero counts (e.g., total_members || null hides 0 members). Use ?? to only coalesce on null/undefined.Severity: BLOCKER for ! assertions in templates. DISCUSS for ! in .ts and || vs ??.
Search changed .ts files for:
catchError — catchError(() => of([])) or catchError(() => EMPTY) without any logging before the fallback. Every catchError should log via logger service or console.error at minimum.catchError that returns a default (e.g., of([])), a component-level catchError on the same stream is unreachable dead code. Handle errors in one place — either the service or the component, not both.EMPTY and of([]) in the same service. Pick one pattern.git diff for removed console.error or logger.error calls that weren't replaced.Severity: BLOCKER for silent or unreachable catchError. DISCUSS for inconsistent fallbacks.
Search changed *.component.ts and *.service.ts files for:
BehaviorSubject for simple state — should use signal() instead. BehaviorSubject is only appropriate for complex async streams that need RxJS operators.cdr.detectChanges() or ChangeDetectorRef — often not required in zoneless Angular 20 when using Angular's reactive primitives (signals, AsyncPipe, toSignal, etc.), but may still be needed for integrations with non-Angular event sources or advanced OnPush patterns. Flag for manual review.model() for internal state — model() creates a two-way bindable input/output on the component's public API. For internal-only state (e.g., dialog visibility, drawer toggles not exposed to parents), use signal() instead. Only use model() when the parent component needs two-way binding (e.g., [(visible)]="childVisible").component-organization.md, simple WritableSignals must be initialized directly (e.g., loading = signal(false)), not in the constructor.Severity: BLOCKER for BehaviorSubject misuse. DISCUSS for ChangeDetectorRef and model() usage.
Search changed .ts files for API calls and verify:
limit for paginationpage_size for paginationIf you cannot verify the upstream contract from the local codebase, flag for manual verification.
Severity: BLOCKER for clearly wrong parameter names. DISCUSS for fields needing upstream verification.
Check the git log and diff for changes that need explicit documentation in the PR description:
Severity: DISCUSS
Search changed .html templates for:
aria-pressed on toggle buttons — button groups acting as toggles must have [attr.aria-pressed]="isActive()".<div (click)> containing an <lfx-button> or <a>.[attr.tabindex]="-1", inert, or conditionally render elements.aria-label on icon-only buttons.Severity: DISCUSS
Search changed .html templates for hardcoded Tailwind color classes that should use LFX design tokens:
bg-blue-50, text-gray-300, border-blue-100, etc. Check tailwind.config.js for the custom LFX color palette. Raw Tailwind defaults are not design tokens.Severity: DISCUSS
Search changed .ts files for per-item API calls inside loops:
.map(item => this.http.get('/api/' + item.id)) or forkJoin(items.map(...)) where a batch endpoint exists.await inside for/forEach/.map() loops calling microserviceProxy.proxyRequest().Severity: DISCUSS
Search changed .html templates and .component.ts files for:
@switch cases — if a component defines tabs/routes/modes in a config array, every entry must have a corresponding @case in the template. A tab in config without a matching case renders blank content.activeTab not constrained to visible set — if tabs are conditionally visible, ensure activeTab resets to a valid tab when the visible set changes.Severity: BLOCKER for missing switch cases. DISCUSS for partial wiring.
Search changed *.component.ts files for:
if (!this.data()) guards that only load data on first render, not when route params change.loading or saving signals, leaving the UI stuck.track $index in @for loops — causes unnecessary DOM churn when items reorder. Prefer track item.uid or a stable identifier.Severity: DISCUSS
Search changed .html templates for:
@if (!isVisitor()) evaluates to true while myRoleLoading() is still true (because isVisitor() defaults to false). Fix: add !myRoleLoading() to the guard.canEdit(), isVisitor(), hasPMOAccess() checks, flag for PR description.Severity: BLOCKER for content flashing during role loading. DISCUSS for blur bypass.
Output the two-phase report:
═══════════════════════════════════════════
PREFLIGHT RESULTS
═══════════════════════════════════════════
PHASE 1: VALIDATION
═══════════════════
✓ Working tree — Clean, N commits ahead of main
✓ License headers — All files have headers (2 auto-fixed)
✓ Formatting — Applied (3 files reformatted)
✓ Linting — No errors
✓ Build — Succeeded
✓ Tests — N/A (no test files for changed code)
✓ Protected files — None modified
✓ Commits — Conventions followed, signed off
Changes summary:
- 2 files modified in packages/shared/
- 3 files modified in apps/lfx-one/src/app/modules/committees/
- 0 protected files touched
Auto-fixes applied:
- Added license header to member-form.component.ts
- Added license header to member-form.component.html
- Reformatted 3 files with prettier
PHASE 2: CODE REVIEW (Angular)
══════════════════════════════
1. ✓ Raw HTML elements — No raw inputs/selects/textareas found
2. ⚠ Dead code — 2 unused imports in settings.service.ts
3. ✓ Component size — All components under 4 injections
4. ✓ Loading states — All data displays have loading guards
5. ✓ Type safety — No non-null assertions or || vs ?? issues
6. ✓ Error handling — All catchError blocks have logging
7. ✓ Signal patterns — No BehaviorSubject or ChangeDetectorRef issues
8. ✓ API alignment — Parameters match upstream contracts
9. ✓ PR description — Behavioral changes documented
10. ⚠ Accessibility — Toggle buttons missing aria-pressed (discuss)
11. ✓ Design tokens — No hardcoded color classes
12. ✓ N+1 patterns — No per-item API calls in loops
13. ✓ Template completeness — All config entries have matching template cases
14. ✓ Stale data — Components re-fetch on input changes
15. ✓ Visitor gating — Permission guards include loading checks
═══════════════════════════════════════════
REVIEW READY (2 discussion items)
═══════════════════════════════════════════
Legend:
✓ — Pass. No issues found.⚠ — Discuss. Not a blocker, but should be considered.✗ — Blocker. Must be fixed before submitting the PR.Final verdict line:
✓ → REVIEW READY⚠ only → REVIEW READY (N discussion items)✗ in Phase 1 or Phase 2 → NOT READY — N blockers must be fixedFor each blocker, provide:
After auto-fixing, check for uncommitted changes:
git status --porcelain
If there are uncommitted changes from auto-fixes, ask:
"Preflight auto-fixed some issues (formatting, license headers). Would you like me to commit these fixes?"
"Your changes look good and are ready for review! Would you like me to create the pull request?"
Report failures in plain language, explaining what each means:
"Found 2 issues that need attention:
- Build error: [plain explanation of what went wrong and whether it can be auto-fixed]
- Missing signoff: [plain explanation and what the user needs to do]
Want me to fix what I can automatically?"
This skill DOES:
This skill does NOT:
/lfx-backend-builder or /lfx-ui-builder)/lfx-product-architect)/lfx-research)tools
Create a new ticket in the LFXV2 Jira project (linuxfoundation.atlassian.net). Guides the user through picking an issue type (Bug, Story, Task, Epic), writing a concise summary, and capturing the requirement, feature, or bug context — collecting reproduction steps for bugs. Optionally attaches a parent epic, labels, or priority if the user provides them. Submits the ticket via Atlassian MCP and returns the URL. Use this skill any time someone asks to "create a Jira ticket", "open an LFXV2 ticket", "file a bug", "log a story", "write up a feature request", "draft a ticket", or any variation of submitting work into LFXV2.
development
Starting point for LFX development. Describe what you want in plain language and this skill routes you to the right workflow.
development
Generate compliant Angular 20 frontend code — components, services, templates, drawers, pagination UI, and styling. Encodes signal patterns, component structure, PrimeNG wrapper strategy, and all frontend conventions. Only activates in Angular repos.
testing
Combine multiple feature branches across repos into worktrees for end-to-end journey testing. Create, refresh, and teardown integration environments that merge branches from multiple repos.