Skip to content

fix: Plan/Dev/Reviewer subtask file path 일관화 (slug + indexing)#298

Merged
hang-in merged 5 commits into
mainfrom
fix/plan-subtask-file-path-consistency
May 28, 2026
Merged

fix: Plan/Dev/Reviewer subtask file path 일관화 (slug + indexing)#298
hang-in merged 5 commits into
mainfrom
fix/plan-subtask-file-path-consistency

Conversation

@hang-in
Copy link
Copy Markdown
Owner

@hang-in hang-in commented May 28, 2026

Summary

외부 사용자 메신저 보고 회복 — 같은 plan 의 같은 subtask 가 Plan chat 안내 vs Dev chat handoff 에서 다른 파일명 으로 나오던 회귀 수정. Developer 가 Architect 가 작성한 파일을 못 찾고 hallucination 또는 작업 실패하던 문제.

Root cause 2 axis:

  • slug source 분기 — Plan chat 만 slugifyPlanTitle(title) (frontend fallback "plan") 사용. 나머지는 모두 DB plan.slug (backend collision counter 결과 "plan-10").
  • indexing 분기 — Dev chat 만 0-indexed (s.idx raw). Plan chat / loadTaskFileTitles / Reviewer 는 1-indexed.

Plan / 핸드오프

변경 요약

Task 파일 변경
T1 src/components/tunaflow/chat/PlanProposalCard.tsx slugifyPlanTitle(proposal.title)getPlanSlug({ slug: plan.slug, title: proposal.title }). DB slug 우선, fallback 유지
T2 src/lib/workflow/implementWorkflow.ts String(s.idx).padStart(2, "0")String(s.idx + 1).padStart(2, "0"). file path 만 1-indexed (DB idx 자체는 0-indexed 보존 — INV-SPC-3)
T3 src/lib/workflow/implementWorkflow.ts Dev chat prompt 에 **전체 계획서**: \docs/plans/${slug}.md`` 추가. Plan chat 과 대칭
T4 src/lib/workflow/planFilePath.test.ts (신규, 13 tests) Plan chat / Dev chat / loadTaskFileTitles / autoRecoverSubtasks 4 generator cross-consistency 검증

Verification

  • npx tsc --noEmit — PASS (no output)
  • npx vitest run47 files / 491 tests pass (baseline 478 + T4 신규 13)
  • cd src-tauri && cargo check — PASS (변경 0, 회귀 가드용 baseline 확인)

회귀 가드 (grep 결과)

git diff main -- src-tauri/                                                                     → 변경 0
git diff main -- src/lib/workflow/helpers.ts                                                    → 변경 0
git diff main -- src/lib/workflow/reviewWorkflow.ts                                             → 변경 0
git diff main -- src/lib/workflow/planWorkflowService.ts                                        → 변경 0
rg "s\.idx\)\.padStart" src/lib/workflow/                                                       → 0 매치
rg "slugifyPlanTitle" src/components/tunaflow/chat/PlanProposalCard.tsx                         → 0 매치 (직접 호출 제거, getPlanSlug 내부 fallback 만 잔존)

DO NOT 영역 (backend slugify_title / find_unique_slug / DB schema / Architect·Reviewer·Developer prompt template / loadTaskFileTitles·autoRecoverSubtasks·reviewWorkflow path 패턴) 모두 변경 0.

Test Plan

  • vitest cross-generator (Plan chat / Dev chat / loadTaskFileTitles) 일치 검증 — 3 case (한글 title + DB collision slug / 영문 title / slug 미제공)
  • autoRecoverSubtasks 가 0-indexed legacy file + 1-indexed file 양쪽 매칭 (backward compat — INV-SPC-4)
  • 사용자 환경 e2e — release publish 후 메신저 보고자 영역 (Architect)

CI 정책

Frontend 한정 (cross-platform 회귀 위험 0). PR 직후 admin merge.

🤖 Generated with Claude Code

dghong and others added 5 commits May 28, 2026 22:31
…ck divergence) (T1)

createPlan 응답의 `plan.slug` 를 우선 사용해 Plan chat 안내 파일명이
Dev chat / Reviewer / loadTaskFileTitles 와 같은 backend slug (collision
counter 결과 포함) 를 사용하도록 통일. 외부 사용자 보고: 한글 title plan
에서 Plan chat 은 `plan` (frontend fallback), Dev chat 은 `plan-10`
(backend collision) 로 파일명 mismatch 발생.

`getPlanSlug` 가 이미 `plan.slug || slugifyPlanTitle(title)` 패턴이라
DB slug 없을 때만 frontend fallback. 직접 호출 미사용으로
`slugifyPlanTitle` import 제거.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ith Plan chat / loadTaskFileTitles) (T2)

Dev chat handoff 의 file path 만 0-indexed (`plan-10-task-00.md`) 였던 회귀
수정. 다른 generator 3 곳 (PlanProposalCard / loadTaskFileTitles /
reviewWorkflow) 은 모두 1-indexed. DB `idx` 컬럼 자체는 0-indexed 보존
(INV-SPC-3) — file path 변환 시점에서만 `+1`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…off (T3)

Plan chat 의 \`docs/plans/{{slug}}.md — 전체 계획서\` 와 대칭으로 Dev chat
prompt 에도 전체 계획서 path 명시. Developer 가 task 파일만 보는 게 아니라
전체 plan 도 read 할 수 있도록 진입점 제공.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
planFilePath.test.ts — Plan chat (PlanProposalCard 의 path 생성 contract)
/ Dev chat (approveAndStartImplementation) / Reviewer load
(loadTaskFileTitles) / autoRecoverSubtasks 4 generator 가 같은 plan +
subtasks input 에 대해 동일 path 생성하는지 cross-validate.

- getPlanSlug 의 DB slug 우선 + null/undefined fallback + 한글-only title 의
  literal "plan" fallback
- approveAndStartImplementation prompt 안 1-indexed file path + 전체 계획서
  path (T2 + T3)
- loadTaskFileTitles 의 read 요청 path 가 1-indexed + DB slug
- autoRecoverSubtasks regex 가 0-indexed + 1-indexed 양쪽 매칭 (INV-SPC-4)
- 3 case cross-generator: 한글 title + "plan-10", 영문 title + 파생 slug,
  slug 미제공 시 fallback

13 신규 테스트. invoke / planApi / artifactApi mock. React 컴포넌트는
import 시 DOM 의존하므로 path 생성 contract 만 mirror.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T4 의 mock subtasks 가 `"pending"` 을 status 로 썼는데 실제 type 은
`"todo" | "approved" | "in_progress" | "done" | "abandoned"`. tsc 에러
수정.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hang-in hang-in merged commit bdcdbd9 into main May 28, 2026
@hang-in hang-in deleted the fix/plan-subtask-file-path-consistency branch May 28, 2026 13:39
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request ensures cross-generator file path consistency for plans and subtasks by aligning slug resolution and switching to 1-based indexing for subtask file names. It also introduces a comprehensive test suite to prevent regressions. The review feedback points out a potential issue in the tests where an unawaited background promise inside approveAndStartImplementation could cause test flakiness, and suggests mocking the helper function to prevent it.

Comment on lines +44 to +46
import { getPlanSlug, slugifyPlanTitle } from "./helpers";
import { approveAndStartImplementation } from "./implementWorkflow";
import { autoRecoverSubtasks, loadTaskFileTitles } from "./planWorkflowService";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

approveAndStartImplementation 함수 내부에서 createArchitectDecisionArtifactawait 없이 비동기(fire-and-forget)로 호출됩니다. 이로 인해 테스트가 종료된 후에도 백그라운드에서 listSubtaskscreateArtifact 등의 비동기 작업이 계속 실행될 수 있으며, 이는 다른 테스트 케이스의 모의 함수(mock) 상태를 오염시키거나 간헐적인 테스트 실패(flaky test)를 유발할 수 있습니다.

./helpers 모듈을 모의(mock)하여 createArchitectDecisionArtifact를 빈 함수로 대체하면 백그라운드 비동기 작업을 안전하게 차단하고 테스트의 일관성을 높일 수 있습니다.

vi.mock("./helpers", async (importOriginal) => {
  const actual = await importOriginal<typeof import("./helpers")>();
  return {
    ...actual,
    createArchitectDecisionArtifact: vi.fn(async () => undefined),
  };
});

import { getPlanSlug, slugifyPlanTitle } from "./helpers";
import { approveAndStartImplementation } from "./implementWorkflow";
import { autoRecoverSubtasks, loadTaskFileTitles } from "./planWorkflowService";

hang-in pushed a commit that referenced this pull request May 28, 2026
…onsistency hotfix

매니페스트 4 곳 + Cargo.lock bump. CHANGELOG entry 추가.

핵심 fix (PR #298, merge bdcdbd9):
- slug source = DB plan.slug (4 generator 일관)
- file path 1-indexed (loadTaskFileTitles 와 일관)
- Dev chat prompt 에 전체 계획서 path 추가

외부 사용자 메신저 보고 — Plan→Dev handoff 의 subtask 파일명 mismatch 회복.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hang-in added a commit that referenced this pull request May 28, 2026
…handle + test mock (#299)

* fix(openai-compat): vllm base url /v1 suffix normalize (PR #297 review, T1 high)

`VLLM_ENDPOINT=http://host:8000/v1` 형식 (vLLM 공식 docs 안내 패턴) 시
`format!("{}/v1/models", ...)` 가 `http://host:8000/v1/v1/models` 가 되어
discover_vllm() 이 실패하던 회귀.

agent_detect.rs::probe_vllm 의 동일 정책 (`/v1` 끝나면 그대로, 아니면
append) 를 `normalize_vllm_base()` helper 로 추출해 두 호출부가 같은
정책을 공유하게 한다. URL 생성은 `format!("{}/models", base)` 로 통일.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(openai-compat): filter empty api key tokens (3 sites, PR #297 review, T2+T3+T4)

`VLLM_API_KEY=""` 또는 `LMSTUDIO_API_KEY=""` 빈 문자열 시 현재 그대로
헤더에 추가되어 `Authorization: Bearer ` (token 없음) 가 송신되어
일부 vLLM / LM Studio 환경에서 401 을 반환하던 회귀.

3 호출부에 동일한 `.ok().filter(|t| !t.is_empty())` 패턴을 적용:
- discover_vllm — 모델 목록 GET (T2)
- stream_run_with_base — chat completion POST (T3)
- stream_run_no_tools_with_base — fallback POST (T4)

빈 토큰은 헤더에서 완전히 누락 (로컬 비보호 인스턴스 호환). 보호된
인스턴스용 토큰이 있으면 그대로 송신.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(executor): Handle::try_current for vllm participant (PR #297 review, T5)

`tokio::runtime::Handle::current()` 는 non-Tokio context 시 panic.
spawn_blocking 안에서 호출되므로 일반적으로는 runtime handle 이
존재하지만, 호스트 환경 (예: test 또는 직접 호출) 에서 graceful
fallback 이 필요. `try_current()` 후 `Err(_)` 시 명시적 AppError 로
변환 — commands/agents.rs:621 의 동일 패턴과 일관.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(plan-file-path): mock helpers to prevent fire-and-forget flakiness (PR #298 review, T6)

`approveAndStartImplementation` 내부 `createArchitectDecisionArtifact(plan)`
는 fire-and-forget 으로 호출되어 test 안에서 비결정적 mock 호출
순서를 만들 수 있음.

`vi.mock("./helpers", async (importOriginal) => ...)` 패턴으로
`createArchitectDecisionArtifact` 만 `vi.fn(async () => undefined)`
로 대체. 나머지 helper (getPlanSlug / slugifyPlanTitle /
buildPlanContext / createAndLinkBranch …) 는 actual 구현 그대로
사용해 cross-generator 일관성 검증 의미를 보존.

13 test 모두 통과 (Test Files 1 passed, Tests 13 passed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: dghong <d9ng@outlook.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant