한 줄 목표
AI 분석 요청을 Server DB에 남는 비동기 AiRun resource 로 생성·조회·재시도하고, HR이 채택한 candidate만 Task로 만들도록 구현합니다.
화면 기준
Figma [PWF] Prototype-WireFrame-3rd의 05_Desktop Core Product > CREATE / REVIEW
자연어 입력 후 즉시 완료된 것처럼 보이지 않고 실행 상태를 조회합니다.
여러 candidate를 카드로 보여 주고 HR이 각각 채택·수정·폐기합니다.
누락정보나 모호성이 있으면 실패 화면이 아니라 추가 확인 화면을 보여 줍니다.
세부 요청·응답 필드는 Notion API 명세 를 기준으로 합니다.
사용자 흐름
POST /api/v1/ai-runs가 요청·Idempotency-Key를 저장하고 202 + aiRunId 반환
Durable worker가 attempt를 만들고 [AI Integration] AiRuntimeClient·계약 검증·장애 격리 구현 #8 AiRuntimeClient 호출
Server가 Runtime 응답을 다시 검증하고 candidate 저장
Client가 GET /api/v1/ai-runs/{aiRunId}로 기술 상태와 분석 outcome 조회
HR이 candidate-decisions로 후보를 채택·수정·폐기
채택 후보만 제안 상태에 맞는 Task가 되며 승인·발송은 별도 command
소유 API
POST /tasks/analyze, /task-analyses/{id}/confirm, /ai-runs/{id}/confirm은 canonical API가 아닙니다.
서로 다른 상태를 구분합니다
종류
값
의미
AiRun status
QUEUED, RUNNING, RETRYING, SUCCEEDED, FAILED
Server의 실행·복구 상태
analysisOutcome
NEEDS_INFO, REVIEW_REQUIRED
정상 분석 결과와 HR의 다음 행동
candidate.proposedTaskStatus
DRAFT, NEEDS_INFO, READY_FOR_REVIEW
후보를 채택했을 때 만들 Task의 시작 상태
낮은 confidence, ambiguity, missing slot은 FAILED가 아닙니다. Runtime 호출·Schema·deadline처럼 기술적으로 결과를 신뢰할 수 없을 때만 실패합니다.
수동 retry는 같은 Run에서 FAILED → RETRYING으로 전이하고 새 AiAttempt를 만듭니다. 이전 attempt 기록은 수정·삭제하지 않습니다.
Candidate 결정 계약
Header: Idempotency-Key
Body: decisions[{candidateId, action=ACCEPT|DISCARD, edits?}], expectedRunVersion
ACCEPT: Server가 허용 필드와 업무 규칙을 재검증한 뒤 Task 생성
DISCARD: 후보는 삭제하지 않고 폐기 결정을 감사 가능하게 보존
같은 decision 재전송은 같은 결과를 반환하며 Task를 중복 생성하지 않음
일부 후보만 선택할 수 있고, 선택되지 않은 후보를 자동 승인하지 않음
저장할 정보
id, companyId, actor, masked input/hash, Idempotency-Key hash
status, analysisOutcome, attempt/retry count, nextAttemptAt, @Version
candidate와 validation error, 제안 Task 상태, 채택/폐기 decision, Task reference
requestId, attemptId, traceId, latencyMs, error code
backend/agent/model/prompt/contextPack/workflowCatalog/contract/knowledge version
민감 원문, 전체 Prompt, Provider secret은 저장하지 않습니다.
구현 범위
Retry 소유권
Server retry: 같은 AiRun에 새로운 AiAttempt를 영속 생성하고 202 + 같은 aiRunId 반환
AI Runtime retry: 한 attempt 내부의 제한된 Provider retry
RemoteAiRuntimeClient 투명 retry 금지; 총 호출 횟수가 곱해지지 않게 함
같은 retry Idempotency-Key의 반복 호출은 AiAttempt 하나만 생성
완료 조건
경계 밖
관계
2026-08-03 통신 방식 및 작업 분리 결정
Agent Plan-Act 흐름을 검토한 결과, Server ↔ AI Runtime의 기본 통신은 #8의 REST·JSON 계약을 유지합니다. SSE나 WebSocket을 양방향 Agent 통신의 기준으로 사용하지 않습니다.
Client는 기존 202 + aiRunId와 GET /api/v1/ai-runs/{aiRunId} polling을 기본으로 사용합니다.
Agent가 requiredFieldKeys를 반환하면 Server가 DB 값을 조회하고, 같은 AiRun 아래 새 AiAttempt를 기록한 뒤 requestedFields를 포함해 다시 호출합니다.
Agent 분석을 시작할 때 실제 Task를 먼저 만들지 않습니다. candidate 저장과 HR의 채택 결정을 거친 뒤에만 Task를 생성합니다.
Client 실시간 진행 표시는 핵심 흐름이 완성된 뒤 선택적으로 SSE를 추가합니다.
분리된 후속 작업:
#74에서 먼저 AI팀과 requiredFieldKeys, requestedFields, 분석 재개 식별자와 최대 왕복 횟수를 확정합니다. #75는 #24와 #74의 저장 상태를 화면에 전달하는 보조 기능이며 #24 완료를 막지 않습니다.
Flyway migration 소유권
한 줄 목표
AI 분석 요청을 Server DB에 남는 비동기 AiRun resource로 생성·조회·재시도하고, HR이 채택한 candidate만 Task로 만들도록 구현합니다.
화면 기준
[PWF] Prototype-WireFrame-3rd의05_Desktop Core Product > CREATE / REVIEW사용자 흐름
POST /api/v1/ai-runs가 요청·Idempotency-Key를 저장하고202 + aiRunId반환AiRuntimeClient호출GET /api/v1/ai-runs/{aiRunId}로 기술 상태와 분석 outcome 조회candidate-decisions로 후보를 채택·수정·폐기소유 API
POST /api/v1/ai-runsGET /api/v1/ai-runs/{aiRunId}POST /api/v1/ai-runs/{aiRunId}/retryPOST /api/v1/ai-runs/{aiRunId}/candidate-decisionsPOST /tasks/analyze,/task-analyses/{id}/confirm,/ai-runs/{id}/confirm은 canonical API가 아닙니다.서로 다른 상태를 구분합니다
QUEUED,RUNNING,RETRYING,SUCCEEDED,FAILEDNEEDS_INFO,REVIEW_REQUIREDDRAFT,NEEDS_INFO,READY_FOR_REVIEW낮은 confidence, ambiguity, missing slot은
FAILED가 아닙니다. Runtime 호출·Schema·deadline처럼 기술적으로 결과를 신뢰할 수 없을 때만 실패합니다.수동 retry는 같은 Run에서
FAILED → RETRYING으로 전이하고 새AiAttempt를 만듭니다. 이전 attempt 기록은 수정·삭제하지 않습니다.Candidate 결정 계약
Idempotency-Keydecisions[{candidateId, action=ACCEPT|DISCARD, edits?}], expectedRunVersionACCEPT: Server가 허용 필드와 업무 규칙을 재검증한 뒤 Task 생성DISCARD: 후보는 삭제하지 않고 폐기 결정을 감사 가능하게 보존저장할 정보
id,companyId, actor, masked input/hash, Idempotency-Key hashstatus,analysisOutcome, attempt/retry count, nextAttemptAt,@VersionrequestId,attemptId,traceId,latencyMs, error code민감 원문, 전체 Prompt, Provider secret은 저장하지 않습니다.
구현 범위
409RUNNINGlease/timeout 복구Retry 소유권
202 + 같은 aiRunId반환RemoteAiRuntimeClient투명 retry 금지; 총 호출 횟수가 곱해지지 않게 함완료 조건
경계 밖
fowoco/aifowoco/knowledge관계
blocked by참조2026-08-03 통신 방식 및 작업 분리 결정
Agent Plan-Act 흐름을 검토한 결과, Server ↔ AI Runtime의 기본 통신은 #8의 REST·JSON 계약을 유지합니다. SSE나 WebSocket을 양방향 Agent 통신의 기준으로 사용하지 않습니다.
202 + aiRunId와GET /api/v1/ai-runs/{aiRunId}polling을 기본으로 사용합니다.requiredFieldKeys를 반환하면 Server가 DB 값을 조회하고, 같은 AiRun 아래 새 AiAttempt를 기록한 뒤requestedFields를 포함해 다시 호출합니다.분리된 후속 작업:
#74에서 먼저 AI팀과
requiredFieldKeys,requestedFields, 분석 재개 식별자와 최대 왕복 횟수를 확정합니다. #75는 #24와 #74의 저장 상태를 화면에 전달하는 보조 기능이며 #24 완료를 막지 않습니다.Flyway migration 소유권
V11__create_worker_link.sql: feat: 근로자 보안 링크(Worker Link) 발급, 안내, 문서제출, 응답제출 구현 #76 Worker Link 소유, 먼저 병합V12__create_ai_run.sql: [AI Run] 비동기 실행 상태·재시도·멱등성 구현 #24 AiRun/AiAttempt/Candidate/Decision 소유main에서 시작합니다.