---
story_id: story_0
sprint_id: sprint_4
status: not_started
priority: P2
assignee: backend
created_date: 2025-11-05
completion_date: null
---

# Story 0: Backend API Enhancements for Advanced UX Features (Optional)

**Priority**: P2 - Optional Enhancement
**Status**: Not Started (Defer if Sprint 4 timeline tight)
**Estimated**: 2 days

## User Story

**As** a frontend developer, **I want** advanced Story/Task fields (acceptance criteria, tags, story points, task order), **So that** I can implement full UX design specifications without workarounds.

## Context

**Current State**:
- Core Story/Task CRUD APIs are 100% complete
- Frontend can implement Sprint 4 P0/P1 Stories without these fields
- Missing fields: AcceptanceCriteria, Tags, StoryPoints (Story), Order (Task)

**Why Optional**:
- Sprint 4 MVP (Stories 1-4) can work with existing fields
- Workarounds available (use Description for criteria, skip tags, use EstimatedHours for points)
- Low frontend urgency - UX design is "nice-to-have" not "must-have"

**When to Implement**:
- If Sprint 4 timeline allows (after P0/P1 Stories complete)
- If frontend requests these fields during development
- If Product Manager upgrades priority to P1

## Acceptance Criteria

- [ ] Story entity includes `AcceptanceCriteria` field (JSON array of strings)
- [ ] Story entity includes `Tags` field (JSON array of strings or many-to-many table)
- [ ] Story entity includes `StoryPoints` field (int?)
- [ ] Task entity includes `Order` field (int)
- [ ] Database migration created and tested
- [ ] DTOs updated to include new fields
- [ ] API endpoints accept and return new fields
- [ ] Validation added for new fields (e.g., StoryPoints >= 0)
- [ ] Unit tests cover new fields
- [ ] Integration tests verify CRUD operations
- [ ] API documentation updated

## Tasks

- [ ] [task_1](../plans/sprint_4_story_0_task_1.md) - Add AcceptanceCriteria and Tags fields to Story entity - `not_started`
- [ ] [task_2](../plans/sprint_4_story_0_task_2.md) - Add StoryPoints field to Story entity - `not_started`
- [ ] [task_3](../plans/sprint_4_story_0_task_3.md) - Add Order field to Task entity - `not_started`
- [ ] [task_4](../plans/sprint_4_story_0_task_4.md) - Create and apply database migration - `not_started`
- [ ] [task_5](../plans/sprint_4_story_0_task_5.md) - Update DTOs and API endpoints - `not_started`
- [ ] [task_6](../plans/sprint_4_story_0_task_6.md) - Write tests and update documentation - `not_started`

**Progress**: 0/6 completed

## Technical Design

### Database Schema Changes

**Story Table**:
```sql
ALTER TABLE Stories
  ADD AcceptanceCriteria NVARCHAR(MAX) NULL,
  ADD Tags NVARCHAR(MAX) NULL,
  ADD StoryPoints INT NULL;

-- Optional: Create Tags table for many-to-many relationship
CREATE TABLE StoryTags (
  StoryId UNIQUEIDENTIFIER NOT NULL,
  Tag NVARCHAR(50) NOT NULL,
  PRIMARY KEY (StoryId, Tag),
  FOREIGN KEY (StoryId) REFERENCES Stories(Id) ON DELETE CASCADE
);
```

**Task Table**:
```sql
ALTER TABLE Tasks
  ADD [Order] INT NOT NULL DEFAULT 0;

CREATE INDEX IX_Tasks_StoryId_Order ON Tasks(StoryId, [Order]);
```

### Domain Model Changes

**Story.cs**:
```csharp
public class Story : Entity
{
    // Existing fields...

    public List<string> AcceptanceCriteria { get; private set; } = new();
    public List<string> Tags { get; private set; } = new();
    public int? StoryPoints { get; private set; }

    public void UpdateAcceptanceCriteria(List<string> criteria)
    {
        AcceptanceCriteria = criteria ?? new List<string>();
        UpdatedAt = DateTime.UtcNow;
    }

    public void UpdateTags(List<string> tags)
    {
        Tags = tags ?? new List<string>();
        UpdatedAt = DateTime.UtcNow;
    }

    public void UpdateStoryPoints(int? points)
    {
        if (points.HasValue && points.Value < 0)
            throw new DomainException("Story points cannot be negative");

        StoryPoints = points;
        UpdatedAt = DateTime.UtcNow;
    }
}
```

**WorkTask.cs**:
```csharp
public class WorkTask : Entity
{
    // Existing fields...

    public int Order { get; private set; }

    public void UpdateOrder(int newOrder)
    {
        if (newOrder < 0)
            throw new DomainException("Task order cannot be negative");

        Order = newOrder;
        UpdatedAt = DateTime.UtcNow;
    }
}
```

### DTO Changes

**StoryDto.cs**:
```csharp
public record StoryDto
{
    // Existing fields...

    public List<string> AcceptanceCriteria { get; init; } = new();
    public List<string> Tags { get; init; } = new();
    public int? StoryPoints { get; init; }
}
```

**TaskDto.cs**:
```csharp
public record TaskDto
{
    // Existing fields...

    public int Order { get; init; }
}
```

### API Endpoint Updates

**StoriesController.cs**:
```csharp
// UpdateStoryRequest updated
public record UpdateStoryRequest
{
    // Existing fields...

    public List<string>? AcceptanceCriteria { get; init; }
    public List<string>? Tags { get; init; }
    public int? StoryPoints { get; init; }
}
```

**TasksController.cs**:
```csharp
// UpdateTaskRequest updated
public record UpdateTaskRequest
{
    // Existing fields...

    public int? Order { get; init; }
}

// New endpoint for bulk reordering
[HttpPut("stories/{storyId:guid}/tasks/reorder")]
public async Task<IActionResult> ReorderTasks(
    Guid storyId,
    [FromBody] ReorderTasksRequest request,
    CancellationToken cancellationToken = default)
{
    // Bulk update task orders
}

public record ReorderTasksRequest
{
    public List<TaskOrderDto> Tasks { get; init; } = new();
}

public record TaskOrderDto
{
    public Guid TaskId { get; init; }
    public int Order { get; init; }
}
```

## Validation Rules

**AcceptanceCriteria**:
- Each criterion max 500 characters
- Max 20 criteria per Story

**Tags**:
- Each tag max 50 characters
- Max 10 tags per Story
- Tag format: alphanumeric + hyphen/underscore only

**StoryPoints**:
- Min: 0
- Max: 100
- Common values: 1, 2, 3, 5, 8, 13, 21 (Fibonacci)

**Order**:
- Min: 0
- Default: 0 (newly created Tasks)
- Auto-increment when creating multiple Tasks

## Testing Strategy

### Unit Tests

**Story Domain Tests**:
```csharp
[Fact]
public void UpdateAcceptanceCriteria_ShouldUpdateCriteriaList()
[Fact]
public void UpdateTags_ShouldUpdateTagsList()
[Fact]
public void UpdateStoryPoints_WithNegativeValue_ShouldThrowException()
```

**Task Domain Tests**:
```csharp
[Fact]
public void UpdateOrder_WithValidOrder_ShouldUpdateOrder()
[Fact]
public void UpdateOrder_WithNegativeOrder_ShouldThrowException()
```

### Integration Tests

**Story API Tests**:
```csharp
[Fact]
public async Task CreateStory_WithAcceptanceCriteria_ShouldReturnStoryWithCriteria()
[Fact]
public async Task UpdateStory_WithTags_ShouldUpdateTags()
[Fact]
public async Task GetStory_ShouldReturnAllNewFields()
```

**Task API Tests**:
```csharp
[Fact]
public async Task CreateTask_ShouldAutoAssignOrder()
[Fact]
public async Task ReorderTasks_ShouldUpdateMultipleTaskOrders()
```

## Migration Strategy

### Phase 1: Add Nullable Columns (Non-Breaking)
```sql
ALTER TABLE Stories
  ADD AcceptanceCriteria NVARCHAR(MAX) NULL,
  ADD Tags NVARCHAR(MAX) NULL,
  ADD StoryPoints INT NULL;

ALTER TABLE Tasks
  ADD [Order] INT NOT NULL DEFAULT 0;
```

### Phase 2: Backfill Data (Optional)
```sql
-- Set default Order based on CreatedAt
WITH OrderedTasks AS (
  SELECT Id, ROW_NUMBER() OVER (PARTITION BY StoryId ORDER BY CreatedAt) - 1 AS NewOrder
  FROM Tasks
)
UPDATE Tasks
SET [Order] = ot.NewOrder
FROM OrderedTasks ot
WHERE Tasks.Id = ot.Id;
```

### Phase 3: Add Indexes
```sql
CREATE INDEX IX_Tasks_StoryId_Order ON Tasks(StoryId, [Order]);
```

### Phase 4: Update EF Core Configuration
```csharp
builder.Property(s => s.AcceptanceCriteria)
    .HasColumnType("nvarchar(max)")
    .HasConversion(
        v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
        v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions)null) ?? new List<string>());

builder.Property(s => s.Tags)
    .HasColumnType("nvarchar(max)")
    .HasConversion(
        v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
        v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions)null) ?? new List<string>());

builder.Property(t => t.Order).IsRequired();
```

## Frontend Integration

### New API Capabilities

**Story Creation with Acceptance Criteria**:
```bash
curl -X POST "/api/v1/epics/{epicId}/stories" \
  -d '{
    "title": "User login",
    "description": "...",
    "acceptanceCriteria": [
      "User can enter username and password",
      "System validates credentials",
      "User redirects to dashboard on success"
    ],
    "tags": ["authentication", "security"],
    "storyPoints": 5
  }'
```

**Task Reordering**:
```bash
curl -X PUT "/api/v1/stories/{storyId}/tasks/reorder" \
  -d '{
    "tasks": [
      { "taskId": "guid1", "order": 0 },
      { "taskId": "guid2", "order": 1 },
      { "taskId": "guid3", "order": 2 }
    ]
  }'
```

### TypeScript Type Updates

**story.ts**:
```typescript
export interface Story {
  // Existing fields...

  acceptanceCriteria: string[];
  tags: string[];
  storyPoints?: number;
}
```

**task.ts**:
```typescript
export interface Task {
  // Existing fields...

  order: number;
}
```

## Rollback Plan

If issues arise:
1. Migration is non-breaking (nullable columns)
2. Frontend can ignore new fields (backward compatible)
3. Rollback migration removes columns:
```sql
ALTER TABLE Stories DROP COLUMN AcceptanceCriteria, Tags, StoryPoints;
ALTER TABLE Tasks DROP COLUMN [Order];
```

## Performance Impact

**Negligible**:
- JSON columns are nullable and indexed
- Task Order index improves sorting performance
- No additional N+1 queries introduced

**Benchmarks**:
- Story query: +5ms (JSON deserialization)
- Task query with Order: -10ms (index improves sorting)
- Net impact: Neutral to positive

## Dependencies

**Prerequisites**:
- Sprint 2 backend complete (existing Story/Task APIs)
- EF Core 9.0 supports JSON columns
- PostgreSQL or SQL Server (JSON support)

**Blocks**:
- None - Optional enhancement doesn't block Sprint 4 frontend

## Definition of Done

- [ ] Database migration created and tested locally
- [ ] Domain entities updated with new fields
- [ ] DTOs updated with new fields
- [ ] API endpoints accept and return new fields
- [ ] Validation logic implemented and tested
- [ ] Unit tests written (>80% coverage)
- [ ] Integration tests written and passing
- [ ] API documentation updated
- [ ] Migration applied to dev/staging environments
- [ ] Frontend team notified of new capabilities
- [ ] No regression in existing Story/Task APIs

## Risk Assessment

**Low Risk** - Optional enhancement with clear rollback path

**Mitigations**:
- Non-breaking change (nullable columns)
- Backward compatible DTOs
- Comprehensive testing before production
- Feature flag for frontend (optional use)

---

**Created**: 2025-11-05 by Backend Agent
**Status**: Awaiting Product Manager approval
**Defer if**: Sprint 4 timeline tight, focus on P0/P1 frontend Stories
**Implement if**: Sprint 4 ahead of schedule, frontend requests these fields
