ado integration
Decompose Story
Analyze a story bead and break it into atomic, dependency-linked task beads. Each task touches one project and is sized for AI agent completion.
Decompose Story Skill
Overview
Transform a story bead into a graph of atomic task beads with proper dependencies. Each task is:
- Project-scoped - touches one logical project (e.g.,
Lista.Accessor.Profilemay include .Interfaces, .Service, .Tests) - AI-agent sized - completable in one context session (1-2 hours)
- Dependency-linked -
bd readysurfaces only unblocked work - ADO-friendly - description renders cleanly in Azure DevOps portal
Usage
Invoke this skill by asking the AI agent:
- "decompose story lista-j8w"
- "break down the story into tasks"
- "use decompose-story skill on lista-j8w"
Note: This is a guided skill, not an automated script. The AI agent follows this workflow to decompose stories.
Template: Use task-enrichment-template.md as a fill-in-the-blank guide for enriching each task.
Prerequisites
- Story bead exists
- Story has acceptance criteria in notes or description
Workflow
Step 0: Ensure Story Bead Exists
The story bead must exist before decomposition can begin.
# Verify the story bead exists
bd show <story-id>
If the bead does NOT exist:
- Use the
lista-storyformula ingest step first, or - Create it manually with
bd createusing ADO data. - Do not proceed until the story bead has title, description, and acceptance criteria.
Step 1: Gather Story Context
Read the story bead:
bd show <story-id>
Extract:
- Title
- Description
- Acceptance Criteria (from notes)
- Labels (especially
ado:*for ADO link)
Step 2: Check for Existing Children
List existing task beads under this story:
bd list --parent <story-id> --type task --json
Avoid creating duplicates. If tasks already exist, decide whether to enrich them (Step 7) or split further.
Step 3: Identify Architectural Touchpoints
From the story description, determine:
- Which accessor stores the data?
- Which engine contains business logic?
- Which manager orchestrates the flow?
- Which host exposes the API?
Reference: .github/instructions/architecture.instructions.md
Step 4: Find Reference Patterns
Search the codebase for similar implementations:
# Find existing models
find src -name "*Profile*.cs" -path "*/Models/*"
# Find existing accessors
find src -name "*Accessor*.cs" -path "*/Service/*"
The reference file becomes the pattern for new code.
Step 5: Generate Atomic Tasks
Break the story into tasks following this dependency order:
1. Interface Definition (no dependencies)
└─► 2. Document Model (blocked by 1)
└─► 3. Service Implementation (blocked by 2)
└─► 4. Integration Test (blocked by 3)
└─► 5. Host Registration (blocked by 4)
Task Types (use as labels):
phase:interface- Contract definitionsphase:model- Marten document modelsphase:implementation- Service codephase:test- Integration/unit testsphase:config- DI registration, host setup
Project Labels: Use the logical project namespace, not individual .csproj files:
project:Lista.Accessor.Profile(includes .Interfaces, .Service, .Tests)project:Lista.Manager.Process(includes .Interfaces, .Service, .Tests)project:Lista.Engine.Compliance(includes .Interfaces, .Service, .Tests)
Step 6: Create Detailed Task Beads
For each task, perform a deep dive to gather all necessary details. Don't just create minimal beads - fully populate each field with actionable information.
6.1 Research Phase (per task)
Before creating/updating each bead:
-
Find Reference Patterns:
# Search for similar existing implementations grep_search "class.*Profile" --includePattern "src/services/**/*.cs" semantic_search "employee profile document model marten" -
Identify Files to Modify/Create:
- Determine exact project paths
- Note which files will be touched
- Identify relevant instruction files
-
Extract Technical Details:
- What packages/libraries are needed?
- What patterns should be followed?
- What are common pitfalls?
6.2 Populate All Bead Fields
Use this template for EVERY task:
Tip: See task-enrichment-template.md for a structured fill-in-the-blank version of this example.
bd create "Add IEmployeeProfileAccessor interface" \
--type task \
--parent <story-id> \
--label "project:Lista.Accessor.Profile" \
--label "phase:interface" \
--description "Define the contract for employee profile data access operations including methods for retrieving, creating, and updating employee personal information with encrypted SSN support." \
--acceptance "- Define GetEmployeeById(Guid id) returning Task<EmployeePersonalProfile?>
- Define SaveEmployee(EmployeePersonalProfile profile) returning Task<Guid>
- Define UpdateEmployee(EmployeePersonalProfile profile) returning Task
- Include EncryptedString type for SSN field (from Basis.Core)
- All methods should use async/await pattern
- Methods throw ArgumentNullException for null inputs" \
--design "Reference Pattern: src/services/accessor/profile/Lista.Accessor.Profile.Interfaces/IProfileAccessor.cs
Project Structure:
- File: src/services/accessor/profile/Lista.Accessor.Profile.Interfaces/IEmployeeProfileAccessor.cs
- Namespace: Lista.Accessor.Profile.Interfaces
Implementation Notes:
- Follow MagicOnion service pattern (IService<IEmployeeProfileAccessor>)
- Use UnaryResult<T> for async operations
- Reference: .github/instructions/magic.accessor.instructions.md" \
--notes "Context: Parent story lista-j8w (ADO #669362)
Dependencies:
- Blocks lista-xxx (EmployeePersonalProfile model implementation)
- Blocks lista-yyy (ProfileAccessor service implementation)
Key Instruction Files:
- .github/instructions/accessor-discovery.instructions.md
- .github/instructions/conventions.instructions.md
Hints:
- Check Basis.Core for existing EncryptedString types
- Ensure all methods follow async naming conventions (Async suffix)"
Step 7: Enrich Existing Tasks (if imported from ADO)
If tasks were already imported from ADO, they need enrichment:
Use task-enrichment-template.md to systematically fill out each task.
# For each existing task, update with full details
bd update <task-id> \
--add-label "project:Lista.Accessor.Profile" \
--add-label "phase:model" \
--description "<detailed-description>" \
--acceptance "<bulleted-acceptance-criteria>" \
--design "<structured-design-notes>" \
--notes "<contextual-information>"
Enrichment Checklist per Task:
- Add project label (project:*)
- Add phase label (phase:*)
- Write 2-4 sentence description
- List 5-10 specific acceptance criteria
- Document reference patterns with file paths
- Add implementation notes and instruction file references
- Document dependencies and context
Step 8: Link Dependencies
After creating/enriching tasks, link blockers:
# Implementation blocked by Model (impl depends on model)
bd dep add <impl-bead> <model-bead>
# Test blocked by Implementation (test depends on impl)
bd dep add <test-bead> <impl-bead>
# Multiple dependencies (both must complete)
bd dep add <test-bead> <dependency-1>
bd dep add <test-bead> <dependency-2>
Note:
bd dep add <A> <B>means "A is blocked by B" (A depends on B). There is no--typeflag — all deps created viabd dep addare blocking deps.
Dependency Guidelines:
- Model tasks block implementation tasks
- Implementation tasks block test tasks
- Interface tasks (if separate) block everything
- Config/registration tasks come last
Step 9: Verify Complete Decomposition
Run these checks to ensure quality:
# 1. Check all tasks have proper labels
bd list --parent <story-id> --json | jq '.[] | {id, labels}'
# 2. Verify dependency chain
bd ready # Should show only the foundation tasks
# 3. View full hierarchy
bd list --parent <story-id> --pretty
# 4. Spot-check task details
bd show <task-id> # Verify all fields are populated
Quality Checklist:
- All tasks have
project:*label - All tasks have
phase:*label - All tasks have detailed descriptions (2-4 sentences)
- All tasks have 5-10 acceptance criteria
- All tasks have design notes with file paths
- All tasks have contextual notes with dependencies
- Dependencies form a logical chain
- Only foundation tasks show as "ready"
- Story AC maps to task acceptance criteria
Task Bead Fields - Detailed Guidelines
See bead-template.md for field descriptions.
Required Information Per Field
--description (2-4 sentences)
Purpose: High-level objective that clearly states WHAT needs to be done.
Must Include:
- The primary deliverable
- Key technical approach
- Why this task exists (how it serves the story)
Example:
"Create the EmployeePersonalProfile Marten document model to store employee identity
and work location data. This model will support encrypted SSN storage using AES-256
and include validation for work state codes. The model serves as the persistence layer
for employee onboarding workflows."
--acceptance (5-10 checkable criteria)
Purpose: Precise, testable conditions that define "done."
Must Include:
- Specific properties/methods to add
- Data types and constraints
- Validation rules
- Edge cases to handle
- Expected behavior for error conditions
Format: Bulleted list, each item actionable
Example:
- Add FirstName property (string, required, max 100 chars)
- Add LastName property (string, required, max 100 chars)
- Add EncryptedSsn property (byte[], encrypted via AesEncryptionService)
- Add WorkState property (string, 2-letter US state code, required)
- Add Employee_ID property (Guid, unique, auto-generated)
- Implement WorkState validation (must be valid US state)
- Add IsNoStateTaxState computed property (returns true for AK, FL, NV, NH, SD, TN, TX, WA, WY)
- Throw ArgumentException for invalid WorkState codes
- Handle null/empty SSN gracefully (store as null in DB)
- Document model inherits from Marten document base class
--design (structured reference info)
Purpose: Provide concrete patterns and file locations for implementation.
Must Include:
- Reference pattern file(s) with exact paths
- Project structure details
- Namespace conventions
- Key instruction files to consult
- Implementation notes/patterns
Format:
Reference Pattern: <path/to/similar/file.cs>
Project Structure:
- File: <exact-file-path-to-create-or-modify>
- Namespace: <expected-namespace>
- Project: <csproj-name>
Implementation Notes:
- <Key pattern to follow>
- <Important architectural decision>
- <Reference to instruction file>
Instruction Files:
- .github/instructions/<relevant-file>.instructions.md
Example:
Reference Pattern: src/services/accessor/profile/Lista.Accessor.Profile.Service/Models/ProfileDocument.cs
Project Structure:
- File: src/services/accessor/profile/Lista.Accessor.Profile.Service/Models/Employee/EmployeePersonalProfile.cs
- Namespace: Lista.Accessor.Profile.Service.Models.Employee
- Project: Lista.Accessor.Profile.Service
Implementation Notes:
- Follow Marten document model pattern (no [Table] attributes needed)
- Use Id property of type Guid for Marten identity
- Computed properties should be calculated, not stored
- Reference encryption service via dependency injection
Instruction Files:
- .github/instructions/marten.models.instructions.md (document patterns)
- .github/instructions/accessor-implementation.instructions.md (service patterns)
--notes (contextual metadata)
Purpose: Additional context, dependencies, and hints for the implementer.
Must Include:
- Parent story reference (with ADO ID)
- Blocking dependencies (what must be done first)
- Blocked dependents (what depends on this)
- Key instruction files
- Implementation hints/warnings
- Known edge cases
Format:
Context: Parent story <bead-id> (ADO #<number>)
Dependencies:
- Blocked by: <bead-id> (<reason>)
- Blocks: <bead-id> (<reason>)
Key Instruction Files:
- <path-to-instruction-file>
Hints:
- <Implementation tip>
- <Common pitfall to avoid>
Edge Cases:
- <Special scenario to handle>
Example:
Context: Parent story lista-kyy (ADO #669362) - Employee ID & Profile
Dependencies:
- Blocked by: lista-jmj (base model must exist first)
- Blocks: lista-49a (accessor implementation needs this model)
- Blocks: lista-0f1 (integration tests need this model)
Key Instruction Files:
- .github/instructions/marten.models.instructions.md
- .github/instructions/conventions.instructions.md
Hints:
- Use IDocumentSession for Marten operations
- Encryption service should be injected, not created directly
- WorkState validation should use a static list of valid codes
- IsNoStateTaxState should be a computed property, not stored
Edge Cases:
- Handle null SSN (some states don't require SSN)
- WorkState must be uppercase 2-letter code
- Employee_ID should be auto-generated on first save
Granularity Guidelines
| Story Scope | Expected Tasks |
|---|---|
| New field on existing model | 2-3 tasks |
| New accessor method | 3-4 tasks |
| New accessor service | 5-8 tasks |
| New engine | 8-12 tasks |
Rule of Thumb: If a task description exceeds 10 acceptance criteria, split it.
Example Decomposition
Story: "(stub) Employee ID & Profile" (lista-kyy, ADO #669362)
Acceptance Criteria:
- AC 1: Store Legal First Name, Last Name, and SSN (encrypted)
- AC 2: Store the Work State (to confirm "No State Income Tax" state)
- AC 3: Create a unique internal Employee_ID
Generated Tasks (with full details):
1. lista-jmj - Create EmployeePersonalProfile document model
Labels: project:Lista.Accessor.Profile, phase:model
Description:
Create the EmployeePersonalProfile Marten document model to store employee identity
and work location data. This model will support encrypted SSN storage using AES-256
and include validation for work state codes. The model serves as the persistence
layer for employee onboarding workflows.
Acceptance Criteria:
- Add FirstName property (string, required, max 100 chars)
- Add LastName property (string, required, max 100 chars)
- Add Employee_ID property (Guid, unique, auto-generated on save)
- Add WorkState property (string, 2-letter US state code, required)
- Create EmployeePersonalProfile.cs in Models/Employee/ folder
- Implement IEquatable<EmployeePersonalProfile> for comparison
- Add XML documentation for all public properties
- Ensure Marten can use Id property for document identity
Design:
Reference Pattern: src/services/accessor/profile/Lista.Accessor.Profile.Service/Models/ProfileDocument.cs
Project Structure:
- File: src/services/accessor/profile/Lista.Accessor.Profile.Service/Models/Employee/EmployeePersonalProfile.cs
- Namespace: Lista.Accessor.Profile.Service.Models.Employee
- Project: Lista.Accessor.Profile.Service
Implementation Notes:
- Marten uses Id (Guid) as document key automatically
- No [Table] attributes needed (document store, not relational)
- Properties should be public with init/set for immutability
- Follow record pattern if immutability is required
Instruction Files:
- .github/instructions/marten.models.instructions.md
Notes:
Context: Parent story lista-kyy (ADO #669362)
Dependencies:
- Blocks: lista-2nu (SSN encryption needs base model)
- Blocks: lista-trp (address fields extend this model)
Hints:
- Keep this task minimal - just the foundation properties
- SSN encryption and address fields come in follow-up tasks
- Use Guid.NewGuid() for Employee_ID if not provided
Blocks: lista-2nu, lista-trp
2. lista-2nu - Implement SSN encryption in EmployeePersonalProfile
Labels: project:Lista.Accessor.Profile, phase:model
Description:
Add encrypted SSN field to EmployeePersonalProfile using AES-256 encryption.
Implement encryption service integration and ensure SSN is never stored in
plaintext. Includes null handling for employees without SSN.
Acceptance Criteria:
- Add EncryptedSsn property (byte[], stores encrypted data)
- Create IEncryptionService interface with Encrypt/Decrypt methods
- Implement AesEncryptionService using AES-256-CBC
- Add Ssn property (string, sets EncryptedSsn via encryption)
- Encryption key configured via appsettings (not hardcoded)
- Handle null/empty SSN input (store as null in DB)
- Throw ArgumentException for invalid SSN format (###-##-####)
- Add unit tests for encryption round-trip
- Add XML documentation for encryption-related properties
Design:
Reference Pattern:
- SSN pattern: Research existing encryption patterns in Basis.Core
- Service pattern: src/services/accessor/profile/Lista.Accessor.Profile.Service/
Project Structure:
- Model: src/services/accessor/profile/Lista.Accessor.Profile.Service/Models/Employee/EmployeePersonalProfile.cs
- Interface: src/services/accessor/profile/Lista.Accessor.Profile.Interfaces/IEncryptionService.cs
- Service: src/services/accessor/profile/Lista.Accessor.Profile.Service/Services/AesEncryptionService.cs
Implementation Notes:
- Use System.Security.Cryptography.Aes for encryption
- Store IV (initialization vector) with encrypted data
- Encryption service should be stateless and thread-safe
- Consider using Data Protection API if available in Basis
Configuration:
- appsettings.json: "Encryption": { "Key": "<base64-key>" }
- Use IConfiguration to inject key
Instruction Files:
- .github/instructions/marten.models.instructions.md
- .github/instructions/conventions.instructions.md
Notes:
Context: Parent story lista-kyy (ADO #669362)
Dependencies:
- Blocked by: lista-jmj (base model must exist)
- Blocks: lista-49a (accessor needs complete model)
Key Instruction Files:
- .github/instructions/accessor-implementation.instructions.md
Hints:
- SSN validation regex: ^\d{3}-\d{2}-\d{4}$
- Store encrypted bytes, not base64 string (smaller)
- Use CryptoStream for encryption/decryption
- Consider async encryption for large batches
Edge Cases:
- Some employees may not have SSN (contractors)
- Handle empty string as null
- Validate SSN format before encryption
Blocked by: lista-jmj
Blocks: lista-49a
3. lista-trp - Add employee address fields to EmployeePersonalProfile
Labels: project:Lista.Accessor.Profile, phase:model
Description:
Extend EmployeePersonalProfile with work address and state tax jurisdiction fields.
Add WorkState validation for US state codes and implement IsNoStateTaxState computed
property to identify employees in states without income tax.
Acceptance Criteria:
- Add Street property (string, optional, max 200 chars)
- Add City property (string, optional, max 100 chars)
- Add State property (string, 2-letter code, optional)
- Add ZipCode property (string, optional, format #####-#### or #####)
- Update WorkState to use validation (must be valid US state code)
- Add IsNoStateTaxState computed property (bool, not stored)
- IsNoStateTaxState returns true for: AK, FL, NV, NH, SD, TN, TX, WA, WY
- Throw ArgumentException for invalid WorkState/State codes
- Add ZipCode format validation
- Update XML documentation for all new properties
Design:
Reference Pattern: src/services/accessor/profile/Lista.Accessor.Profile.Service/Models/Employee/EmployeePersonalProfile.cs
Project Structure:
- File: src/services/accessor/profile/Lista.Accessor.Profile.Service/Models/Employee/EmployeePersonalProfile.cs (modify)
- Namespace: Lista.Accessor.Profile.Service.Models.Employee
Implementation Notes:
- IsNoStateTaxState should be a computed property using expression body
- WorkState validation can use static HashSet<string> of valid codes
- ZipCode validation regex: ^\d{5}(-\d{4})?$
- State codes should be stored uppercase
Validation Pattern:
```csharp
private static readonly HashSet<string> NoStateTaxStates = new()
{ "AK", "FL", "NV", "NH", "SD", "TN", "TX", "WA", "WY" };
public bool IsNoStateTaxState =>
WorkState != null && NoStateTaxStates.Contains(WorkState.ToUpperInvariant());
Instruction Files:
- .github/instructions/marten.models.instructions.md
- .github/instructions/conventions.instructions.md
**Notes:**
Context: Parent story lista-kyy (ADO #669362)
Dependencies:
- Blocked by: lista-jmj (base model must exist)
- Blocks: lista-49a (accessor needs complete model)
Hints:
- Address fields are optional (remote workers may not have physical location)
- WorkState is required (for tax jurisdiction)
- State vs WorkState: State is mailing address, WorkState is tax jurisdiction
- Normalize state codes to uppercase on set
Edge Cases:
- WorkState required, but address fields optional
- Handle null WorkState gracefully in IsNoStateTaxState
- ZipCode can be 5 or 9 digits (ZIP+4 format)
**Blocked by:** lista-jmj
**Blocks:** lista-49a
---
#### 4. `lista-49a` - Implement ProfileAccessor CRUD operations
**Labels:** `project:Lista.Accessor.Profile`, `phase:implementation`
**Description:**
Implement basic CRUD operations in ProfileAccessor for EmployeePersonalProfile. Integrate with Marten document store for persistence and include proper error handling and validation. Support async operations throughout.
**Acceptance Criteria:**
- Implement GetEmployeeById(Guid id) returning Task<EmployeePersonalProfile?>
- Implement SaveEmployee(EmployeePersonalProfile profile) returning Task<Guid>
- Implement UpdateEmployee(EmployeePersonalProfile profile) returning Task
- Implement DeleteEmployee(Guid id) returning Task<bool>
- Use IDocumentSession for all Marten operations
- All methods async/await pattern
- Throw ArgumentNullException for null inputs
- SaveEmployee auto-generates Employee_ID if not provided
- UpdateEmployee validates employee exists before update
- Return null from GetEmployeeById if not found
- Ensure SSN remains encrypted through all operations
**Design:**
Reference Pattern: src/services/accessor/profile/Lista.Accessor.Profile.Service/ProfileAccessor.cs
Project Structure:
- File: src/services/accessor/profile/Lista.Accessor.Profile.Service/EmployeeProfileAccessor.cs
- Namespace: Lista.Accessor.Profile.Service
- Project: Lista.Accessor.Profile.Service
Implementation Notes:
- Inject IDocumentSession via constructor
- Use session.Store() for insert/update
- Use session.SaveChangesAsync() to commit
- Use session.Query<T>() for queries
- Follow MagicOnion service pattern (IService<T>)
Marten Operations:
// Get
var employee = await _session.LoadAsync<EmployeePersonalProfile>(id);
// Save
_session.Store(employee);
await _session.SaveChangesAsync();
// Update
var existing = await _session.LoadAsync<EmployeePersonalProfile>(id);
if (existing == null) throw new NotFoundException();
_session.Store(updated);
await _session.SaveChangesAsync();
Instruction Files:
- .github/instructions/accessor-implementation.instructions.md
- .github/instructions/magic.accessor.instructions.md
- .github/instructions/marten.models.instructions.md
**Notes:**
Context: Parent story lista-kyy (ADO #669362)
Dependencies:
- Blocked by: lista-2nu (SSN encryption must be in model)
- Blocked by: lista-trp (address fields must be in model)
- Blocks: lista-ui7 (tests need implementation)
- Blocks: lista-0f1 (tests need implementation)
Key Instruction Files:
- .github/instructions/accessor-implementation.instructions.md
- deps/basis/docs/basis-endpoints.md
Hints:
- Use IDocumentSession, not IDocumentStore directly
- Marten auto-commits on SaveChangesAsync()
- No need for explicit transactions for single operations
- Consider bulk operations for multiple saves
Edge Cases:
- Handle duplicate Employee_ID gracefully
- Validate SSN is encrypted before save
- DeleteEmployee should be soft delete (add IsDeleted flag)
**Blocked by:** lista-2nu, lista-trp
**Blocks:** lista-ui7, lista-0f1
---
#### 5. `lista-ui7` - Verify unique Employee_ID generation
**Labels:** `project:Lista.Accessor.Profile`, `phase:test`
**Description:**
Write integration tests to verify Employee_ID uniqueness and SSN constraint validation. Use TestContainers for PostgreSQL and test concurrent operations.
**Acceptance Criteria:**
- Test Employee_ID auto-generation is unique across records
- Test SSN uniqueness constraint prevents duplicates
- Test duplicate SSN returns appropriate error
- Test Employee_ID format matches Guid pattern
- Test concurrent saves don't create duplicate IDs
- Use TestContainers for real PostgreSQL instance
- Test cleanup (delete test data after run)
- All tests pass consistently
**Design:**
Reference Pattern: src/services/accessor/profile/Lista.Accessor.Profile.Tests/EmployeeProfileAccessTests.cs
Project Structure:
- File: src/services/accessor/profile/Lista.Accessor.Profile.Tests/EmployeeProfileAccessTests.cs
- Namespace: Lista.Accessor.Profile.Tests
- Project: Lista.Accessor.Profile.Tests
Instruction Files:
- .github/instructions/integration.tests.instructions.md
**Notes:**
Context: Parent story lista-kyy (ADO #669362)
Dependencies:
- Blocked by: lista-49a (implementation must exist)
Hints:
- Use TestContainers.PostgreSql package
- See integration.tests.instructions.md for setup
**Blocked by:** lista-49a
---
#### 6. `lista-0f1` - Add unit tests for SSN encryption and address fields
**Labels:** `project:Lista.Accessor.Profile`, `phase:test`
**Description:**
Write integration tests for SSN encryption and address field functionality including round-trip encryption, validation, and edge cases.
**Acceptance Criteria:**
- Test SSN encryption round-trip (store encrypted, retrieve decrypted)
- Test address field persistence
- Test WorkState validation rejects invalid codes
- Test IsNoStateTaxState returns true for no-tax states
- Test null/empty SSN handling
- Use TestContainers for PostgreSQL
**Design:**
Reference Pattern: src/services/accessor/profile/Lista.Accessor.Profile.Tests/EmployeeProfileAccessTests.cs
Project Structure:
- File: src/services/accessor/profile/Lista.Accessor.Profile.Tests/EmployeeProfileAccessTests.cs
Instruction Files:
- .github/instructions/integration.tests.instructions.md
**Notes:**
Context: Parent story lista-kyy (ADO #669362)
Dependencies:
- Blocked by: lista-49a (implementation must exist)
**Blocked by:** lista-49a
---
**Summary:**
- 6 tasks total
- Proper dependency chain: foundation → enhancements → implementation → tests
- All tasks have complete acceptance criteria, design notes, and context
- Each task is atomic and AI-agent sized (1-2 hours)