Files
hanarang-rails/prisma/schema.prisma
이랑이 a88323716d feat(stage-1-2): hierarchical sub-agent team — sister-agent + SubTask tracking
Stage 1 + 2 통합 구현 — 사용자 피드백 반영:
  manager / principal / lead / junior 계층 구조 추가.
  부장이 복잡도를 판단해서 팀을 동적으로 꾸린다.

Design:
- .plans/design/hierarchy.md — 전체 설계 문서 (점수/tier/plan/escalation)

Prisma schema:
- SubTask (계층 트리 + 역할/모델/state/complexity)
- SubTaskEvent (JSONL 스타일 이벤트 로그)

rails (core):
- src/hierarchy/roles.ts — 4역할 기본 config + 모델 매핑
  manager/principal: gpt-5.4 / glm-5.1
  lead: gpt-codex-5.3 / glm-5
  junior: glm-5-turbo / gpt-5
- src/hierarchy/complexity.ts — 규칙 기반 스코어러 (7 factors, 0-100)
- src/hierarchy/planner.ts — tier → DecompositionPlan (trivial/simple/moderate/complex/massive)
  + concurrency budget 강제
- src/hierarchy/store.ts — Prisma CRUD + tree builder
- src/handoff/http-transport.ts — 실제 HTTP transport (MockTransport 대체)
- src/handoff/build.ts — config + env 기반 transport 빌더
  (env RAILS_TRANSPORT_MODE + RAILS_AGENT_{STAGE}_HOST 오버라이드)
- src/server/http.ts — sub-task 엔드포인트 4개 추가
    POST /api/sub-tasks
    PATCH /api/sub-tasks/:id
    POST /api/sub-tasks/:id/events
    GET /api/pipelines/:id/sub-tasks (tree view)

sister-agent (new sub-project):
- sister-agent/ — 각 LXC 에 배포될 Node.js 데몬
- src/types.ts, roles.ts, complexity.ts, planner.ts
- src/spawn.ts — executeInvocation: 복잡도 점수 → plan → spawn 트리
  simulation mode (현재는 work 를 delay 로 시뮬레이트, LLM 연동은 후속)
- src/rails-client.ts — sub-task / event push
- src/server.ts — POST /invoke 엔드포인트 (port 18801)

LXC 리소스 실측 기반 기본 concurrency:
  104/106/107: 8 concurrent sub-agents
  105 (narang, build 중): 6

검증: tsc --noEmit ✓ | vitest 105/105 ✓ | rails build ✓ | sister-agent build ✓

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:11:16 +09:00

139 lines
4.3 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Pipeline {
id String @id @db.VarChar(26) // ULID
projectName String @db.VarChar(255)
requirements String @db.Text
currentState String @db.VarChar(50) @default("idle")
contextJson String @db.LongText
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
transitions StateTransition[]
actorSpawns ActorSpawn[]
contracts Contract[]
escalations Escalation[]
subTasks SubTask[]
@@index([currentState])
@@index([createdAt])
@@map("pipelines")
}
model StateTransition {
id Int @id @default(autoincrement())
pipelineId String @db.VarChar(26)
fromState String @db.VarChar(50)
toState String @db.VarChar(50)
eventType String @db.VarChar(50)
eventPayload String @db.LongText
timestamp DateTime @default(now())
pipeline Pipeline @relation(fields: [pipelineId], references: [id], onDelete: Cascade)
@@index([pipelineId, timestamp])
@@index([eventType])
@@map("state_transitions")
}
model ActorSpawn {
id Int @id @default(autoincrement())
pipelineId String @db.VarChar(26)
actorName String @db.VarChar(100)
stage String @db.VarChar(50)
spawnedAt DateTime @default(now())
exitCode Int?
exitedAt DateTime?
resultJson String? @db.LongText
pipeline Pipeline @relation(fields: [pipelineId], references: [id], onDelete: Cascade)
@@index([pipelineId, spawnedAt])
@@map("actor_spawns")
}
model Contract {
id String @id @db.VarChar(26) // ULID
pipelineId String @db.VarChar(26)
sprintId String @db.VarChar(100)
version String @db.VarChar(20) @default("v1")
bodyJson String @db.LongText
frozenAt DateTime?
createdAt DateTime @default(now())
pipeline Pipeline @relation(fields: [pipelineId], references: [id], onDelete: Cascade)
@@index([pipelineId])
@@index([sprintId])
@@map("contracts")
}
model SubTask {
id String @id @db.VarChar(26)
pipelineId String @db.VarChar(26)
parentId String? @db.VarChar(26)
role String @db.VarChar(30)
agentName String @db.VarChar(50)
title String @db.VarChar(500)
description String @db.Text
state String @db.VarChar(30) @default("queued")
complexityScore Int?
complexityTier String? @db.VarChar(20)
model String @db.VarChar(50) @default("")
resultJson String? @db.LongText
errorReason String? @db.Text
startedAt DateTime?
completedAt DateTime?
createdAt DateTime @default(now())
pipeline Pipeline @relation(fields: [pipelineId], references: [id], onDelete: Cascade)
parent SubTask? @relation("SubTaskHierarchy", fields: [parentId], references: [id], onDelete: NoAction, onUpdate: NoAction)
children SubTask[] @relation("SubTaskHierarchy")
events SubTaskEvent[]
@@index([pipelineId, parentId])
@@index([state])
@@index([agentName, state])
@@map("sub_tasks")
}
model SubTaskEvent {
id Int @id @default(autoincrement())
subTaskId String @db.VarChar(26)
eventType String @db.VarChar(50)
payloadJson String @db.LongText
timestamp DateTime @default(now())
subTask SubTask @relation(fields: [subTaskId], references: [id], onDelete: Cascade)
@@index([subTaskId, timestamp])
@@index([eventType])
@@map("sub_task_events")
}
model Escalation {
id String @id @db.VarChar(26) // ULID
pipelineId String @db.VarChar(26)
reason String @db.VarChar(500)
errorCategory String @db.VarChar(50)
stage String @db.VarChar(50) @default("")
attempts Int @default(0)
contextSnapshot String @db.LongText
resolvedAt DateTime?
resolution String? @db.VarChar(50)
createdAt DateTime @default(now())
pipeline Pipeline @relation(fields: [pipelineId], references: [id], onDelete: Cascade)
@@index([pipelineId])
@@index([createdAt])
@@map("escalations")
}