-
Notifications
You must be signed in to change notification settings - Fork 8
chore: upgrade agentic support with spec-feature skill, context handoff, and widget tests #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
8613c0c
docs: note that make watch is human-only, agents use make gen
michalurbanek 7187dd7
feat: add spec-feature skill to chain prd → techspec → tasks
michalurbanek 88f1736
feat: add context.md handoff across prd, techspec, and tasks skills
michalurbanek 17e54b0
test: add widget tests for landing, authentication, and home features
michalurbanek 061c53f
fix: address pr-review nits in skills and widget tests
michalurbanek 6575828
fix: resolve localized strings from widget tree instead of hardcoding…
michalurbanek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../.agents/skills/spec-feature |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
test/features/authentication/authentication_page_content_test.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
| }, | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| 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 | ||
| expect(find.text('Open debug tools'), findsOneWidget); | ||
| }, | ||
| ); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.