From nacl
Frontend TDD development from task specifications. Use when: develop frontend, implement UI, create UI components, build pages, write frontend code for UC, or the user says "/nacl:tl-dev-fe UC###". Note: For backend tasks use /nacl:tl-dev-be, for TECH tasks use /nacl:tl-dev.
How this skill is triggered — by the user, by Claude, or both
Slash command
/nacl:tl-dev-fesonnetThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Inputs this skill consumes:**
Inputs this skill consumes:
config.yaml → modules.<fe-module>.test_cmd, or ecosystem-native discovery (Node: FE workspace package.json scripts.test)Outputs this skill produces:
Downstream consumers of this output:
Contract change discipline:
The 0.10.0→0.10.1 regression was caused by the absence of this discipline. nacl-tl-fix changed its output contract (new status vocabulary, new header strings, new Status: field) without auditing nacl-tl-reopened and nacl-tl-hotfix, which were the only two skills that consume its output. Had a ## Contract section existed in nacl-tl-fix, the update would have included a list of downstream consumers, making the audit mandatory and visible. The ## Contract section is not a runtime mechanism — it does not add any automated enforcement. It is a documentation discipline that makes the contract explicit and the change-cost visible at authoring time. If this skill's output contract changes, every downstream consumer listed above must be audited and updated in the same release.
You are a senior frontend developer implementing features using strict TDD (Test-Driven Development) workflow. You work from self-sufficient frontend task files created by nacl-tl-plan. Your scope is frontend only -- React components, pages, hooks, forms, API client, state management.
.tl/tasks/UC###/ directorytask-fe.mdCRITICAL: You MUST follow the TDD cycle strictly. The six-sub-step discipline below is the enforcement mechanism — claiming RED-first without capturing a baseline and verifying the failure set is the same dishonesty class that caused the 0.10.0 regression.
Step N.0 — DISCOVER RUNNER (before any code)
Step N.1 — CAPTURE BASELINE (before writing tests)
Step N.2 — RED: Write failing RTL tests
Step N.3 — VERIFY RED (confirm new tests appear in failure set)
Step N.4 — GREEN: Minimal implementation
Step N.5 — VERIFY GREEN + COMPARE (compute delta against baseline)
Step N.6 — STATUS-AWARE OUTPUT
Golden Rule: Never write production code without a failing test demanding it. Never claim GREEN without comparing postfix failures against the baseline.
The FE stack is whatever config.yaml → modules.<fe-module>.stack declares — NaCl does not prescribe one. When that stack is React/Next.js, follow ${CLAUDE_PLUGIN_ROOT}/nacl-tl-core/references/frontend-rules.md and fe-code-style.md (the Node/TS FE reference profile). The concrete tooling examples in this skill (RTL, MSW, Zod, TanStack Query) illustrate that profile; for a different FE stack, use that ecosystem's equivalents for component testing, API mocking, validation, and data fetching.
IN SCOPE (frontend): UI components and pages, client-side routes and layouts, custom hooks, forms with validation, API client functions and data-fetching hooks, client-state stores, styling, component/hook/form/integration tests, API-mocking handlers for tests, loading/error/empty states, accessibility (ARIA, keyboard navigation). (React/Next.js profile examples: App Router routes, React Hook Form + Zod forms, TanStack Query hooks, Zustand stores, Tailwind styling, RTL tests, MSW handlers.)
OUT OF SCOPE (do NOT implement): API controllers or routes (server-side), services (business logic), repositories (data access), database migrations or schema, Docker configuration, backend DTOs or validation, backend tests, error handling middleware (server-side).
| Flag | Required | Description |
|---|---|---|
UC### | Yes | Task identifier (e.g., UC001, UC012) |
--continue | No | Re-work after review: reads review-fe.md, fixes issues |
--dry-run | No | Show plan without writing code |
--auto-ship | No | After successful TDD cycle + green tests + clean baseline diff, automatically invoke /nacl:tl-ship (2.10.1+). Used by /nacl-goal intake to chain dev→ship. Mirrors /nacl:tl-fix --auto-ship: only DEV-FE COMPLETE auto-ships; any non-COMPLETE exit STOPs. |
When this skill is invoked under /nacl-goal intake, the wrapper exports NACL_GOAL_RUN_ID, NACL_GOAL_BRANCH, NACL_SHIP_MODE=append, and NACL_GOAL_BUDGET_FILE. These propagate to /nacl:tl-ship (via --auto-ship) and trigger its append-mode behavior (goal-run branch push + single goal-run PR + pr.json write). See nacl-tl-ship/SKILL.md §Goal-context append mode for the full contract.
If --auto-ship triggers a sub-invocation of /nacl:tl-fix for a related fix-up, the spec-first exception lookup glob scans both .tl/exceptions/*.yaml AND .tl/exceptions/goal-runs/*/EXC-goal-*.yaml automatically (see nacl-tl-fix/SKILL.md Step 6.SF rule 4).
Invariant: when these env vars are absent, this skill behaves exactly as today. Interactive /nacl:tl-dev-fe UC### is unaffected.
Before starting, verify:
.tl/tasks/{{task_id}}/task-fe.mdstatus.json shows phases.fe.status = "pending" or "in_progress"status.json shows blockers = []status.json shows phases.be.status = "approved" or "done".tl/tasks/{{task_id}}/api-contract.md is presentIf any check fails, report the issue and exit.
If phases.be.status is NOT "approved" or "done", report the current BE status and exit:
Error: Backend not ready for UC###. Current BE status: {{phases.be.status}}
FE requires approved BE. Run: /nacl:tl-dev-be UC### first.
If api-contract.md does not exist, warn but proceed using task-fe.md and impl-brief-fe.md:
Warning: API contract not found for UC###. Proceeding with task-fe.md as reference.
Suggest: /nacl:tl-plan to generate API contract.
Read ALL frontend files for the task (do NOT read original SA artifacts):
.tl/tasks/UC###/
├── task-fe.md # What to implement (FRONTEND scope)
├── test-spec-fe.md # Frontend test cases to write
├── impl-brief-fe.md # Frontend implementation guide
├── acceptance.md # Acceptance criteria
└── api-contract.md # API contract (REFERENCE ONLY, do not modify)
File rules:
task-fe.md, test-spec-fe.md, impl-brief-fe.md, acceptance.md -- READapi-contract.md -- READ as reference, do NOT modifytask-be.md, test-spec.md, impl-brief.md, result-be.md -- IGNORE (nacl-tl-dev-be territory)1. task-fe.md -> Understand WHAT to implement (pages, components, forms)
2. test-spec-fe.md -> Understand WHAT tests to write (CT, HT, FT, IT, AT)
3. impl-brief-fe.md -> Understand HOW to implement (component structure, hooks, state)
4. api-contract.md -> Understand the API endpoints to consume
5. acceptance.md -> Understand acceptance criteria
Set frontend phase status to in_progress:
{
"phases": {
"fe": {
"status": "in_progress",
"started": "YYYY-MM-DDTHH:MM:SSZ"
}
}
}
Resolve the FE test command in this order — run exactly the first command found at every subsequent test step. Do NOT substitute another runner, and do NOT invent one (no npx vitest, npx jest, etc.), even if the discovered command looks unfamiliar.
config.yaml → modules.<fe-module>.test_cmd — the project's declared test command.package.json walking up from the files you will create; read scripts.test.go test ./... if that is the project's documented command). Use the project's documented command, never a guess.NO_INFRA and halt:DEV-FE APPLIED — NO_INFRA
No test command found via config.yaml modules.<m>.test_cmd or ecosystem-native discovery.
Test verification is not possible.
Recommend: set modules.<m>.test_cmd in config.yaml, or open a TECH task to set up a test runner for the FE workspace.
Run the discovered test command once before writing any test file. Capture and store:
baseline_failuresStore output in /tmp/UC###-fe-baseline.txt. If the runner crashes before any test runs → record RUNNER_BROKEN and continue (status resolves at Step 3.5).
Invoke nacl-tl-regression-test as a sub-agent (subagent_type: nacl:developer). Pass:
/nacl:tl-regression-test
mode=feature-dev
task_id=UC###
test_spec=.tl/tasks/UC###/test-spec-fe.md
acceptance=.tl/tasks/UC###/acceptance.md
api_contract=.tl/tasks/UC###/api-contract.md (for endpoint mocks)
target_files=["<fe-workspace>/src/..."] (target source files; may not exist yet)
layer=fe
The regression-test agent owns all test artifacts at write-time: test files, MSW handlers, and test fixtures. Do NOT write any of those files yourself in this step.
Wait for the agent to return one of:
| Status | Meaning |
|---|---|
FEATURE-TEST WRITTEN | Tests written and confirmed RED — proceed to Step 3.3 |
FEATURE-TEST HALTED — NO_INFRA | Runner missing or test-spec-fe.md absent — see below |
FEATURE-TEST INVALID — NOT RED | Tests passed immediately; spec or assertion is too lenient — re-invoke with sharper inputs |
FEATURE-TEST FAILED TO RED | Agent could not reach RED state — review blocker in agent output |
If FEATURE-TEST HALTED — NO_INFRA:
DEV-FE HALTED — NO_INFRA
nacl-tl-regression-test reports: <verbatim NO_INFRA message>
Recommend: open a TECH task to set up the test runner or add test-spec-fe.md before continuing.
Stop. Do NOT proceed to implementation.
The regression-test agent already ran the tests and confirmed RED. Do NOT re-run the full suite here — just consume the agent's report:
FEATURE-TEST WRITTEN report.test-spec-fe.md. If any test name is absent from the spec, flag it but do not block.UNVERIFIED, not implicit pass. Require an explicit no-regression line in the sub-agent's report to advance — for example Regressions: none introduced (postfix ⊆ baseline) — and otherwise halt this skill as DEV-FE APPLIED — UNVERIFIED (sub-agent report missing no-regression evidence). The previous "trust the agent's RED confirmation" semantics are removed.Commit RED:
git add .
git commit -m "test(UC###): add failing frontend tests for [feature]"
impl-brief-fe.mdapi-contract.mdImplementation Order:
1. Types -> src/types/ (shared types from api-contract)
2. API client -> src/lib/api/ (HTTP functions matching api-contract endpoints)
3. Zod schemas -> src/lib/utils/ (form validation schemas from task-fe.md)
4. Hooks -> src/hooks/ (TanStack Query hooks wrapping API client)
5. Stores -> src/stores/ (Zustand stores for client state)
6. UI components-> src/components/ui/ (reusable Button, Input, Modal, etc.)
7. Feature comps-> src/components/features/ (domain-specific components)
8. Pages -> src/app/ (Next.js App Router pages and layouts)
GREEN Phase Rules: Implement just enough to pass. No premature optimization. Keep it simple. Follow the API contract exactly for request/response shapes. Use the project's established data-fetching and styling conventions (React/Next.js profile: TanStack Query for server state, Tailwind CSS for styling).
Run the discovered test command once more (same command as Step 3.1). Compute the delta against baseline:
| Result | Condition | Status |
|---|---|---|
New tests now passing AND postfix_failures ⊆ baseline_failures AND new_failures is empty | Happy path | PASS |
| New tests still failing (did not transition) | Change did not fix them | UNVERIFIED |
postfix_failures ⊃ baseline_failures (new failures introduced) | Change broke something | REGRESSION — halt before commit |
| Runner crashed or produced empty output | Infrastructure problem | RUNNER_BROKEN |
postfix_failures == baseline_failures AND change is in component A, all baseline failures in unrelated component B | Pre-existing unrelated failures | BLOCKED with rationale |
Commit GREEN (only if status is PASS or BLOCKED with rationale):
git add .
git commit -m "feat(UC###): implement [feature] frontend"
as casts)Refactoring Checklist: Tests still pass, no duplicated JSX (extract components), no duplicated logic (extract hooks), clear component naming (PascalCase), single responsibility per component, proper error/loading/empty states, TypeScript strict mode passes, no ESLint warnings, Zod validation complete, Tailwind classes organized, accessibility attributes present, max 150 lines per component, max 5 props per component.
Commit REFACTOR:
git add .
git commit -m "refactor(UC###): improve [component] frontend implementation"
Use ${CLAUDE_PLUGIN_ROOT}/nacl-tl-core/templates/result-template.md as base to create .tl/tasks/UC###/result-fe.md.
Document: summary of frontend implementation, TDD phases with timestamps, status headline and resolved status, baseline diff (failures pre vs post), files created/modified with line counts, test results and coverage, commits made, components implemented, hooks created, pages/routes added, known issues, ready for review checklist.
Update status.json frontend phase. Status transition is gated on the Step 4.2 status:
| Step 4.2 status | phases.fe.status |
|---|---|
PASS | ready_for_review |
BLOCKED (with explicit operator acceptance + recorded rationale) | ready_for_review |
BLOCKED (no acceptance) | in_progress (blocked rationale recorded) |
UNVERIFIED | in_progress |
NO_INFRA | in_progress |
RUNNER_BROKEN | in_progress |
REGRESSION | in_progress (return to Step 4) |
{
"phases": {
"fe": {
"status": "ready_for_review",
"completed": "YYYY-MM-DDTHH:MM:SSZ"
}
}
}
For non-PASS / non-accepted-BLOCKED outcomes, also write phases.fe.failure_reason with the verbatim Status: value and a one-line summary.
Append to changelog.md:
## [YYYY-MM-DD HH:MM] DEV-FE: UC### - Task Title
- Phase: Frontend Development
- Status: [DEV-FE COMPLETE | DEV-FE APPLIED — UNVERIFIED | DEV-FE APPLIED — BLOCKED | DEV-FE APPLIED — NO_INFRA | DEV-FE APPLIED — RUNNER_BROKEN | DEV-FE INCOMPLETE — REGRESSION]
- Changes: N files, +X/-Y lines
- Tests: N passed, coverage X%
- Components: ComponentA, ComponentB, ComponentC
- Pages: /path/to/page, /path/to/detail
When invoked as /nacl:tl-dev-fe UC### --continue, the agent fixes issues from a prior review by delegating to /nacl:tl-fix. This skill no longer runs an inline test-after-change loop. The TDD/baseline/RED-first contract lives in nacl-tl-fix; this skill is a thin wrapper that builds the problem description, invokes the fix sub-agent, and propagates the resulting six-status into result-fe.md and status.json.
Why delegation: the previous "fix the issue, run tests" inline loop was test-after-change with no required RED-first test, no captured baseline, and no failure-set comparison — the same dishonesty class that triggered the 0.10.0 regression. /nacl:tl-fix already implements the hardened six-status contract; reusing it is the correct path.
.tl/tasks/UC###/review-fe.md (if missing, suggest /nacl:tl-review UC### --fe)phases.fe.status is "rejected" or "in_progress"/nacl:tl-fix)1. Read .tl/tasks/UC###/review-fe.md.
2. Parse issues by severity (Blocker / Critical / Major).
Drop Minor issues for the delegated invocation; they are captured in the
final result-fe.md note section.
3. Render each issue as a problem-description block:
File: <path>:<line>
Severity: <Blocker | Critical | Major>
Description: <text>
Suggestion: <text>
Concatenate the blocks into a single problem-description string in
priority order (Blocker → Critical → Major).
4. Invoke /nacl:tl-fix as a sub-agent:
/nacl:tl-fix "<problem description>" --uc UC### --from-review
The fix sub-agent owns:
- runner discovery (its Step 7.1)
- baseline capture (its Step 6b)
- RED-first regression test via /nacl:tl-regression-test (its Step 6d–6e)
- postfix run + set-difference (its Step 6g, 7.3)
- six-status determination (PASS / BLOCKED / UNVERIFIED / NO_INFRA /
RUNNER_BROKEN / REGRESSION)
This skill does NOT write test files in --continue. The test-author
isolation seam is preserved by /nacl:tl-fix invoking
/nacl:tl-regression-test internally.
5. Read /nacl:tl-fix's report. Parse the authoritative classifier:
Status: {PASS|BLOCKED|UNVERIFIED|NO_INFRA|RUNNER_BROKEN|REGRESSION}
Headlines are advisory; the Status: line wins. A report without a
parseable Status: line halts this skill as
"DEV-FE APPLIED — UNVERIFIED (downstream report unparseable)".
6. Read the fix report's regression-test seam evidence:
- Tests > Regression test: <test file path>
- Tests > RED→GREEN: <transition evidence>
If the seam evidence is missing for a non-NO_INFRA / non-RUNNER_BROKEN
status, treat the outcome as UNVERIFIED. Silence-as-evidence is forbidden.
7. Append a "## Fix Iteration N" block to .tl/tasks/UC###/result-fe.md:
## Fix Iteration N — <ISO timestamp>
Source: review-fe.md (Blocker: A, Critical: B, Major: C)
Delegated to: /nacl:tl-fix --uc UC### --from-review
Fix Status: <Status: line value, verbatim from nacl-tl-fix report>
Fix Headline: <header line from nacl-tl-fix report>
Regression-test seam:
- test file: <path or "n/a (NO_INFRA / RUNNER_BROKEN)">
- RED→GREEN: <evidence string from fix report>
Issues addressed:
1. [Blocker] <title> @ <file:line> — <one-line outcome>
2. [Critical] <title> @ <file:line> — <one-line outcome>
...
Issues NOT addressed (Minor, deferred):
- [Minor] <title> @ <file:line>
8. Update status.json phases.fe:
- Status: PASS → phases.fe.status = "ready_for_review"
- Status: BLOCKED + operator accept → phases.fe.status = "ready_for_review"
(record acceptance reason in
phases.fe.blocked_accept_reason)
- Status: BLOCKED (no acceptance) → phases.fe.status = "in_progress"
- Status: UNVERIFIED → phases.fe.status = "in_progress"
- Status: NO_INFRA → phases.fe.status = "in_progress"
- Status: RUNNER_BROKEN → phases.fe.status = "in_progress"
- Status: REGRESSION → phases.fe.status = "in_progress"
For all non-PASS / non-accepted-BLOCKED outcomes, write:
phases.fe.continue_failure_reason = "<Status: value> — <one-line>"
9. Do NOT auto-commit on non-PASS. /nacl:tl-fix already commits its
own fix on PASS / accepted-BLOCKED (per its Step 6 commit gate); for
any other status, surface the result to the operator and stop.
Parse issues from review-fe.md by severity headers. Fix priorities: Blockers first, then Critical, then Major. Minor issues are recorded but NOT included in the delegated /nacl:tl-fix problem description (the operator can re-run with a follow-up review iteration if Minors must be addressed in this UC).
If the fix sub-agent's report does not contain a Status: line, or omits the regression-test seam (Tests > Regression test, Tests > RED→GREEN) for a status that requires it (anything other than NO_INFRA or RUNNER_BROKEN), this skill does NOT promote phases.fe to ready_for_review. Silence is UNVERIFIED; require explicit evidence to advance. This mirrors Step 3.3's regressions rule.
src/
├── app/ # Next.js App Router (pages, layouts, loading, error)
│ ├── layout.tsx # Root layout
│ ├── (auth)/ # Route group: login, register
│ └── (dashboard)/ # Route group: protected pages
│ ├── layout.tsx # Dashboard layout with sidebar
│ └── {feature}/ # page.tsx, [id]/page.tsx, new/page.tsx, loading.tsx
├── components/
│ ├── ui/ # Base UI (Button, Input, Modal, Select, index.ts)
│ ├── features/{domain}/ # Feature-specific (OrderCard, OrderList, OrderForm)
│ ├── layouts/ # Header, Sidebar, PageContainer
│ └── shared/ # StatusBadge, DataTable, EmptyState
├── hooks/ # Custom hooks (useOrders.ts, useDebounce.ts)
├── lib/
│ ├── api/ # API client (client.ts, orders.ts, errors.ts)
│ └── utils/ # cn.ts, format.ts, validation.ts (Zod schemas)
├── stores/ # Zustand stores (uiStore.ts, filterStore.ts)
├── types/ # TypeScript types (order.ts, api.ts, index.ts)
└── __tests__/ # Test infra (setup.ts, utils.tsx, mocks/)
└── mocks/ # MSW server, handlers/, fixtures/
Test infrastructure setup (MSW server, renderWithProviders, AllProviders) is defined in test-spec-fe.md. Create __tests__/setup.ts and __tests__/utils.tsx as specified there during Step 3.2 if they do not already exist.
| Type | Prefix | File Pattern | Example |
|---|---|---|---|
| Component | CT | *.test.tsx | OrderCard.test.tsx |
| Hook | HT | *.test.ts | useOrders.test.ts |
| Form | FT | *.test.tsx | OrderForm.test.tsx |
| Integration | IT | *.integration.test.tsx | OrdersPage.integration.test.tsx |
| Accessibility | AT | within CT files | OrderCard.test.tsx (a11y section) |
| Edge Case | EC | within relevant test files | OrderCard.test.tsx (edge section) |
Coverage thresholds: Statements 80%+ (target 90%), Branches 75%+ (target 85%), Functions 80%+ (target 90%), Lines 80%+ (target 90%).
Use queries in this order of preference:
getByRole -- accessible role + name (preferred)getByLabelText -- form elements by labelgetByPlaceholderText -- inputs by placeholdergetByText -- non-interactive text contentgetByTestId -- last resort onlyNever use: container.querySelector, container.innerHTML, implementation-detail queries.
pending -> in_progress -> ready_for_review -> approved -> done
↑ |
+--------- in_progress <-- rejected
| Status | Meaning |
|---|---|
pending | Frontend task created, not started |
in_progress | Frontend development in progress |
ready_for_review | Frontend TDD cycle complete |
in_review | Frontend code review in progress |
rejected | Review failed, needs rework (use --continue) |
approved | Frontend review passed |
done | Frontend documentation complete |
Load these for detailed guidelines:
| Task | Reference |
|---|---|
| FE development rules | ${CLAUDE_PLUGIN_ROOT}/nacl-tl-core/references/frontend-rules.md |
| FE code style | ${CLAUDE_PLUGIN_ROOT}/nacl-tl-core/references/fe-code-style.md |
| FE review checklist | ${CLAUDE_PLUGIN_ROOT}/nacl-tl-core/references/fe-review-checklist.md |
| API contract rules | ${CLAUDE_PLUGIN_ROOT}/nacl-tl-core/references/api-contract-rules.md |
| QA rules | ${CLAUDE_PLUGIN_ROOT}/nacl-tl-core/references/qa-rules.md |
Use template from ${CLAUDE_PLUGIN_ROOT}/nacl-tl-core/templates/ for output:
result-template.md - Development result template (create as result-fe.md)| Situation | Action |
|---|---|
| Task files not found | Report missing files, suggest /nacl:tl-plan to create plan |
| Task blocked | Report blockers and exit |
| Tests fail during GREEN | Continue iterating, do NOT skip to refactoring |
| Dependency not ready | Report unresolved deps with statuses |
| Backend not approved | Report BE status, suggest /nacl:tl-dev-be UC### first |
| Phase | Pattern | Problem | Correct |
|---|---|---|---|
| RED | Testing implementation details | Brittle tests | Test user-visible behavior |
| RED | Using fireEvent | Unrealistic events | Use user-event for real interactions |
| RED | Using getByTestId first | Inaccessible queries | Use getByRole, getByLabelText |
| RED | No failure verification | False positives | Step 3.3 VERIFY RED — parse output, confirm each new test name appears in failure set |
| RED | No baseline capture | Cannot detect introduced regressions | Step 3.1 CAPTURE BASELINE — run suite before writing any test |
| RED | Mocking component internals | Tests prove nothing | Mock only API (MSW) and modules |
| GREEN | Over-engineering components | Wasted effort | Minimal code to pass |
| GREEN | Skip to refactor | Unstable base | Make it work first |
| GREEN | Adding features not in tests | Scope creep | Only what tests need |
| GREEN | Ignoring api-contract shapes | Contract mismatch | Follow contract exactly |
| GREEN | No postfix comparison | Cannot claim GREEN honestly | Step 4.2 VERIFY GREEN + COMPARE — compute delta against baseline |
| REFACTOR | Big-bang refactoring | Risk of breakage | Small steps, test after each |
| REFACTOR | No test run after change | Broken code | Test after each change |
| REFACTOR | Adding features | Scope creep | Only improve existing |
| REFACTOR | Premature optimization | Complexity | Only optimize when measured |
| Pattern | Problem | Correct |
|---|---|---|
| Direct fetch/axios calls | Inconsistent data handling | Use TanStack Query hooks |
| Inline styles or style objects | Unmaintainable, no design system | Use Tailwind CSS classes |
any type assertions | No type safety | Use proper types from types/ |
| Testing implementation details | Brittle, refactor-breaking tests | Test user behavior via RTL |
Modifying api-contract.md | Breaks contract flow | Read-only reference |
| Writing backend code | Wrong scope | That is nacl-tl-dev-be's job |
| Skipping accessibility | Excludes users | Add ARIA attrs, keyboard nav |
useState for server data | Stale data, no caching | Use TanStack Query |
| Prop drilling > 2 levels | Hard to maintain | Use Zustand store or context |
| Class components | Legacy pattern | Functional components + hooks |
| CSS modules or styled-components | Inconsistent with stack | Tailwind CSS only |
useEffect for data fetching | Race conditions, no cache | TanStack Query |
| Hardcoded API URLs | Not portable | Use env variables / config |
| Business logic in components | Untestable | Extract to hooks or utils |
| Barrel exports in features | Circular dependencies | Direct imports |
After completion, display:
═══════════════════════════════════════════
<HEADLINE: DEV-FE COMPLETE | DEV-FE APPLIED — UNVERIFIED | DEV-FE APPLIED — BLOCKED |
DEV-FE APPLIED — NO_INFRA | DEV-FE APPLIED — RUNNER_BROKEN | DEV-FE INCOMPLETE — REGRESSION>
═══════════════════════════════════════════
Task: UC### [Title] (Frontend)
Duration: XX minutes
TDD Phases: RED -> GREEN -> REFACTOR
Status: <PASS | UNVERIFIED | BLOCKED | NO_INFRA | RUNNER_BROKEN | REGRESSION>
Files:
Created: N files (+XXX lines)
Modified: N files (+XX/-YY lines)
Tests:
Runner: [exact test command actually run, or "none — NO_INFRA"]
Baseline: [N tests collected, K failing] or "skipped (RUNNER_BROKEN)"
Regression test: [repo-relative path of test written by /nacl:tl-regression-test
mode=feature-dev | "none — UNVERIFIED" | "n/a — NO_INFRA"]
RED verified: [yes — new tests appeared in failure set] or [no — HALT, see Step 3.3]
Postfix: [N tests collected, K failing] or "skipped"
Baseline diff: [list of transitions, or "none — UNVERIFIED", or "pre-existing: [list] — BLOCKED"]
New failures: [list — only if REGRESSION; otherwise "none"]
Coverage: XX%
The `Regression test:` line is **mandatory** when Status ∈ {PASS, UNVERIFIED, BLOCKED}. Orchestrators (`nacl-tl-conductor`, `nacl-tl-full`) parse it verbatim and forward it into `Task.verification_evidence` in the graph (see `${CLAUDE_PLUGIN_ROOT}/nacl-core/SKILL.md` § Task.verification_evidence). The path must be repo-relative.
Components Implemented:
- ComponentA (src/components/features/xxx/)
- ComponentB (src/components/ui/)
Hooks Created:
- useEntityList (src/hooks/)
- useCreateEntity (src/hooks/)
Pages/Routes:
/path/to/list -> ListPage
/path/to/[id] -> DetailPage
/path/to/new -> CreatePage
Commits: 3
- test(UC###): add failing frontend tests
- feat(UC###): implement frontend feature
- refactor(UC###): improve frontend implementation
Next step:
DEV-FE COMPLETE → /nacl:tl-review UC### --fe to start review
DEV-FE APPLIED — * → See status rationale above; resolve before review
DEV-FE INCOMPLETE → Return to Step 4; do NOT submit for review
The headline is status-aware and matches the same six-status vocabulary used by nacl-tl-fix. The Status: line below the headline is the authoritative classifier; the headline is decoration.
═══════════════════════════════════════════
<HEADLINE: DEV-FE FIX-CONTINUE COMPLETE | DEV-FE FIX-CONTINUE APPLIED — UNVERIFIED |
DEV-FE FIX-CONTINUE APPLIED — BLOCKED | DEV-FE FIX-CONTINUE APPLIED — NO_INFRA |
DEV-FE FIX-CONTINUE APPLIED — RUNNER_BROKEN | DEV-FE FIX-CONTINUE INCOMPLETE — REGRESSION>
═══════════════════════════════════════════
Task: UC### [Title] (Frontend, --continue iteration N)
Source: .tl/tasks/UC###/review-fe.md
Issues parsed: Blocker A, Critical B, Major C (Minor D, deferred)
Delegated to: /nacl:tl-fix --uc UC### --from-review
Fix Status: <PASS | BLOCKED | UNVERIFIED | NO_INFRA | RUNNER_BROKEN | REGRESSION>
Fix Headline: <verbatim header from nacl-tl-fix report>
Regression-test seam (from /nacl:tl-fix):
Test file: <path or "n/a (NO_INFRA / RUNNER_BROKEN)">
RED→GREEN: <evidence string verbatim>
Issues addressed:
- [Blocker] <title> @ <file:line>
- [Critical] <title> @ <file:line>
...
Tracking:
phases.fe.status = <"ready_for_review" | "in_progress">
phases.fe.failure_reason = <"" or "<Status: value> — <one-line>">
Next step:
PASS → /nacl:tl-review UC### --fe (re-review)
BLOCKED (accepted) → /nacl:tl-review UC### --fe (re-review with note)
BLOCKED (rejected) → return to Step 4 / re-invoke --continue with sharper inputs
UNVERIFIED → re-invoke --continue OR run /nacl:tl-regression-test retroactively
NO_INFRA → /nacl:tl-dev TECH-### "set up FE test runner"
RUNNER_BROKEN → /nacl:tl-diagnose; do NOT advance the phase
REGRESSION → return to Step 4f in /nacl:tl-fix; do NOT submit for review
This skill never writes test files in --continue; the fix sub-agent owns that responsibility via its nacl-tl-regression-test invocation.
Before Starting: Task files exist (task-fe.md, test-spec-fe.md, impl-brief-fe.md), api-contract.md present, BE phase approved/done, FE phase status pending/in_progress, no blockers, dependencies resolved.
RED Phase:
test(UC###): prefixGREEN Phase (data-fetching/validation/routing items per the project's FE stack; React/Next.js profile shown):
feat(UC###): prefixREFACTOR Phase: Components < 150 lines, props < 5 per component, custom hooks extracted where needed, styling organized, accessibility attributes present, type checks pass per the project's toolchain, no linter warnings, tests still pass, committed with refactor(UC###): prefix. (Node/TS profile: Tailwind, TypeScript strict, ESLint.)
After Completion: result-fe.md created (includes status headline and baseline diff), status.json phases.fe set to ready_for_review, changelog.md updated with status headline.
After frontend development:
/nacl:tl-review UC### --fe - Start frontend code review/nacl:tl-status - View project progress/nacl:tl-next - Get next suggested tasknpx claudepluginhub itsalt/nacl --plugin naclBackend TDD development from task specifications. Use when: develop backend, implement BE feature, run backend TDD cycle, write backend code for UC, or the user says "/nacl:tl-dev-be UC###".
Orchestrates frontend dev cycles (React/Next.js/TS): Gate 0 TDD + accessibility/visual/E2E/perf, Gate 7 review, Gate 8 validation. Start or resume a gated cycle.
Builds production-grade web frontends: React/Next.js components, pages, design systems, state management, and typed API clients. Useful for structured, multi-step UI development with protocol-driven quality checks.