Feature Implementation Workflow
A rigorous, multi-phase process for implementing new features in the WalkWithMe codebase. Enforces triple-verified planning, codebase-consistent code style, systematic bug tracking, and mandatory post-implementation review. The deliverable is a fully working, tested feature with clean code that matches existing patterns.
When to Use This Skill
- •User asks to implement a new feature or add functionality
- •User asks to build a significant UI component with backend support
- •Any task that touches both
convex/andsrc/directories - •Any change requiring new schema tables, queries, mutations, components, or routes
Scope Boundaries
- •Does NOT cover pure bug fixes (no planning phase needed)
- •Does NOT cover pure UI restyling (use
/ui-uxskill instead) - •Does NOT cover refactoring without new functionality
- •Does NOT cover deployment or CI/CD changes
Phase 1: Discovery & Requirements
Goal: Deeply understand what needs to be built and how it fits into the existing codebase before writing any plan.
- •Clarify the feature request — Ask the user clarifying questions if the requirements are vague. Do not proceed with ambiguous requirements.
- •Study related existing code — Read files in the areas the feature will touch:
- •
convex/schema.tsfor the current data model - •Existing
convex/*.tsmodules that the feature relates to - •Existing components in
src/components/that the feature extends or resembles - •Existing hooks in
src/hooks/for patterns to follow - •Route files in
src/routes/where the feature will be accessed
- •
- •Identify the code style — Before writing any code, internalize these patterns from the codebase:
- •No semicolons, single quotes, trailing commas (Prettier config)
- •Convex functions:
getAuthUserId(ctx)at top of every authenticated handler, throw'Unauthorized'if null - •Convex queries use
.withIndex()for filtered reads, indexes defined inconvex/schema.ts - •Frontend components: named exports,
@/path alias for imports,cn()for conditional classes - •Forms:
react-hook-form+zodvalidation schemas - •Translations: keys in both
src/locales/en.jsonandsrc/locales/pl.json - •Icons:
lucide-reactonly - •UI primitives:
shadcn/uicomponents from@/components/ui/
- •Map the feature surface — List every file that will be created or modified. Group them:
- •Schema changes (
convex/schema.ts) - •Backend functions (new or modified
convex/*.tsfiles) - •Hooks (
src/hooks/use*.ts) - •Components (
src/components/**/*.tsx) - •Routes (
src/routes/**/*.tsx) - •Translations (
src/locales/en.json,src/locales/pl.json)
- •Schema changes (
Ask yourself: "Can I describe exactly what data flows from the database to the UI for this feature? If not, I need to study the codebase more."
Phase 2: Implementation Plan (Triple-Verified)
Goal: Create a detailed implementation plan, then verify it against the codebase three times, editing after each pass.
Step 1: Write the Initial Plan
- •Create the plan file — Write a file named
PLAN_[FEATURE_NAME].mdin the project root (e.g.,PLAN_WALK_TRACKER.md). - •Plan structure — The plan MUST include these sections:
- •Feature Summary — One paragraph describing what the feature does from the user's perspective
- •Data Model — Exact schema changes: table names, field names, field types, indexes
- •Backend Functions — Each query/mutation: name, file, args, return value, logic summary
- •Frontend Components — Each component: name, file path, props, what it renders, which hooks it uses
- •Hooks — Each custom hook: name, file path, what Convex queries/mutations it wraps, what state it manages
- •Route Changes — Any new or modified route files
- •Translation Keys — List of new i18n keys needed (both
enandpl) - •Implementation Order — Numbered sequence: schema first, then backend, then hooks, then components, then routes, then translations
- •Be concrete — Every entry must reference exact file paths, function names, and field names. No vague language like "add appropriate fields" or "create necessary components."
Step 2: First Verification Pass
- •Read the plan file you just wrote.
- •Cross-check against
convex/schema.ts— Do the proposed tables/fields conflict with existing ones? Are the field types compatible with how Convex validators work (v.string(),v.number(),v.optional(), etc.)? - •Cross-check against existing modules — Do the proposed function names conflict with exports in existing
convex/*.tsfiles? Do the proposed component names conflict with existing components? - •Check data flow — Trace the data from schema → query → hook → component. Does every field the component needs actually exist in the query return value?
- •Edit the plan if any issues found. Save the updated plan file.
Step 3: Second Verification Pass
- •Read the updated plan file again.
- •Check for missing indexes — Every
.withIndex()call in the planned queries must have a matching index in the schema changes. - •Check for missing auth guards — Every mutation and query that accesses user data must include
getAuthUserId(ctx)check. - •Check component hierarchy — Are the proposed components placed correctly in the component tree? Do they have access to the data they need?
- •Check for edge cases — What happens with empty states? What if the user has no data yet? What if a referenced record is deleted?
- •Edit the plan if any issues found. Save the updated plan file.
Step 4: Third Verification Pass
- •Read the updated plan file one final time.
- •Run the "new agent" test — Could someone unfamiliar with this codebase follow this plan and produce correct code? If any step is ambiguous, clarify it.
- •Check implementation order — Are there any circular dependencies? Can each step be completed without forward references?
- •Check for scope creep — Is the plan doing more than what was requested? Remove anything unnecessary.
- •Final edit if any issues found. Save the final plan file.
Ask yourself: "Have I actually edited the plan file three times, or did I skip a pass because it 'looked fine'? Every pass must produce a re-read and explicit confirmation."
Step 5: User Approval
- •Present the plan to the user with a summary of:
- •What will be built
- •Which files will be created or modified
- •The implementation order
- •Wait for approval before writing any implementation code.
Phase 3: Backend Implementation
Goal: Build the data layer — schema, queries, and mutations — matching existing code style exactly.
- •Update the schema — Edit
convex/schema.tsto add new tables or fields. Follow existing patterns:- •Use
vvalidators fromconvex/values - •Do NOT define
_idor_creationTime(these are automatic) - •Add indexes for every field you will query by
- •Normalize ID pairs (
user1Id < user2Id) for bidirectional relationships
- •Use
- •Create backend functions — Create or modify files in
convex/. For each function:- •Import
getAuthUserIdfrom@convex-dev/auth/server - •Import
vfromconvex/valuesandquery/mutationfrom./_generated/server - •Start every handler with
const userId = await getAuthUserId(ctx)and throw/return if null - •Use
.withIndex()for filtered queries, never.filter()on large datasets - •Match the code style: no semicolons, single quotes, trailing commas, arrow functions
- •Import
- •Verify backend compiles — Run
npx convex dev(or check the running dev process) to confirm no type errors inconvex/files.
Ask yourself: "Does every new query have a matching index in the schema? Does every mutation check auth?"
Phase 4: Frontend Implementation
Goal: Build hooks, components, routes, and translations that match the existing codebase style.
- •Create custom hooks — In
src/hooks/:- •Wrap Convex
useQuery(api.module.fn)anduseMutation(api.module.fn)calls - •Follow naming convention:
useFeatureName.ts(e.g.,useBeacon.ts,useWalkTracker.ts) - •Export a single hook function that returns the data and actions the components need
- •Wrap Convex
- •Create components — In
src/components/:- •Use named exports (not default exports)
- •Import UI primitives from
@/components/ui/(Button, Card, Dialog, etc.) - •Import icons from
lucide-react - •Use
cn()from@/lib/utilsfor conditional class merging - •Use Tailwind classes matching the glassmorphism dark-mode theme (
bg-card/80 backdrop-blur-sm border-white/10) - •Use
react-hook-form+zodfor any forms - •Use
useTranslation()for all user-facing text
- •Add or update routes — In
src/routes/:- •Follow TanStack Start file-based routing conventions
- •Dashboard routes go under
src/routes/dashboard/
- •Add translations — Add keys to both:
- •
src/locales/en.json— English text - •
src/locales/pl.json— Polish translations
- •
- •Check imports — Verify every import resolves. If a
shadcn/uicomponent is needed but not yet installed, runnpx shadcn@latest add <component>. - •Run lint and format — Execute
npm run checkto auto-fix formatting and lint issues.
Ask yourself: "If I put my new component next to an existing one, would they look like they were written by the same person? If not, adjust the style."
Phase 5: Bug Tracking & Resolution
Goal: Systematically find, document, and fix every bug before declaring the feature complete.
- •
Create the bug file — Create
BUGS_[FEATURE_NAME].mdin the project root (e.g.,BUGS_WALK_TRACKER.md). - •
Bug file format — Each entry must include:
markdown### BUG-001: [Short description] - **File:** [exact file path] - **Line:** [approximate line number] - **Severity:** critical | major | minor - **Description:** [what goes wrong] - **Status:** open | fixed
- •
During implementation — Every time you encounter or create a bug, immediately add it to the bug file. Do NOT stop to fix it mid-flow unless it blocks the next step.
- •
After implementation is complete — Read the bug file. Fix every bug in order of severity (critical first, then major, then minor).
- •
Update status — Mark each bug as
fixedin the bug file after resolving it. - •
Verify fixes — After all bugs are fixed, run:
- •
npm run check— formatting and lint - •
npm run build— production build succeeds - •
npm run test— all tests pass (if tests exist for the feature)
- •
Ask yourself: "Did I actually write bugs down as I found them, or did I try to fix them inline and lose track?"
Phase 6: Final Review & Browser Verification
Goal: Prove the feature works correctly, matches codebase style, and does not break existing functionality.
Step 1: Comprehensive Code Review
- •Read every file you created or modified — Read them in full, not just the changed lines.
- •Style consistency check — Compare your code side-by-side with existing code in the same directory. Verify:
- •Same formatting (no semicolons, single quotes, trailing commas)
- •Same import ordering patterns
- •Same naming conventions (camelCase for functions/variables, PascalCase for components)
- •Same error handling patterns (
throw new Error('Unauthorized')vsreturn null) - •Same component structure (hooks at top, early returns, then JSX)
- •Logic review — Trace the complete data flow one more time: user action → component → hook → mutation → database → query → hook → component. Confirm it works end-to-end.
- •Check for regressions — Did any of your changes to existing files break other features? Search for other components that import from files you modified.
- •Run the full check suite:
- •
npm run check— formatting and lint pass - •
npm run build— production build succeeds - •
npm run test— all tests pass
- •
Step 2: Browser Verification
- •Ensure dev servers are running —
npm run devandnpx convex devmust both be active. - •Navigate to the feature — Open the page where the feature lives in the browser.
- •Test the happy path — Perform the primary action the feature enables. Verify the UI updates correctly.
- •Test edge cases — Test with:
- •Empty state (no data yet)
- •Error state (disconnect network, invalid input)
- •Rapid interactions (double-click, fast navigation)
- •Verify responsive layout — Check the feature looks correct on mobile (375px) and desktop (1440px).
- •Report results — Inform the user what was tested and the outcome.
Step 3: Cleanup
- •Delete temporary files — Remove
PLAN_[FEATURE_NAME].mdandBUGS_[FEATURE_NAME].mdfrom the project root, or ask the user if they want to keep them. - •Summarize — Provide the user with:
- •What was built (one paragraph)
- •Files created/modified (list)
- •How to use the feature (brief instructions)
- •Any known limitations or future improvements
Code Style Reference
Code you write MUST match these patterns found in the existing codebase.
Backend (Convex) Pattern
import { getAuthUserId } from '@convex-dev/auth/server'
import { v } from 'convex/values'
import { mutation, query } from './_generated/server'
export const myMutation = mutation({
args: {
fieldName: v.string(),
},
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx)
if (!userId) throw new Error('Unauthorized')
const user = await ctx.db.get(userId)
if (!user) throw new Error('User not found')
// Business logic here
await ctx.db.insert('tableName', {
userId,
fieldName: args.fieldName,
createdAt: Date.now(),
})
},
})
export const myQuery = query({
args: {},
handler: async (ctx) => {
const userId = await getAuthUserId(ctx)
if (!userId) return []
return await ctx.db
.query('tableName')
.withIndex('by_userId', (q) => q.eq('userId', userId))
.collect()
},
})
Frontend Component Pattern
import { Button } from '@/components/ui/button'
import { useMyFeature } from '@/hooks/useMyFeature'
import { cn } from '@/lib/utils'
import { SomeIcon } from 'lucide-react'
export function MyComponent() {
const { data, doAction } = useMyFeature()
if (!data) return null
return (
<div
className={cn(
'rounded-xl border border-white/10 bg-card/80 backdrop-blur-sm p-4',
)}
>
<Button variant="outline" className="gap-2" onClick={() => doAction()}>
<SomeIcon className="w-4 h-4" />
Action Label
</Button>
</div>
)
}
Hook Pattern
import { useMutation, useQuery } from 'convex/react'
import { api } from '../../convex/_generated/api'
export function useMyFeature() {
const data = useQuery(api.module.myQuery)
const doAction = useMutation(api.module.myMutation)
return {
data,
doAction,
}
}
Example Walkthrough: Implementing a "Walk Notes" Feature
User request: "Add a way for users to add notes to their walks — like what they saw, how the dog behaved, etc."
Phase 1: Discovery
- •Read
convex/schema.ts— foundwalkstable exists withuserId,startTime,endTime,route,distance,durationfields - •Read
convex/walks.ts— foundstartWalk,endWalk,getWalkHistoryfunctions - •Read
src/hooks/useWalkTracker.ts— found it wraps walk mutations/queries - •Read
src/components/dashboard/walk/WalkDetailsSheet.tsx— this is where notes would display - •Identified style patterns: no semicolons, single quotes,
getAuthUserIdat top,cn()for classes
Phase 2: Plan (Triple-Verified)
Written to PLAN_WALK_NOTES.md:
- •Schema: add
notes: v.optional(v.string())to walks table - •Backend: add
updateWalkNotesmutation inconvex/walks.ts - •Hook: add
updateNotestouseWalkTrackerreturn value - •Component: add notes textarea to
WalkDetailsSheet.tsx - •Translations: add
walk.notes,walk.notesPlaceholderto both locale files
Pass 1: Checked schema — notes field does not conflict. Mutation uses getAuthUserId. Index exists on by_userId. No edits needed.
Pass 2: Checked that WalkDetailsSheet has access to walkId (it does, via props). Checked edge case — what if notes is empty string? Decided to treat empty string same as undefined. Edited plan to add this clarification.
Pass 3: Checked implementation order — schema → mutation → hook → component → translations. No circular deps. Plan is minimal and focused. No edits needed.
Phase 3: Backend
- •Added
notes: v.optional(v.string())to walks table in schema - •Added
updateWalkNotesmutation toconvex/walks.ts— checks auth, patches the walk document - •Verified
npx convex devshows no errors
Phase 4: Frontend
- •Added
updateNotestouseWalkTrackerhook - •Added textarea with save button to
WalkDetailsSheet.tsx - •Added translation keys:
walk.notes= "Walk Notes" / "Notatki z spaceru",walk.notesPlaceholder= "What happened on this walk?" / "Co wydarzylo sie na spacerze?" - •Ran
npm run check— clean
Phase 5: Bug Tracking
Created BUGS_WALK_NOTES.md:
### BUG-001: Notes don't persist on page refresh - File: src/hooks/useWalkTracker.ts - Line: 45 - Severity: critical - Description: Used local state instead of Convex query for notes display - Status: fixed (switched to useQuery) ### BUG-002: Save button enabled when notes unchanged - File: src/components/dashboard/walk/WalkDetailsSheet.tsx - Line: 72 - Severity: minor - Description: Button should be disabled when content matches saved value - Status: fixed (added dirty check)
Phase 6: Final Review
- •Re-read all modified files — style matches existing code
- •Traced data flow: user types → textarea onChange → save button →
updateWalkNotesmutation → Convex DB →useQueryauto-updates → textarea displays saved notes - •Ran
npm run check,npm run build— both pass - •Tested in browser: added notes, refreshed, notes persisted
- •Tested empty state: no notes shown, placeholder text visible
- •Cleaned up plan and bug files
Anti-Patterns
| Anti-Pattern | Why It Fails | Do This Instead |
|---|---|---|
| Skipping plan verification passes ("Plan looks fine, moving on") | Bugs that could be caught in planning slip into implementation, costing 10x more time to fix | Actually re-read the plan file three times. Each pass catches different categories of issues |
| Fixing bugs inline during implementation | Loses track of what was fixed, may introduce new bugs while fixing old ones, disrupts implementation flow | Write bugs to the bug file immediately, fix them all after implementation is complete |
| Writing code in a different style (semicolons, default exports, different naming) | Creates inconsistency that makes the codebase harder to maintain and review | Read 2-3 existing files in the same directory before writing. Match their patterns exactly |
| Skipping the final code review ("I just wrote it, I know it works") | Fresh eyes (even your own) catch issues that flow state misses — wrong variable names, missing error handling, dead code | Read every file you touched in full after all bugs are fixed |
| Not testing in the browser ("It compiles, ship it") | Compilation proves syntax, not behavior. Real bugs appear in user interaction — wrong layout, missing data, broken state transitions | Navigate to the feature and test the happy path, empty state, and edge cases |
| Vague plan entries ("Create the component") | Cannot verify if the plan is correct or complete without concrete details | Specify exact file paths, function names, props, and data flow |
| Starting to code without user approval | Risk building the wrong thing entirely. Rework costs more than waiting for confirmation | Always present the plan and wait for explicit approval |
| Forgetting translations | Feature works in English but Polish users see raw translation keys | Add entries to both en.json and pl.json as part of implementation, not as an afterthought |
| Missing Convex indexes | Queries work in dev with small data but time out in production | Every .withIndex() call must have a matching index in convex/schema.ts |
| Skipping auth checks in mutations | Security vulnerability — any user can modify any other user's data | Every Convex mutation starts with getAuthUserId(ctx) and throws if null |
Pre-Delivery Checklist
Planning
- •
PLAN_[FEATURE_NAME].mdwas created with all required sections - • Plan was verified against codebase three separate times
- • Plan was edited after at least one verification pass
- • User approved the plan before implementation began
Backend
- • Schema changes use correct
vvalidators - • No
_idor_creationTimedefined in schema (they are automatic) - • Every queried field has an index in
convex/schema.ts - • Every mutation calls
getAuthUserId(ctx)and handles null - •
npx convex devshows no type errors
Frontend
- • Components use named exports
- • Imports use
@/path alias - • Icons from
lucide-reactonly - • UI primitives from
@/components/ui/ - •
cn()used for conditional classes - • Forms use
react-hook-form+zod - • All user-facing text uses
useTranslation()
Code Style
- • No semicolons
- • Single quotes
- • Trailing commas
- • Code matches style of neighboring files in the same directory
- •
npm run checkpasses with no errors
Bug Tracking
- •
BUGS_[FEATURE_NAME].mdwas created during implementation - • All bugs marked as
fixed - • No open bugs remain
Verification
- • Every created/modified file re-read in full after bugs fixed
- •
npm run checkpasses - •
npm run buildsucceeds - •
npm run testpasses (if tests exist) - • Feature tested in the browser
- • Happy path works
- • Empty state handled
- • Edge cases tested
Tips
- •Read before you write — Before creating any file, read 2-3 existing files in the same directory. Your code should be indistinguishable from the existing code.
- •Plan on paper, code in editor — The triple verification catches bugs that are 10x cheaper to fix in a plan than in code. Do not rush through it.
- •One bug, one entry — Every bug gets its own entry in the bug file with severity. This prevents "I'll remember to fix that" amnesia.
- •Schema first, always — The data model dictates everything downstream. Get it right before touching any other file.
- •Test the sad path — Empty states, missing data, and error conditions are where features break. Test them explicitly.
- •Keep the plan file lean — The plan should contain what you need to implement, not a design document. If a section grows beyond 10 lines, you are over-explaining.