PBI-46: Sprint-niveau jobflow met cascade-FAIL (F1/F2/F4 Scrum4Me) (#136)
* ST-1243: F1 schema + propagateStatusUpwards-helper voor sprint-flow Schema-uitbreidingen voor de sprint-niveau jobflow (PBI-46): - TaskStatus, StoryStatus, PbiStatus, SprintStatus krijgen FAILED - Nieuwe enums: SprintRunStatus, PrStrategy - Nieuw SprintRun-model dat per-task ClaudeJobs groepeert - ClaudeJob.sprint_run_id koppeling + index - Product.pr_strategy (default SPRINT) - Bijhorende Prisma-migratie propagateStatusUpwards vervangt updateTaskStatusWithStoryPromotion en herevalueert de keten Task → Story → PBI → Sprint → SprintRun bij elke task-statuswijziging. Bij FAILED cancelt het sibling-jobs in dezelfde SprintRun. PBI-status BLOCKED blijft handmatig en wordt niet overschreven. Status-mappers + theme krijgen failed-token + label-uitbreidingen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ST-1244: F2 sprint-runs actions + deprecate per-task enqueues actions/sprint-runs.ts (nieuw): - startSprintRunAction met pre-flight (impl_plan / open ClaudeQuestion / PBI BLOCKED|FAILED) - Maakt SprintRun + ClaudeJobs in PBI→Story→Task volgorde - resumeSprintAction zet FAILED tasks/stories/PBIs terug en start nieuwe SprintRun - cancelSprintRunAction breekt lopende SprintRun af zonder cascade actions/claude-jobs.ts: - enqueueClaudeJobAction, enqueueAllTodoJobsAction, previewEnqueueAllAction, enqueueClaudeJobsBatchAction nu deprecation-stubs (UI-cleanup volgt in F4) - cancelClaudeJobAction blijft beschikbaar voor losse jobs Tests bijgewerkt: 11 nieuwe sprint-runs tests, claude-jobs(-batch) tests herzien naar deprecation-asserties. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ST-1246: F4 UI Start/Resume/Cancel sprint + pr_strategy dropdown - components/sprint/sprint-run-controls.tsx: knoppen Start Sprint (sprintStatus=ACTIVE), Hervat sprint (sprintStatus=FAILED) en Annuleer sprint-run (lopende run). Pre-flight blocker-modal toont blockers met directe links naar de relevante pagina's. - components/products/pr-strategy-select.tsx: dropdown SPRINT|STORY in product-settings, met optimistic update + sonner-toast op fail. - actions/products.ts: updatePrStrategyAction (eigenaar-only, demo-block). - Sprint-page: query op actieve SprintRun + tonen van controls-balk. Live cascade-visualisatie (T-634) staat als follow-up genoteerd — huidige sprint-board statusbadges volstaan voor MVP. De Solo-board "Voer uit"-knoppen zijn niet expliciet verwijderd; ze tonen nu de deprecation-error van de gestubde actions tot de Solo-flow opnieuw ontworpen wordt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ab8c3dca3f
commit
77617e89ac
25 changed files with 1798 additions and 1014 deletions
|
|
@ -22,11 +22,13 @@ enum StoryStatus {
|
|||
OPEN
|
||||
IN_SPRINT
|
||||
DONE
|
||||
FAILED
|
||||
}
|
||||
|
||||
enum PbiStatus {
|
||||
READY
|
||||
BLOCKED
|
||||
FAILED
|
||||
DONE
|
||||
}
|
||||
|
||||
|
|
@ -58,6 +60,7 @@ enum TaskStatus {
|
|||
IN_PROGRESS
|
||||
REVIEW
|
||||
DONE
|
||||
FAILED
|
||||
}
|
||||
|
||||
enum LogType {
|
||||
|
|
@ -74,6 +77,21 @@ enum TestStatus {
|
|||
enum SprintStatus {
|
||||
ACTIVE
|
||||
COMPLETED
|
||||
FAILED
|
||||
}
|
||||
|
||||
enum SprintRunStatus {
|
||||
QUEUED
|
||||
RUNNING
|
||||
PAUSED
|
||||
DONE
|
||||
FAILED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
enum PrStrategy {
|
||||
SPRINT
|
||||
STORY
|
||||
}
|
||||
|
||||
enum IdeaStatus {
|
||||
|
|
@ -109,32 +127,33 @@ enum UserQuestionStatus {
|
|||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
username String @unique
|
||||
email String? @unique
|
||||
password_hash String
|
||||
is_demo Boolean @default(false)
|
||||
bio String? @db.VarChar(160)
|
||||
bio_detail String? @db.VarChar(2000)
|
||||
must_reset_password Boolean @default(false)
|
||||
avatar_data Bytes?
|
||||
active_product_id String?
|
||||
active_product Product? @relation("UserActiveProduct", fields: [active_product_id], references: [id], onDelete: SetNull)
|
||||
idea_code_counter Int @default(0)
|
||||
min_quota_pct Int @default(20)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
roles UserRole[]
|
||||
api_tokens ApiToken[]
|
||||
products Product[]
|
||||
ideas Idea[]
|
||||
product_members ProductMember[]
|
||||
assigned_stories Story[] @relation("StoryAssignee")
|
||||
login_pairings LoginPairing[]
|
||||
asked_questions ClaudeQuestion[] @relation("ClaudeQuestionAsker")
|
||||
answered_questions ClaudeQuestion[] @relation("ClaudeQuestionAnswerer")
|
||||
claude_jobs ClaudeJob[]
|
||||
claude_workers ClaudeWorker[]
|
||||
id String @id @default(cuid())
|
||||
username String @unique
|
||||
email String? @unique
|
||||
password_hash String
|
||||
is_demo Boolean @default(false)
|
||||
bio String? @db.VarChar(160)
|
||||
bio_detail String? @db.VarChar(2000)
|
||||
must_reset_password Boolean @default(false)
|
||||
avatar_data Bytes?
|
||||
active_product_id String?
|
||||
active_product Product? @relation("UserActiveProduct", fields: [active_product_id], references: [id], onDelete: SetNull)
|
||||
idea_code_counter Int @default(0)
|
||||
min_quota_pct Int @default(20)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
roles UserRole[]
|
||||
api_tokens ApiToken[]
|
||||
products Product[]
|
||||
ideas Idea[]
|
||||
product_members ProductMember[]
|
||||
assigned_stories Story[] @relation("StoryAssignee")
|
||||
login_pairings LoginPairing[]
|
||||
asked_questions ClaudeQuestion[] @relation("ClaudeQuestionAsker")
|
||||
answered_questions ClaudeQuestion[] @relation("ClaudeQuestionAnswerer")
|
||||
claude_jobs ClaudeJob[]
|
||||
claude_workers ClaudeWorker[]
|
||||
started_sprint_runs SprintRun[] @relation("SprintRunStartedBy")
|
||||
|
||||
@@index([active_product_id])
|
||||
@@map("users")
|
||||
|
|
@ -175,6 +194,7 @@ model Product {
|
|||
repo_url String?
|
||||
definition_of_done String
|
||||
auto_pr Boolean @default(false)
|
||||
pr_strategy PrStrategy @default(SPRINT)
|
||||
archived Boolean @default(false)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
|
@ -277,11 +297,36 @@ model Sprint {
|
|||
completed_at DateTime?
|
||||
stories Story[]
|
||||
tasks Task[]
|
||||
sprint_runs SprintRun[]
|
||||
|
||||
@@index([product_id, status])
|
||||
@@map("sprints")
|
||||
}
|
||||
|
||||
model SprintRun {
|
||||
id String @id @default(cuid())
|
||||
sprint Sprint @relation(fields: [sprint_id], references: [id], onDelete: Cascade)
|
||||
sprint_id String
|
||||
started_by User @relation("SprintRunStartedBy", fields: [started_by_id], references: [id])
|
||||
started_by_id String
|
||||
status SprintRunStatus @default(QUEUED)
|
||||
pr_strategy PrStrategy
|
||||
branch String?
|
||||
pr_url String?
|
||||
started_at DateTime?
|
||||
finished_at DateTime?
|
||||
failure_reason String?
|
||||
failed_task Task? @relation("SprintRunFailedTask", fields: [failed_task_id], references: [id], onDelete: SetNull)
|
||||
failed_task_id String?
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
jobs ClaudeJob[]
|
||||
|
||||
@@index([sprint_id, status])
|
||||
@@index([started_by_id, status])
|
||||
@@map("sprint_runs")
|
||||
}
|
||||
|
||||
model Task {
|
||||
id String @id @default(cuid())
|
||||
story Story @relation(fields: [story_id], references: [id], onDelete: Cascade)
|
||||
|
|
@ -308,6 +353,7 @@ model Task {
|
|||
updated_at DateTime @updatedAt
|
||||
claude_questions ClaudeQuestion[]
|
||||
claude_jobs ClaudeJob[]
|
||||
sprint_run_failures SprintRun[] @relation("SprintRunFailedTask")
|
||||
|
||||
@@unique([product_id, code])
|
||||
@@index([story_id, priority, sort_order])
|
||||
|
|
@ -326,6 +372,8 @@ model ClaudeJob {
|
|||
task_id String?
|
||||
idea Idea? @relation(fields: [idea_id], references: [id], onDelete: Cascade)
|
||||
idea_id String?
|
||||
sprint_run SprintRun? @relation(fields: [sprint_run_id], references: [id], onDelete: SetNull)
|
||||
sprint_run_id String?
|
||||
kind ClaudeJobKind @default(TASK_IMPLEMENTATION)
|
||||
status ClaudeJobStatus @default(QUEUED)
|
||||
claimed_by_token ApiToken? @relation(fields: [claimed_by_token_id], references: [id], onDelete: SetNull)
|
||||
|
|
@ -352,31 +400,32 @@ model ClaudeJob {
|
|||
@@index([user_id, status])
|
||||
@@index([task_id, status])
|
||||
@@index([idea_id, status])
|
||||
@@index([sprint_run_id, status])
|
||||
@@index([status, claimed_at])
|
||||
@@index([status, finished_at])
|
||||
@@map("claude_jobs")
|
||||
}
|
||||
|
||||
model ModelPrice {
|
||||
id String @id @default(cuid())
|
||||
model_id String @unique
|
||||
input_price_per_1m Decimal @db.Decimal(12, 6)
|
||||
output_price_per_1m Decimal @db.Decimal(12, 6)
|
||||
cache_read_price_per_1m Decimal @db.Decimal(12, 6)
|
||||
cache_write_price_per_1m Decimal @db.Decimal(12, 6)
|
||||
currency String @default("USD")
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
id String @id @default(cuid())
|
||||
model_id String @unique
|
||||
input_price_per_1m Decimal @db.Decimal(12, 6)
|
||||
output_price_per_1m Decimal @db.Decimal(12, 6)
|
||||
cache_read_price_per_1m Decimal @db.Decimal(12, 6)
|
||||
cache_write_price_per_1m Decimal @db.Decimal(12, 6)
|
||||
currency String @default("USD")
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@map("model_prices")
|
||||
}
|
||||
|
||||
model ClaudeWorker {
|
||||
id String @id @default(cuid())
|
||||
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
|
||||
user_id String
|
||||
token ApiToken @relation(fields: [token_id], references: [id], onDelete: Cascade)
|
||||
token_id String
|
||||
id String @id @default(cuid())
|
||||
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
|
||||
user_id String
|
||||
token ApiToken @relation(fields: [token_id], references: [id], onDelete: Cascade)
|
||||
token_id String
|
||||
product_id String?
|
||||
started_at DateTime @default(now())
|
||||
last_seen_at DateTime @default(now())
|
||||
|
|
@ -437,8 +486,8 @@ model IdeaProduct {
|
|||
product_id String
|
||||
created_at DateTime @default(now())
|
||||
|
||||
idea Idea @relation(fields: [idea_id], references: [id], onDelete: Cascade)
|
||||
product Product @relation(fields: [product_id], references: [id], onDelete: Cascade)
|
||||
idea Idea @relation(fields: [idea_id], references: [id], onDelete: Cascade)
|
||||
product Product @relation(fields: [product_id], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([idea_id, product_id])
|
||||
@@index([product_id])
|
||||
|
|
@ -468,7 +517,7 @@ model UserQuestion {
|
|||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
idea Idea @relation(fields: [idea_id], references: [id], onDelete: Cascade)
|
||||
idea Idea @relation(fields: [idea_id], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([idea_id, status])
|
||||
@@index([user_id])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue