Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .agents/skills/prd/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,36 @@ Save the PRD to `.claude/tasks/[feature-name]/prd.md`.

Choose a stable kebab-case feature folder name, for example `profile-detail`, `event-check-in`, or `push-notification-settings`. If a matching task folder already exists, ask before overwriting an existing PRD.

### Step 4b: Write context.md

After saving `prd.md`, write `.claude/tasks/[feature-name]/context.md` with the following
structure (use the actual values from the PRD — do not leave placeholder text):

```markdown
# [Feature Name] — Spec Context

## Feature Name
[kebab-case folder name]

## Problem Statement
[1-2 sentences capturing the core problem and who it affects]

## Target Platforms
[Android, iOS, web, macOS, Windows, Linux — list only those in scope]

## Key Product Decisions
- [bullet list of the significant product choices captured in the PRD]

## Out of Scope
- [bullet list of items explicitly excluded]

## Open Questions
- [any questions that remain unresolved after the PRD; empty list if none]
```

This file accumulates context across the spec pipeline. Later skills (`techspec`, `tasks`) will
read it and append their own sections — do not remove or reformat existing sections.

### Step 5: Report Results

- Confirm the file path
Expand Down
107 changes: 107 additions & 0 deletions .agents/skills/spec-feature/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
name: spec-feature
description: >
Top-level orchestrator for Flutter feature specification. Runs the full uninterrupted pipeline:
prd → techspec → tasks in one shot. Use when the user says "spec a feature", "write a spec for",
"start feature spec", "plan a feature", or wants to go from idea to implementation-ready tasks
without typing three separate commands.
allowed-tools: Bash Read Grep Glob Edit Write Agent Skill
user-invocable: true
model: claude-opus-4-7
---

# Flutter Template Spec Feature

Single entry point that runs the full feature specification pipeline from idea to
implementation-ready tasks. It is designed to be invoked once; each sub-skill may pause for
user input during its mandatory clarification steps — that is intentional and correct. After
each sub-skill finishes, this orchestrator automatically invokes the next phase without requiring
the user to type the next command.

Use this when you want to go from a feature idea directly to a full task breakdown, without
manually running `/prd`, `/techspec`, and `/tasks` in sequence.

## Prerequisites

- No existing `prd.md` or `techspec.md` for this feature (or the user has confirmed it is safe
to overwrite).
- The user can answer clarification questions — both `prd` and `techspec` have mandatory
interactive clarification steps that will pause the pipeline for user input.

If a PRD and tech spec already exist and are complete, use `/start-job [feature-name]` instead.

## Arguments

Accepts an optional feature name. If provided, pass it to each sub-skill so they can pre-fill
the folder name and skip asking. If omitted, the `prd` sub-skill will determine the feature name
during its workflow.

## Pipeline

Run each phase in order. Between phases, do NOT ask for user approval — the sub-skills themselves
handle all necessary user interaction. If a phase fails hard (the sub-skill reports an
unrecoverable error), stop and surface it to the user.

> **Critical orchestration rule:** when a sub-skill returns control, do not treat its output as
> the end of your turn. You are still inside this `/spec-feature` orchestrator. Immediately
> invoke the next phase's skill in the same response. The only valid stops are:
> (a) a sub-skill's mandatory clarification pause (which is correct — wait for the user, then
> resume), (b) a hard failure, or (c) the Final Report after Phase 3.

### Phase 1 — Product Requirements Document

Invoke the `prd` skill (pass the feature name argument if provided).

The `prd` skill has two mandatory interactive steps: it will ask clarifying questions (Step 1)
and present a plan for confirmation (Step 2). These pauses are correct — wait for user responses.
After the user confirms the plan and `prd` saves `prd.md`, it will return control.

**After `prd` returns (i.e., after `prd.md` has been saved): do not stop, do not wait for
additional input, do not summarize. Immediately invoke `techspec` (Phase 2) in the same
response.**

### Phase 2 — Technical Specification

Invoke the `techspec` skill (pass the feature name argument if provided).

The `techspec` skill has one mandatory interactive step: it will ask technical clarification
questions (Step 3). This pause is correct — wait for user responses. After the user answers and
`techspec` saves `techspec.md`, it will return control.

**After `techspec` returns (i.e., after `techspec.md` has been saved): do not stop, do not wait
for additional input, do not summarize. Immediately invoke `tasks` (Phase 3) in the same
response.**

### Phase 3 — Task Breakdown

Invoke the `tasks` skill (pass the feature name argument if provided).

The `tasks` skill runs without approval prompts and generates `tasks.md` plus individual task
files. When invoked from this orchestrator (not directly by the user), `tasks` must skip its
next-step prompt and return control here.

**After `tasks` returns: produce the Final Report. This is the only place the pipeline stops on
success.**

## Final Report

Output a single consolidated summary:
- Feature name and the folder created under `.claude/tasks/`
- What the PRD captured (problem statement, target platforms, scope boundaries)
- Key technical decisions from the tech spec (state model, data sources, navigation shape, any
new packages needed)
- Task count, phases, and dependency shape
- **Reminder:** the feature is now ready for implementation. Run `/start-job [feature-name]` to
execute the implementation pipeline, or step through manually with `/implement-tasks-sequence`.

## Rules

- **Never commit.** This pipeline only produces spec and task files.
- **Never skip sub-skill clarification steps.** The mandatory interactive steps in `prd` and
`techspec` exist to capture requirements accurately. Do not bypass or pre-answer them.
- **Do not edit generated specs.** After `prd` or `techspec` writes a file, do not rewrite it
as the orchestrator. Only the sub-skill owns its output.
- **Stop on hard failure.** If a phase cannot proceed (missing context, the sub-skill reports an
unrecoverable error), stop and surface the issue to the user — do not continue on a broken base.
- **Pass the feature name through.** If an argument was provided, pass it consistently to all
three sub-skills so the folder name stays stable across phases.
30 changes: 27 additions & 3 deletions .agents/skills/tasks/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ The user does not want to review the task list before implementation. Generate t
total count reasonable (aim for cohesive tasks, not micro-tasks), and stop after reporting.
The caller (`/start-job` or the user directly) decides what runs next.

### Step 0: Load Shared Context

Before reading the PRD and tech spec, check whether `.claude/tasks/[feature-name]/context.md`
exists. If it does, read it. Use its contents to skip re-deriving already-captured decisions —
the problem statement, target platforms, architectural choices, and files to create are already
settled. Do not re-ask the user about items already recorded there.

### Step 1: Analyze PRD and Tech Spec

Read both documents and identify:
Expand Down Expand Up @@ -87,6 +94,23 @@ Each task file should include:
`*.freezed.dart`, `*.gr.dart`), reuse from `lib/common/` before adding new primitives
- Quality gates — `make gen` clean, `fvm flutter analyze` passes, `fvm flutter test` passes

### Step 4b: Append to context.md

Append the following section to `.claude/tasks/[feature-name]/context.md` (create the file if
it does not exist). Use actual values — do not leave placeholder text:

```markdown

## Tasks (from tasks breakdown)

### Task Count and Phases
[e.g., "8 tasks across 3 phases: Foundation (3), Core (3), Integration (2)"]

### Dependency Shape
[one sentence describing the dependency structure, e.g., "All Foundation tasks must complete
before Core tasks begin; Integration tasks depend on all Core tasks."]
```

### Step 5: Brief Summary

Output a one-paragraph summary (what was generated, how many tasks, dependency shape). Keep it
Expand All @@ -106,9 +130,9 @@ asking how they want to proceed:

Wait for the user's choice. Do NOT auto-run anything.

When invoked by `/start-job`: skip the prompt above and return control to the `/start-job`
orchestrator. The orchestrator must then invoke `implement-tasks-sequence` as Phase 2 without
pausing for user input.
When invoked by `/start-job` or `/spec-feature`: skip the prompt above and return control to the
calling orchestrator. `/start-job` will then invoke `implement-tasks-sequence` as Phase 2;
`/spec-feature` will produce its Final Report.

## Project context

Expand Down
28 changes: 28 additions & 0 deletions .agents/skills/techspec/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ services for backend, and FVM-pinned Flutter SDK.

## Workflow

### Step 0: Load Shared Context

Before reading the PRD, check whether `.claude/tasks/[feature-name]/context.md` exists. If it
does, read it. Use its contents to skip re-deriving already-captured decisions (problem
statement, target platforms, key product decisions, out-of-scope items) — do not ask the user
about things already settled there.

### Step 1: Analyze PRD

Read the PRD at `.claude/tasks/[feature-name]/prd.md` and extract:
Expand Down Expand Up @@ -97,6 +104,27 @@ Save to `.claude/tasks/[feature-name]/techspec.md`.
- Summarize key architectural decisions (state model, data sources, navigation shape,
any new packages needed in `pubspec.yaml`)

### Step 5b: Append to context.md

Append the following section to `.claude/tasks/[feature-name]/context.md` (create the file if
it does not exist). Use actual values from the tech spec — do not leave placeholder text:

```markdown

## Technical Decisions (from techspec)

### Key Architectural Decisions
- State model: [e.g., AsyncNotifier with UserProfileState freezed union]
- Data sources: [e.g., dio HTTP via UserProfileUseCase, no local cache]
- Navigation: [e.g., new @RoutePage full-screen route, no deep-link]

### Files to Create
- [brief list of new files, one per line, relative to lib/]

### New Packages Needed
- [package name and reason, or "none"]
```

### Step 6: Final next-step prompt

After saving the techspec, end with this guidance:
Expand Down
8 changes: 8 additions & 0 deletions .claude/commands/spec-feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
description: Run the full feature specification pipeline (prd → techspec → tasks) end to end.
argument-hint: [feature name]
---

Use the `spec-feature` skill to go from a feature idea to implementation-ready tasks in one shot.

$ARGUMENTS
1 change: 1 addition & 0 deletions .claude/skills/spec-feature
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Use this file as the entrypoint for automated work in this repository.

## Codegen And Commands
- Run `make gen` after changing `@riverpod`, `@freezed`, `@RoutePage`, localization, or generated asset inputs.
- Use `make watch` for continuous code generation when doing annotation-heavy work.
- Use `make watch` for continuous code generation when doing annotation-heavy work. This is a human-only command — agents must never run it (it spawns a persistent background watcher). Agents always use one-shot `make gen`.
- Do not hand-edit generated files such as `*.g.dart`, `*.freezed.dart`, `*.gr.dart`, or files under `lib/assets/`.
- Preferred validation commands:
- `fvm flutter analyze`
Expand Down Expand Up @@ -69,6 +69,7 @@ Existing skills:
- `implement` — implement one generated Flutter task using the repo architecture and verification rules
- `implement-tasks-sequence` — execute generated task files in dependency order before final verification
- `techspec` — translate a PRD into an implementation-ready Flutter tech spec at `.claude/tasks/<feature>/techspec.md`, grounded in the Riverpod / Freezed / AutoRoute / Dio / Firebase stack
- `spec-feature` — run the full specification pipeline (`prd` → `techspec` → `tasks`) in one shot, from feature idea to implementation-ready tasks

### How AI tools find these skills
- **Spec-compliant agents** scan `.agents/skills/` directly — it is the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_app/app/configuration/configuration.dart';
import 'package:flutter_app/app/setup/flavor.dart';
import 'package:flutter_app/app/theme/app_theme.dart';
import 'package:flutter_app/assets/app_localizations.gen.dart';
import 'package:flutter_app/features/authentication/authentication_page_content.dart';
import 'package:flutter_app/features/authentication/authentication_state.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';

Widget _wrap(Widget child) {
return MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
theme: AppTheme.getThemeData(brightness: Brightness.light),
home: Scaffold(body: child),
);
}

class _StubAuthNotifier extends AuthenticationStateNotifier {
_StubAuthNotifier(this._stub);

final AuthenticationState _stub;

@override
FutureOr<AuthenticationState> build() async => _stub;
}

void main() {
Configuration.setup(flavor: Flavor.develop);

patrolWidgetTest(
'AuthenticationPageContent - shows all sign-in buttons',
($) async {
await $.pumpWidgetAndSettle(
ProviderScope(
overrides: [
authenticationStateProvider.overrideWith(
() => _StubAuthNotifier(
const AuthenticationState(isGoogleSigningIn: false, isAppleSigningIn: false),
),
),
],
child: _wrap(const AuthenticationPageContent()),
),
);

expect(find.text('Mock Sign In'), findsOneWidget);
expect(find.text('Sign in Anonymously'), findsOneWidget);
expect(find.text('Sign in with Google'), findsOneWidget);
expect(find.text('Sign in with Apple'), findsOneWidget);
},
);

patrolWidgetTest(
'AuthenticationPageContent - Apple button visible while Google is loading',
($) async {
// pumpWidgetAndSettle would time out because isGoogleSigningIn=true keeps
// the loading spinner animating indefinitely — use a single pump instead.
await $.tester.pumpWidget(
ProviderScope(
overrides: [
authenticationStateProvider.overrideWith(
() => _StubAuthNotifier(
const AuthenticationState(isGoogleSigningIn: true, isAppleSigningIn: false),
),
),
],
child: _wrap(const AuthenticationPageContent()),
),
);
await $.tester.pump();

expect(find.text('Sign in with Apple'), findsOneWidget);
},
);
}
42 changes: 42 additions & 0 deletions test/features/home/home_page_content_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/configuration/configuration.dart';
import 'package:flutter_app/app/setup/flavor.dart';
import 'package:flutter_app/app/theme/app_theme.dart';
import 'package:flutter_app/assets/app_localizations.gen.dart';
import 'package:flutter_app/features/home/home_page_content.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';

Widget buildSubject() {
return MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
theme: AppTheme.getThemeData(brightness: Brightness.light),
home: const Scaffold(body: HomePageContent()),
);
}

void main() {
Configuration.setup(flavor: Flavor.develop);

patrolWidgetTest(
'HomePageContent - shows Home title',
($) async {
await $.pumpWidgetAndSettle(buildSubject());

expect(find.byKey(const Key('home_title')), findsOneWidget);
expect(find.text('Home'), findsOneWidget);
},
);

patrolWidgetTest(
'HomePageContent - shows debug tools button',
($) async {
await $.pumpWidgetAndSettle(buildSubject());

// Tap not tested here — it requires a router context
final l10n = AppLocalizations.of($.tester.element(find.byType(HomePageContent)))!;
expect(find.text(l10n.featureHomepageOpenDebugTools), findsOneWidget);
},
);
}
Loading
Loading