okstra 0.101.0 → 0.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/kr/architecture.md +1 -1
- package/docs/project-structure-overview.md +1 -1
- package/docs/superpowers/plans/2026-06-24-cross-project-precondition.md +542 -0
- package/docs/superpowers/plans/2026-06-24-decision-drafts-render-gap.md +568 -0
- package/docs/superpowers/plans/2026-06-24-manual-user-test-section.md +203 -0
- package/docs/superpowers/specs/2026-06-24-cross-project-precondition-design.md +129 -0
- package/docs/superpowers/specs/2026-06-24-decision-drafts-render-gap-design.md +185 -0
- package/docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md +121 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +1 -0
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -0
- package/runtime/prompts/profiles/final-verification.md +4 -3
- package/runtime/prompts/profiles/implementation-planning.md +12 -9
- package/runtime/python/okstra_ctl/report_views.py +39 -3
- package/runtime/python/okstra_ctl/run.py +0 -1
- package/runtime/schemas/final-report-v1.0.schema.json +106 -3
- package/runtime/skills/okstra-container/SKILL.md +1 -1
- package/runtime/templates/reports/final-report.template.md +63 -2
- package/runtime/templates/reports/final-verification-input.template.md +3 -3
- package/runtime/templates/reports/i18n/en.json +28 -1
- package/runtime/templates/reports/i18n/ko.json +28 -1
- package/runtime/templates/reports/report.css +10 -0
- package/runtime/templates/reports/report.js +16 -0
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-run.py +70 -1
package/docs/kr/architecture.md
CHANGED
|
@@ -347,7 +347,7 @@ okstra phase 는 PRD / issue file 을 직접 쓰지 않습니다. 동등한 결
|
|
|
347
347
|
|
|
348
348
|
- `implementation`을 제외한 모든 phase는 source code edit, build, migration, deployment, 그 밖의 state-mutating 명령을 금지합니다(`final-verification`은 read-only 테스트 명령만 허용). `implementation`은 승인된 plan의 파일 목록 안에서만 edit/commit이 허용되며, `git push`·publish·deploy·실제 migration·third-party write API는 여전히 금지됩니다.
|
|
349
349
|
- **모든 task-type 격리 worktree (BLOCKING)**: 모든 task-type 의 첫 번째 phase prepare 단계에서 `okstra-ctl` 이 자동으로 task-key 단위 `git worktree` 를 생성하고, 같은 task-key 의 이후 phase (`requirements-discovery` → `error-analysis` → `implementation-planning` → `implementation`) 는 동일한 worktree·브랜치를 재사용합니다. 위치는 `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/` (segment 의 `/`·`:` 등 특수문자는 `-` 로 정규화) 이고, 브랜치 이름은 `<work-category-prefix>-<task-id-segment>` (예: `feat-dev-9436`, `fix-dev-7311`) 입니다. base ref 는 첫 phase prepare 시점의 main worktree `HEAD`. `~/.okstra/worktrees/registry.json` (flock-guarded) 가 task-key → path/branch 매핑을 전역 관리해 동시 실행 시 path·branch 충돌을 방지합니다. configured sync dirs 는 main worktree 에서 symlink 로 연결되어 task checkout 사이의 filesystem continuity 를 제공합니다 (sync 대상 목록은 `project.json` 의 `worktreeSyncDirs` 또는 `OKSTRA_WORKTREE_SYNC_DIRS` 환경변수로 override 가능; 빈 배열이면 sync 비활성화). 이 sync 는 okstra context/write boundary 를 확장하지 않습니다. caller 가 이미 다른 worktree 안에 있거나 project_root 가 git repo 가 아니면 provisioning 은 skip 되고 executor 는 project_root 에서 그대로 작업합니다. worktree 는 run 종료 후 자동 삭제되지 않으며 후속 phase·PR 작성·rollback 검증의 권위 artefact 입니다. 수동 cleanup: `git -C <main-worktree> worktree remove <path>` → `git -C <main-worktree> branch -D <branch>` + registry 항목 삭제. 자세한 동작은 `prompts/profiles/implementation.md` 의 *Task worktree* 블록과 `prompts/lead/okstra-lead-contract.md` 의 *Task worktree (BLOCKING for every task-type)* 섹션 참고.
|
|
350
|
-
- **implementation stage 격리 worktree (동시 병렬)**: 위 task-key 단위 worktree 는 `requirements-discovery`~`implementation-planning` 의 모델입니다. `implementation` task 는 **stage 격리** 로 동작합니다 (spec `docs/superpowers/specs/2026-06-06-stage-worktree-isolation-design.md`) — **한 run = 한 stage**, 각 run 이 `.../<task-id-segment>/stage-<N>/` (브랜치 `<prefix>-<task-id-segment>-s<N>`) 격리 worktree 를 발급받습니다. registry 가 task-key 와 **stage-key** (`<task-key>#stage-<N>`) 를 함께 flock 예약하고, `_resolve_effective_stages` 가 `consumers.jsonl` 의 `started` + registry 예약 stage 를 ready 집합에서 제외하므로(점유 SSOT = registry), 사용자가 두 `implementation` run 을 동시에 띄우면 서로 다른 독립 stage 를 충돌 없이 진행합니다. base 결정: 독립 = 공통 anchor(첫 stage 진입 HEAD 고정), 단일 의존 = 선행 done commit, 다중 의존 = 선행이 모두 ancestor 인 task worktree HEAD(`git merge-base --is-ancestor`; 미머지 시 `PrepareError`). cost-aware-design 의 ready-set batch 는 stage 마다 격리 branch 가 필요해 의미를 잃으므로(같은 branch 에 두 stage-key reserve 시 branch-uniqueness 충돌) 폐기되었고, 순차 진행은 stage done 후 다음 run, 동시 진행은 별도 run 으로 — cost 등가. `--stage <auto|N>` 또는 wizard `stage_pick` 으로 stage 를 선택합니다. worktree 뿐 아니라 **run 산출물(report·state·worker-results·manifest)도 `runs/implementation/stage-<N>/` 로 stage 별 격리**되므로 동시 실행하는 두 stage 의 보고서·상태가 섞이지 않습니다. 반면 `consumers.jsonl` 과 worktree registry 는 stage 간 공유되는 조율 SSOT 라 task-type 루트(`runs/implementation/`)에 그대로 둡니다.
|
|
350
|
+
- **implementation stage 격리 worktree (동시 병렬)**: 위 task-key 단위 worktree 는 `requirements-discovery`~`implementation-planning` 의 모델입니다. `implementation` task 는 **stage 격리** 로 동작합니다 (spec `docs/superpowers/specs/2026-06-06-stage-worktree-isolation-design.md`) — **한 run = 한 stage**, 각 run 이 `.../<task-id-segment>/stage-<N>/` (브랜치 `<prefix>-<task-id-segment>-s<N>`) 격리 worktree 를 발급받습니다. registry 가 task-key 와 **stage-key** (`<task-key>#stage-<N>`) 를 함께 flock 예약하고, `_resolve_effective_stages` 가 `consumers.jsonl` 의 `started` + registry 예약 stage 를 ready 집합에서 제외하므로(점유 SSOT = registry), 사용자가 두 `implementation` run 을 동시에 띄우면 서로 다른 독립 stage 를 충돌 없이 진행합니다. base 결정: 독립 = 공통 anchor(첫 stage 진입 HEAD 고정), 단일 의존 = 선행 done commit, 다중 의존 = 선행이 모두 ancestor 인 task worktree HEAD(`git merge-base --is-ancestor`; 미머지 시 `PrepareError`). cost-aware-design 의 ready-set batch 는 stage 마다 격리 branch 가 필요해 의미를 잃으므로(같은 branch 에 두 stage-key reserve 시 branch-uniqueness 충돌) 폐기되었고, 순차 진행은 stage done 후 다음 run, 동시 진행은 별도 run 으로 — cost 등가. `--stage <auto|N>` 또는 wizard `stage_pick` 으로 stage 를 선택합니다. wizard `stage_pick` 은 각 stage 의 상태(`[완료]`/`[진행중]`/`[준비됨]`/`[대기]`)를 라벨에 표시하는 멀티선택이며, 선택분의 의존성 closure 를 Kahn 위상정렬(`stage_targets.order_stage_closure`)해 render-args 의 `chain-stages` CSV 로 내보냅니다. `okstra-run` SKILL 이 이 큐를 받아 의존성 순서대로 단일-stage run 을 N 회 순차 실행(무인 연쇄)하되, 각 stage 의 Phase 6 `done` 행을 확인한 뒤 다음으로 넘어갑니다. 이는 오케스트레이션 계층일 뿐이며 wizard·prepare 의 **한 run = 한 stage** 격리 불변은 그대로입니다. worktree 뿐 아니라 **run 산출물(report·state·worker-results·manifest)도 `runs/implementation/stage-<N>/` 로 stage 별 격리**되므로 동시 실행하는 두 stage 의 보고서·상태가 섞이지 않습니다. 반면 `consumers.jsonl` 과 worktree registry 는 stage 간 공유되는 조율 SSOT 라 task-type 루트(`runs/implementation/`)에 그대로 둡니다.
|
|
351
351
|
- **단일-stage final-verification 의 run 산출물 격리 (동시 병렬)**: 단독-stage `final-verification`(`--stage <N>`)도 implementation 과 동일하게 run 산출물을 `runs/final-verification/stage-<N>/` 하위에 격리하고(seq 도 stage 별 독립), 팀 이름에 `-fv-s<N>` 접미사를 붙입니다 — `-fv-` 구분자로 같은 stage 의 implementation 팀(`-s<N>`)과도, 전체-task 검증의 기본 이름과도 충돌하지 않습니다. 따라서 여러 stage 의 final-verification 을 동시에 띄워도 state·worker-results·보고서·팀이 섞이지 않습니다. worktree 는 새로 만들지 않고 해당 implementation stage worktree 를 registry 에서 read-only 로 재사용하며, 그래서 registry stage-key 예약도 하지 않습니다. `teamName` 라벨에 `-fv-s<N>` 접미사를 붙이는 것은 audit/표시용 구분일 뿐이며, 실제 팀은 세션별 implicit team(`session-<leadSid>`)이라 v2.1.178 이전의 `TeamCreate` 이름 충돌 hard-fail 은 더 이상 발생하지 않습니다. 전체-task 검증(stage 빈 값)은 기존 평면 `runs/final-verification/` 구조를 유지합니다.
|
|
352
352
|
- `implementation` 과 `release-handoff` 를 제외한 모든 phase 는 source code edit, build, migration, deployment, 그 밖의 state-mutating 명령을 금지합니다 (`final-verification` 은 read-only 테스트 명령만 허용). `implementation` 은 승인된 plan 의 파일 목록 안에서만 edit/commit 이 허용되며, `git push`·publish·deploy·실제 migration·third-party write API 는 여전히 금지됩니다. `release-handoff` 는 source code 자체는 수정하지 않고, 사용자가 메뉴로 선택한 commit / push / PR 명령만 실행합니다 (force push, base 브랜치 직접 push, hook bypass, release publish 는 여전히 금지).
|
|
353
353
|
- 사용자가 "다음 단계 진행해" 같은 표현을 보내도, 그 발화만으로 다음 phase가 자동 시작되지 않습니다. 다음 phase는 새 `okstra.sh` 실행으로만 시작합니다.
|
|
@@ -194,7 +194,7 @@ Important modules:
|
|
|
194
194
|
|---|---|
|
|
195
195
|
| `run.py` | `prepare_task_bundle()` single authority and CLI parser |
|
|
196
196
|
| `implementation_stage.py` | `implementation` 단일-stage run 오케스트레이션 — consumer 상태 복구 → 가용 Stage Map entry 선택 → 격리 stage worktree provision → 선택 stage 를 run context 로 발행 (`run.py` 에서 추출) |
|
|
197
|
-
| `stage_targets.py` | Stage readiness/verification 정책 SSOT — 어떤 stage 가 실행 가능한지, 어느 commit 에서 분기하는지, final-verification 이 무엇을 검사하는지 결정 (`consumers.jsonl` 행과 git ancestry 검사를 단일 인터페이스 뒤로 캡슐화) |
|
|
197
|
+
| `stage_targets.py` | Stage readiness/verification 정책 SSOT — 어떤 stage 가 실행 가능한지, 어느 commit 에서 분기하는지, final-verification 이 무엇을 검사하는지 결정 (`consumers.jsonl` 행과 git ancestry 검사를 단일 인터페이스 뒤로 캡슐화). `order_stage_closure` 는 wizard 멀티선택 stage 집합의 의존성 closure 를 Kahn 위상정렬해 `chain-stages` 무인 연쇄 순서를 산출 |
|
|
198
198
|
| `stage_reconcile.py` | stage prepare 흐름 공용 best-effort git reconciliation (`git_reconcile.auto_reconcile` 위임; advisory — 실패는 stderr 보고만, dependency gate 가 권위 판정 유지) |
|
|
199
199
|
| `run_context.py` | Per-task mutex, run context and run-input persistence; `consumers_mutex` helper for atomic `consumers.jsonl` writes |
|
|
200
200
|
| `consumers.py` | Append-only `consumers.jsonl` writer + reader — records which `implementation` runs consumed which `implementation-planning` stage |
|
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
# Cross-Project Dependencies Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** implementation-planning 보고서에서 타 프로젝트(다른 repo·published 패키지) 의존을 1급·렌더되는 구조 필드 `crossProjectDependencies` 로 승격하고, soft "Recommended Next Step" 이 아니라 구체 작업이 명시된 "선행 필수"로 검증기에서 강제한다.
|
|
6
|
+
|
|
7
|
+
**Architecture:** data.json(SSOT) → Jinja 템플릿 결정론적 렌더 구조를 그대로 따른다. 스키마에 `$defs/CrossProjectDependency` 와 `implementationPlanning.crossProjectDependencies` 배열을 추가(양방향 `direction` 통합), 템플릿 §5.4 에 엔트리별 블록 렌더, `validate-run.py` 에 DM `kind: cross-project` ↔ XP `upstream-precondition` 교차 필드 게이트를 둔다. 기존 미렌더 `## Cross-Repo Carry` 부록은 `downstream-carry` 방향으로 흡수돼 실제 렌더된다.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** JSON Schema (Draft) + Jinja2 템플릿 + Python 검증기/테스트(pytest). 빌드는 `npm run build`(tools/build.mjs → runtime/ 동기화), 통합 게이트는 `npm run check`.
|
|
10
|
+
|
|
11
|
+
## Global Constraints
|
|
12
|
+
|
|
13
|
+
- `runtime/` 은 빌드 출력 — 직접 수정 금지. 소스(`schemas/`, `templates/`, `prompts/`, `validators/`, `agents/`)만 수정 후 `npm run build`.
|
|
14
|
+
- pre-1.0 — backwards-compat shim 금지. enum/스키마는 자유롭게 확장한다.
|
|
15
|
+
- 사용자 대면 문서·prose 는 한국어, 코드 식별자/경로/CLI 인자는 영어.
|
|
16
|
+
- 완료 선언 전 `npm run check` 가 그대로 exit 0 (test:js 실패 시 test:py 미실행이므로 통합 명령으로 확인).
|
|
17
|
+
- 검증기 동작 변경 = seed/소스 파일에 반영(개인 `.claude/` 아님).
|
|
18
|
+
- 새 식별자(`crossProjectDependencies`, `CrossProjectDependency`, `XP-`, `cross-project`, 헤딩 `Cross-Project Dependencies`, i18n 키) 추가 후 전 사용처를 grep 으로 일관 확인.
|
|
19
|
+
|
|
20
|
+
**스펙:** [docs/superpowers/specs/2026-06-24-cross-project-precondition-design.md](../specs/2026-06-24-cross-project-precondition-design.md)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
### Task 1: 스키마 — `CrossProjectDependency` def + 필드 + DM enum + 픽스처
|
|
25
|
+
|
|
26
|
+
**Files:**
|
|
27
|
+
- Modify: `schemas/final-report-v1.0.schema.json` (implementationPlanning.required @ 353-364, properties @ 366-414, DependencyRow enum @ 1369)
|
|
28
|
+
- Modify: `tests/fixtures/final-report-data/implementation-planning-001.data.json` (implementationPlanning 블록)
|
|
29
|
+
- Create: `tests/contract/test_cross_project_precondition.py`
|
|
30
|
+
|
|
31
|
+
**Interfaces:**
|
|
32
|
+
- Produces: `$defs/CrossProjectDependency` (id `^XP-\d{3,}$`, direction enum `upstream-precondition|downstream-carry`, project/requiredWork/verificationSignal/linkedWork/howToStart 모두 string minLength 1); `implementationPlanning.crossProjectDependencies: array<CrossProjectDependency>` (required, minItems 없음 → `[]` 허용); DependencyRow.kind 에 `cross-project` 추가.
|
|
33
|
+
- Consumes: `okstra_ctl.final_report_schema.load_schema()` / `schema_validate(data, schema)` (스크립트가 PYTHONPATH 에 있음 — conftest).
|
|
34
|
+
|
|
35
|
+
- [ ] **Step 1: 실패 테스트 작성** — 스키마가 새 필드/enum 을 수용·거부하는지
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
# tests/contract/test_cross_project_precondition.py
|
|
39
|
+
"""crossProjectDependencies 스키마 모양 + validate-run 교차 필드 게이트 단위 테스트.
|
|
40
|
+
|
|
41
|
+
스키마는 각 XP 행의 7필드 비-빈을 보장하고(모양), validate-run 은
|
|
42
|
+
DM `kind: cross-project` 가 있으면 XP `upstream-precondition` 행이 존재하는지(존재)를 본다.
|
|
43
|
+
"""
|
|
44
|
+
from __future__ import annotations
|
|
45
|
+
|
|
46
|
+
import copy
|
|
47
|
+
import importlib.util
|
|
48
|
+
import json
|
|
49
|
+
|
|
50
|
+
from _paths import REPO_ROOT
|
|
51
|
+
|
|
52
|
+
import pytest
|
|
53
|
+
|
|
54
|
+
from okstra_ctl.final_report_schema import load_schema, schema_validate
|
|
55
|
+
|
|
56
|
+
FIXTURE = (
|
|
57
|
+
REPO_ROOT / "tests" / "fixtures" / "final-report-data"
|
|
58
|
+
/ "implementation-planning-001.data.json"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.fixture(scope="module")
|
|
63
|
+
def schema():
|
|
64
|
+
return load_schema()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest.fixture()
|
|
68
|
+
def data():
|
|
69
|
+
return json.loads(FIXTURE.read_text(encoding="utf-8"))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_fixture_with_cross_project_rows_validates(schema, data):
|
|
73
|
+
assert schema_validate(data, schema) == []
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_empty_required_work_is_rejected(schema, data):
|
|
77
|
+
bad = copy.deepcopy(data)
|
|
78
|
+
bad["implementationPlanning"]["crossProjectDependencies"][0]["requiredWork"] = ""
|
|
79
|
+
assert schema_validate(bad, schema) != []
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_unknown_direction_is_rejected(schema, data):
|
|
83
|
+
bad = copy.deepcopy(data)
|
|
84
|
+
bad["implementationPlanning"]["crossProjectDependencies"][0]["direction"] = "sideways"
|
|
85
|
+
assert schema_validate(bad, schema) != []
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_dm_cross_project_kind_is_accepted(schema, data):
|
|
89
|
+
# 픽스처는 이미 kind='cross-project' DM 행을 포함한다(Task 1 Step 4).
|
|
90
|
+
kinds = [r["kind"] for r in data["implementationPlanning"]["dependencyMigrationRisk"]]
|
|
91
|
+
assert "cross-project" in kinds
|
|
92
|
+
assert schema_validate(data, schema) == []
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- [ ] **Step 2: 테스트 실패 확인**
|
|
96
|
+
|
|
97
|
+
Run: `python3 -m pytest tests/contract/test_cross_project_precondition.py -v`
|
|
98
|
+
Expected: FAIL — 픽스처에 `crossProjectDependencies` 키가 없고 스키마에 def 가 없어 `KeyError`/검증 통과 실패.
|
|
99
|
+
|
|
100
|
+
- [ ] **Step 3: 스키마에 def + 필드 + enum 추가**
|
|
101
|
+
|
|
102
|
+
`schemas/final-report-v1.0.schema.json`:
|
|
103
|
+
|
|
104
|
+
(a) DependencyRow enum (line 1369) 에 `cross-project` 추가:
|
|
105
|
+
```json
|
|
106
|
+
"enum": ["order", "backfill", "flag-precondition", "repo-sequencing", "cross-project", "other"]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
(b) `implementationPlanning.required` (line 353-364) 에 `crossProjectDependencies` 추가(`planBodyVerification` 뒤):
|
|
110
|
+
```json
|
|
111
|
+
"requirementCoverage",
|
|
112
|
+
"planBodyVerification",
|
|
113
|
+
"crossProjectDependencies"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
(c) `implementationPlanning.properties` 의 `planBodyVerification` 항목(line 413) 뒤에 추가:
|
|
117
|
+
```json
|
|
118
|
+
"planBodyVerification": { "$ref": "#/$defs/PlanBodyVerification" },
|
|
119
|
+
"crossProjectDependencies": {
|
|
120
|
+
"type": "array",
|
|
121
|
+
"items": { "$ref": "#/$defs/CrossProjectDependency" }
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
(d) `$defs` 에 `DependencyRow` (line 1375 `},` 뒤) 신설:
|
|
126
|
+
```json
|
|
127
|
+
"CrossProjectDependency": {
|
|
128
|
+
"type": "object",
|
|
129
|
+
"required": ["id", "direction", "project", "requiredWork", "verificationSignal", "linkedWork", "howToStart"],
|
|
130
|
+
"additionalProperties": false,
|
|
131
|
+
"properties": {
|
|
132
|
+
"id": { "type": "string", "pattern": "^XP-\\d{3,}$" },
|
|
133
|
+
"direction": { "enum": ["upstream-precondition", "downstream-carry"] },
|
|
134
|
+
"project": { "type": "string", "minLength": 1 },
|
|
135
|
+
"requiredWork": { "type": "string", "minLength": 1 },
|
|
136
|
+
"verificationSignal": { "type": "string", "minLength": 1 },
|
|
137
|
+
"linkedWork": { "type": "string", "minLength": 1 },
|
|
138
|
+
"howToStart": { "type": "string", "minLength": 1 }
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- [ ] **Step 4: 픽스처에 샘플 행 추가**
|
|
144
|
+
|
|
145
|
+
`tests/fixtures/final-report-data/implementation-planning-001.data.json` 의 `implementationPlanning` 블록에서:
|
|
146
|
+
|
|
147
|
+
(a) `dependencyMigrationRisk` 배열에 `cross-project` DM 행 1개 추가:
|
|
148
|
+
```json
|
|
149
|
+
{ "id": "DM-002", "kind": "cross-project", "item": "common-api 가 isSlanted 컬럼을 publish 해야 jobs 가 컴파일된다.", "impact": "미publish 면 yarn install/compile 단계에서 resolution 실패.", "mitigation": "common-api 를 별도 okstra run 으로 먼저 진행 — XP-001 참조." }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
(b) `implementationPlanning` 객체에 `crossProjectDependencies` 키 추가(upstream 1 + downstream 1):
|
|
153
|
+
```json
|
|
154
|
+
"crossProjectDependencies": [
|
|
155
|
+
{
|
|
156
|
+
"id": "XP-001",
|
|
157
|
+
"direction": "upstream-precondition",
|
|
158
|
+
"project": "@fonts-ninja-org/common-api",
|
|
159
|
+
"requiredWork": "Font.d.ts 에 isSlanted 컬럼 추가 후 npm publish.",
|
|
160
|
+
"verificationSignal": "npm view @fonts-ninja-org/common-api version 이 ≥ 1.27.46 이고 Font.d.ts 에 isSlanted 가 존재.",
|
|
161
|
+
"linkedWork": "Stage 2 (yarn install) / Stage 3 (compile) — 신호 충족 전 진행 불가.",
|
|
162
|
+
"howToStart": "common-api repo 에서 okstra-brief (이 보고서 절대경로 Source Material) → okstra-run 으로 planning→implementation."
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"id": "XP-002",
|
|
166
|
+
"direction": "downstream-carry",
|
|
167
|
+
"project": "@fonts-ninja-org/helpers",
|
|
168
|
+
"requiredWork": "이 run 이 노출한 classifier 결과를 helpers 가 소비하도록 어댑터 추가.",
|
|
169
|
+
"verificationSignal": "이 run 의 PR 머지 + classifier 엔드포인트 live.",
|
|
170
|
+
"linkedWork": "helpers 의 후속 okstra run 이 이 run 산출물을 외부 precondition 으로 소비.",
|
|
171
|
+
"howToStart": "helpers repo 에서 okstra-brief (이 보고서 절대경로 Source Material) → okstra-run."
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- [ ] **Step 5: 테스트 통과 확인**
|
|
177
|
+
|
|
178
|
+
Run: `python3 -m pytest tests/contract/test_cross_project_precondition.py -v`
|
|
179
|
+
Expected: PASS (4 passed — Task 3 의 validate-run 테스트는 아직 이 파일에 없음).
|
|
180
|
+
|
|
181
|
+
- [ ] **Step 6: 커밋**
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
git add schemas/final-report-v1.0.schema.json tests/fixtures/final-report-data/implementation-planning-001.data.json tests/contract/test_cross_project_precondition.py
|
|
185
|
+
git commit -m "feat(report-schema): add crossProjectDependencies field and cross-project DM kind"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### Task 2: 템플릿 + i18n — `### Cross-Project Dependencies` 렌더
|
|
191
|
+
|
|
192
|
+
**Files:**
|
|
193
|
+
- Modify: `templates/reports/final-report.template.md` (§5.4, Dependency/Migration Risk endif @ 218 직후)
|
|
194
|
+
- Modify: `templates/reports/i18n/ko.json`, `templates/reports/i18n/en.json`
|
|
195
|
+
- Modify: `tests/report/test_render_final_report.py` (`_PLANNING_REQUIRED` @ 43, planning 렌더 테스트 @ 95-123)
|
|
196
|
+
|
|
197
|
+
**Interfaces:**
|
|
198
|
+
- Consumes: `implementationPlanning.crossProjectDependencies` (Task 1), i18n 키 `sectionAside.crossProjectDependencies` / `emptyState.crossProjectDependencies` / `implementationPlanning.crossProjectFields.*` / `implementationPlanning.crossProjectRecognitionNote`.
|
|
199
|
+
- Produces: 렌더 마크다운에 `### Cross-Project Dependencies` 헤딩 + 엔트리별 블록.
|
|
200
|
+
|
|
201
|
+
- [ ] **Step 1: 실패 테스트 작성** — 렌더 결과에 헤딩·방향 태그·라벨·XP ID 가 있는지
|
|
202
|
+
|
|
203
|
+
`tests/report/test_render_final_report.py` 의 `_PLANNING_REQUIRED` 튜플(line 43)에 추가:
|
|
204
|
+
```python
|
|
205
|
+
"Plan Body Verification",
|
|
206
|
+
"Gate result",
|
|
207
|
+
"Cross-Project Dependencies",
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
그리고 새 테스트 함수 추가(파일 끝, 기존 import `_render_fixture`/`render`/`find_default_template`/`_load` 사용):
|
|
211
|
+
```python
|
|
212
|
+
def test_cross_project_dependencies_render():
|
|
213
|
+
rendered = _render_fixture("implementation-planning-001.data.json")
|
|
214
|
+
assert "### Cross-Project Dependencies" in rendered
|
|
215
|
+
assert "XP-001" in rendered
|
|
216
|
+
assert "[upstream-precondition]" in rendered
|
|
217
|
+
assert "[downstream-carry]" in rendered
|
|
218
|
+
assert "@fonts-ninja-org/common-api" in rendered
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_cross_project_dependencies_empty_state():
|
|
222
|
+
data = _load("implementation-planning-001.data.json")
|
|
223
|
+
data["implementationPlanning"]["crossProjectDependencies"] = []
|
|
224
|
+
rendered = render(data, template_path=find_default_template())
|
|
225
|
+
assert "### Cross-Project Dependencies" in rendered
|
|
226
|
+
# 빈 배열이면 엔트리 블록은 없고 empty-state 한 줄만.
|
|
227
|
+
assert "XP-001" not in rendered
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
- [ ] **Step 2: 테스트 실패 확인**
|
|
231
|
+
|
|
232
|
+
Run: `python3 -m pytest tests/report/test_render_final_report.py -k cross_project -v`
|
|
233
|
+
Expected: FAIL — 템플릿에 섹션이 없어 헤딩 미존재.
|
|
234
|
+
|
|
235
|
+
- [ ] **Step 3: i18n 키 추가 (ko.json)**
|
|
236
|
+
|
|
237
|
+
`templates/reports/i18n/ko.json`:
|
|
238
|
+
- `sectionAside` 객체에: `"crossProjectDependencies": "프로젝트 간 의존성"`
|
|
239
|
+
- `emptyState` 객체에: `"crossProjectDependencies": "- 다른 프로젝트에 대한 선행/후속 의존 없음."`
|
|
240
|
+
- `implementationPlanning` 객체에:
|
|
241
|
+
```json
|
|
242
|
+
"crossProjectFields": {
|
|
243
|
+
"requiredWork": "필요 작업",
|
|
244
|
+
"verificationSignal": "검증 신호",
|
|
245
|
+
"linkedWork": "연결된 작업",
|
|
246
|
+
"howToStart": "시작 방법"
|
|
247
|
+
},
|
|
248
|
+
"crossProjectRecognitionNote": "참고: 상대 프로젝트의 run 은 구조적으로 독립이다 — done-set·stage 번호·depends-on 이 모두 자기 repo 기준이며, 이 run 의 완료 stage 가 자동으로 done 으로 인식되지 않는다. 이 섹션은 상대 run 의 planning 을 seed 하는 narrative 입력일 뿐이다."
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
- [ ] **Step 4: i18n 키 추가 (en.json)**
|
|
252
|
+
|
|
253
|
+
`templates/reports/i18n/en.json`:
|
|
254
|
+
- `sectionAside` 객체에: `"crossProjectDependencies": "Cross-Project Dependencies"` (영문 헤딩 == aside → aside 생략, dependencyRisk 패턴과 동일)
|
|
255
|
+
- `emptyState` 객체에: `"crossProjectDependencies": "- No upstream/downstream dependency on another project."`
|
|
256
|
+
- `implementationPlanning` 객체에:
|
|
257
|
+
```json
|
|
258
|
+
"crossProjectFields": {
|
|
259
|
+
"requiredWork": "Required work",
|
|
260
|
+
"verificationSignal": "Verification signal",
|
|
261
|
+
"linkedWork": "Linked work",
|
|
262
|
+
"howToStart": "How to start"
|
|
263
|
+
},
|
|
264
|
+
"crossProjectRecognitionNote": "Note: the other project's run is structurally independent — its done-set, stage numbers, and depends-on all resolve against its own repo, and this run's completed stages are NOT auto-recognized as done there. This section is narrative input that seeds the other run's planning only."
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
- [ ] **Step 5: 템플릿 블록 추가**
|
|
268
|
+
|
|
269
|
+
`templates/reports/final-report.template.md` 의 `### Dependency / Migration Risk` 블록 `{%- endif %}` (line 218) 직후, `### Validation Checklist` (line 220) **앞**에 삽입:
|
|
270
|
+
```jinja
|
|
271
|
+
### Cross-Project Dependencies{% if t("sectionAside.crossProjectDependencies") != "Cross-Project Dependencies" %} ({{ t("sectionAside.crossProjectDependencies") }}){% endif %}
|
|
272
|
+
|
|
273
|
+
{% if implementationPlanning.crossProjectDependencies | length == 0 -%}
|
|
274
|
+
{{ t("emptyState.crossProjectDependencies") }}
|
|
275
|
+
{%- else %}
|
|
276
|
+
{% for xp in implementationPlanning.crossProjectDependencies %}
|
|
277
|
+
**{{ xp.id }} — [{{ xp.direction }}] {{ xp.project }}**
|
|
278
|
+
|
|
279
|
+
- {{ t("implementationPlanning.crossProjectFields.requiredWork") }}: {{ xp.requiredWork }}
|
|
280
|
+
- {{ t("implementationPlanning.crossProjectFields.verificationSignal") }}: {{ xp.verificationSignal }}
|
|
281
|
+
- {{ t("implementationPlanning.crossProjectFields.linkedWork") }}: {{ xp.linkedWork }}
|
|
282
|
+
- {{ t("implementationPlanning.crossProjectFields.howToStart") }}: {{ xp.howToStart }}
|
|
283
|
+
{% endfor %}
|
|
284
|
+
{{ t("implementationPlanning.crossProjectRecognitionNote") }}
|
|
285
|
+
{%- endif %}
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
- [ ] **Step 6: 테스트 통과 확인**
|
|
290
|
+
|
|
291
|
+
Run: `python3 -m pytest tests/report/test_render_final_report.py -v`
|
|
292
|
+
Expected: PASS (cross_project 2개 + 기존 planning substring 테스트 포함 전부).
|
|
293
|
+
|
|
294
|
+
- [ ] **Step 7: 커밋**
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
git add templates/reports/final-report.template.md templates/reports/i18n/ko.json templates/reports/i18n/en.json tests/report/test_render_final_report.py
|
|
298
|
+
git commit -m "feat(report): render Cross-Project Dependencies section in implementation-planning"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
### Task 3: 검증기 — DM `cross-project` ↔ XP 교차 필드 게이트 + 헤딩 substring
|
|
304
|
+
|
|
305
|
+
**Files:**
|
|
306
|
+
- Modify: `validators/validate-run.py` (`PLANNING_REQUIRED_SECTIONS` @ 1132-1143; taskType 분기 @ 1488; 새 함수 `_validate_final_verification_consistency` @ 1530 인근에 형제 함수)
|
|
307
|
+
- Modify: `tests/contract/test_cross_project_precondition.py` (Task 1 파일에 validate-run 테스트 추가)
|
|
308
|
+
|
|
309
|
+
**Interfaces:**
|
|
310
|
+
- Consumes: `data["implementationPlanning"]["dependencyMigrationRisk"]` / `["crossProjectDependencies"]` (Task 1 모양).
|
|
311
|
+
- Produces: `vr._validate_implementation_planning_cross_project(data, failures)` — DM `kind=='cross-project'` 가 있으면 XP `direction=='upstream-precondition'` 행 ≥ 1 을 강제, 아니면 `failures` 에 메시지 append.
|
|
312
|
+
|
|
313
|
+
- [ ] **Step 1: 실패 테스트 작성** — Task 1 의 `test_cross_project_precondition.py` 끝에 추가
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
VALIDATOR_PATH = REPO_ROOT / "validators" / "validate-run.py"
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _load_validator():
|
|
320
|
+
spec = importlib.util.spec_from_file_location("validate_run", VALIDATOR_PATH)
|
|
321
|
+
module = importlib.util.module_from_spec(spec)
|
|
322
|
+
assert spec.loader is not None
|
|
323
|
+
spec.loader.exec_module(module)
|
|
324
|
+
return module
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@pytest.fixture(scope="module")
|
|
328
|
+
def vr():
|
|
329
|
+
return _load_validator()
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _planning_data(dm_rows, xp_rows):
|
|
333
|
+
return {
|
|
334
|
+
"header": {"taskType": "implementation-planning"},
|
|
335
|
+
"implementationPlanning": {
|
|
336
|
+
"dependencyMigrationRisk": dm_rows,
|
|
337
|
+
"crossProjectDependencies": xp_rows,
|
|
338
|
+
},
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def test_cross_project_dm_without_upstream_xp_fails(vr):
|
|
343
|
+
failures: list[str] = []
|
|
344
|
+
data = _planning_data(
|
|
345
|
+
[{"id": "DM-001", "kind": "cross-project", "item": "x", "impact": "y", "mitigation": "z"}],
|
|
346
|
+
[],
|
|
347
|
+
)
|
|
348
|
+
vr._validate_implementation_planning_cross_project(data, failures)
|
|
349
|
+
assert failures != []
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def test_cross_project_dm_with_upstream_xp_passes(vr):
|
|
353
|
+
failures: list[str] = []
|
|
354
|
+
data = _planning_data(
|
|
355
|
+
[{"id": "DM-001", "kind": "cross-project", "item": "x", "impact": "y", "mitigation": "z"}],
|
|
356
|
+
[{"id": "XP-001", "direction": "upstream-precondition", "project": "p",
|
|
357
|
+
"requiredWork": "w", "verificationSignal": "s", "linkedWork": "l", "howToStart": "h"}],
|
|
358
|
+
)
|
|
359
|
+
vr._validate_implementation_planning_cross_project(data, failures)
|
|
360
|
+
assert failures == []
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def test_no_cross_project_dm_is_noop(vr):
|
|
364
|
+
failures: list[str] = []
|
|
365
|
+
data = _planning_data(
|
|
366
|
+
[{"id": "DM-001", "kind": "order", "item": "x", "impact": "y", "mitigation": "z"}],
|
|
367
|
+
[],
|
|
368
|
+
)
|
|
369
|
+
vr._validate_implementation_planning_cross_project(data, failures)
|
|
370
|
+
assert failures == []
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
- [ ] **Step 2: 테스트 실패 확인**
|
|
374
|
+
|
|
375
|
+
Run: `python3 -m pytest tests/contract/test_cross_project_precondition.py -k "dm or noop" -v`
|
|
376
|
+
Expected: FAIL — `AttributeError: module 'validate_run' has no attribute '_validate_implementation_planning_cross_project'`.
|
|
377
|
+
|
|
378
|
+
- [ ] **Step 3: 검증 함수 추가** — `validators/validate-run.py` 의 `_validate_final_verification_consistency` 정의(line 1530) **앞**에 삽입
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
def _validate_implementation_planning_cross_project(data: dict, failures: list[str]) -> None:
|
|
382
|
+
"""타 프로젝트 의존을 DM 행(`kind == 'cross-project'`)으로 선언했다면
|
|
383
|
+
`crossProjectDependencies` 에 `direction == 'upstream-precondition'` 행이
|
|
384
|
+
반드시 있어야 한다 — 실제 선행 필수 의존이 soft `recommendedNextSteps`
|
|
385
|
+
추천으로 새어나가지 못하게 강제한다. 각 XP 행의 필드 비-빈은 스키마가
|
|
386
|
+
보장하고, 이 검사는 cross-project DM 신호가 있을 때 XP 행이 *존재*하는지를 본다.
|
|
387
|
+
"""
|
|
388
|
+
ip = data.get("implementationPlanning")
|
|
389
|
+
if not isinstance(ip, dict):
|
|
390
|
+
return
|
|
391
|
+
dm_rows = ip.get("dependencyMigrationRisk") or []
|
|
392
|
+
has_cross_project_dm = any(
|
|
393
|
+
isinstance(r, dict) and r.get("kind") == "cross-project" for r in dm_rows
|
|
394
|
+
)
|
|
395
|
+
if not has_cross_project_dm:
|
|
396
|
+
return
|
|
397
|
+
xp_rows = ip.get("crossProjectDependencies") or []
|
|
398
|
+
has_upstream = any(
|
|
399
|
+
isinstance(r, dict) and r.get("direction") == "upstream-precondition"
|
|
400
|
+
for r in xp_rows
|
|
401
|
+
)
|
|
402
|
+
if not has_upstream:
|
|
403
|
+
failures.append(
|
|
404
|
+
"final-report data.json: a dependencyMigrationRisk row has "
|
|
405
|
+
"kind='cross-project' but crossProjectDependencies carries no "
|
|
406
|
+
"direction='upstream-precondition' entry. Record the cross-project "
|
|
407
|
+
"dependency as a mandatory precondition (concrete requiredWork / "
|
|
408
|
+
"verificationSignal / howToStart) — not a soft Recommended Next Step."
|
|
409
|
+
)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
- [ ] **Step 4: taskType 분기에서 호출** — line 1488 의 final-verification 분기 옆에 추가
|
|
413
|
+
|
|
414
|
+
기존:
|
|
415
|
+
```python
|
|
416
|
+
if (data.get("header") or {}).get("taskType") == "final-verification":
|
|
417
|
+
_validate_final_verification_consistency(data, failures)
|
|
418
|
+
```
|
|
419
|
+
로 바꾼다:
|
|
420
|
+
```python
|
|
421
|
+
task_type = (data.get("header") or {}).get("taskType")
|
|
422
|
+
if task_type == "final-verification":
|
|
423
|
+
_validate_final_verification_consistency(data, failures)
|
|
424
|
+
elif task_type == "implementation-planning":
|
|
425
|
+
_validate_implementation_planning_cross_project(data, failures)
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
- [ ] **Step 5: 헤딩 substring 게이트 추가** — `PLANNING_REQUIRED_SECTIONS` (line 1132-1143)
|
|
429
|
+
|
|
430
|
+
```python
|
|
431
|
+
"Requirement Coverage",
|
|
432
|
+
"Plan Body Verification",
|
|
433
|
+
"Cross-Project Dependencies",
|
|
434
|
+
)
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
- [ ] **Step 6: 테스트 통과 확인**
|
|
438
|
+
|
|
439
|
+
Run: `python3 -m pytest tests/contract/test_cross_project_precondition.py -v`
|
|
440
|
+
Expected: PASS (7 passed — 스키마 4 + validate-run 3).
|
|
441
|
+
|
|
442
|
+
- [ ] **Step 7: 커밋**
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
git add validators/validate-run.py tests/contract/test_cross_project_precondition.py
|
|
446
|
+
git commit -m "feat(validate-run): gate cross-project DM rows on upstream-precondition entry"
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
### Task 4: 프로파일 prose + report-writer 동기화 (doc-only)
|
|
452
|
+
|
|
453
|
+
**Files:**
|
|
454
|
+
- Modify: `prompts/profiles/implementation-planning.md` (Cross-Repo Carry 부록 @ 94-98; 섹션 헤딩 contract @ 57; deliverable shape @ 61-116; self-review @ 122-130)
|
|
455
|
+
- Modify: `prompts/lead/report-writer.md` (Recommended Next Steps 정책 @ 284 인근)
|
|
456
|
+
|
|
457
|
+
**TDD exemption:** prose-only — 런타임 동작이 없는 문서 변경. 검증은 `bash validators/validate-workflow.sh` (phase contract) + Task 5 의 `npm run check`.
|
|
458
|
+
|
|
459
|
+
- [ ] **Step 1: Cross-Repo Carry 부록 재작성 (양방향 통합)**
|
|
460
|
+
|
|
461
|
+
`prompts/profiles/implementation-planning.md` 의 `**Cross-repo carry packet ...**` 불릿(94-98)을, freeform 부록이 아니라 **구조 필드 `crossProjectDependencies` 채우기**를 지시하도록 교체. 핵심 문구(한국어):
|
|
462
|
+
- "타 프로젝트(다른 repo / 다른 top-level 배포 모듈 / published 패키지)에 대한 의존은 `crossProjectDependencies` 배열에 `XP-NNN` 행으로 기록한다."
|
|
463
|
+
- `direction: upstream-precondition` = 이 run 이 상대의 선행 작업을 기다림(선행 필수). `requiredWork` = 상대가 만들어야 할 구체 작업, `verificationSignal` = 이 run 이 진행 전 관측할 신호(PR 머지 / 엔드포인트 live / 버전 publish), `linkedWork` = 신호 충족 전 막히는 이 계획의 stage/step, `howToStart` = 상대 repo 에서 `okstra-brief`(이 보고서 절대경로 Source Material) → `okstra-run`.
|
|
464
|
+
- `direction: downstream-carry` = 이 run 이 상대가 쓸 것을 만들어 줌(기존 carry). `requiredWork` = 상대가 후속 구현할 B-portion, `verificationSignal` = 상대가 확인할(이 run 이 인도한) 신호, `howToStart` 동일.
|
|
465
|
+
- 기존 "Recognition caveat" 문구는 렌더 노트(i18n `crossProjectRecognitionNote`)로 이전됐음을 명시하고, "이 run 은 상대 repo 트리·`.okstra/` 에 쓰지 않는다"는 한계 유지.
|
|
466
|
+
- cross-repo dependency 는 `depends-on` 으로 표현 불가(그 게이트는 한 repo git graph 내부만 해소 — `_resolve_stage_base_commit`)라는 기존 경고 유지.
|
|
467
|
+
|
|
468
|
+
- [ ] **Step 2: 섹션 헤딩 contract 갱신** — line 57 의 필수 substring 목록에 `Cross-Project Dependencies` 추가
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
... `Dependency`, `Cross-Project Dependencies`, `Validation Checklist`, `Rollback`, `Requirement Coverage`.
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
- [ ] **Step 3: deliverable shape 항목 추가** — `dependency / migration risk assessment` 불릿(line 100) 인근에 추가
|
|
475
|
+
|
|
476
|
+
문구(한국어): "**Cross-Project Dependencies (조건부 필수):** 계획이 다른 프로젝트/repo/published 패키지의 작업에 의존하면 (a) `dependencyMigrationRisk` 에 `kind: cross-project` DM 행을, (b) `crossProjectDependencies` 에 매칭 `XP-NNN` 행을 둔다. upstream-precondition 행은 `requiredWork`·`verificationSignal`·`howToStart` 가 구체적이어야 하며(`validate-run.py` 가 DM `cross-project` ↔ upstream XP 1:1 을, 스키마가 필드 비-빈을 강제), 단일 프로젝트 계획은 빈 배열."
|
|
477
|
+
|
|
478
|
+
- [ ] **Step 4: self-review 패스 + recommendedNextSteps 정책**
|
|
479
|
+
|
|
480
|
+
(a) self-review(line 122-130)에 점검 항목 추가: "타 repo/패키지 의존을 빠뜨리지 않았는가; DM `cross-project` 행이 있으면 매칭 `upstream-precondition` XP 행이 있고 `requiredWork` 가 추상 표현이 아닌 구체 작업인가; cross-repo 작업을 한 task 의 stage 로 욱여넣지 않고 별도 run + XP 행으로 분리했는가."
|
|
481
|
+
|
|
482
|
+
(b) recommendedNextSteps 정책 한 줄 추가: "cross-project 선행/후속의 substance 는 `crossProjectDependencies` 에 두고, `§3 Recommended Next Steps` 에는 그 섹션을 가리키는 포인터만 둔다(이중 기록 금지)."
|
|
483
|
+
|
|
484
|
+
- [ ] **Step 5: report-writer.md 동기화** — line 284 `Recommended Next Steps` 항목에 한 줄 추가
|
|
485
|
+
|
|
486
|
+
문구(한국어): "cross-project 의존은 `crossProjectDependencies`(§5.4 Cross-Project Dependencies)가 authoritative — `recommendedNextSteps` 에는 substance 를 복제하지 말고 그 섹션을 가리키는 포인터 한 줄만 둔다."
|
|
487
|
+
|
|
488
|
+
- [ ] **Step 6: phase contract 검증**
|
|
489
|
+
|
|
490
|
+
Run: `bash validators/validate-workflow.sh`
|
|
491
|
+
Expected: exit 0 (phase contract 통과).
|
|
492
|
+
|
|
493
|
+
- [ ] **Step 7: 커밋**
|
|
494
|
+
|
|
495
|
+
```bash
|
|
496
|
+
git add prompts/profiles/implementation-planning.md prompts/lead/report-writer.md
|
|
497
|
+
git commit -m "docs(profile): elevate cross-project dependency to mandatory precondition in implementation-planning"
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
### Task 5: 빌드 + 통합 게이트 검증
|
|
503
|
+
|
|
504
|
+
**Files:** (수정 없음 — 빌드/검증만)
|
|
505
|
+
|
|
506
|
+
- [ ] **Step 1: 런타임 동기화**
|
|
507
|
+
|
|
508
|
+
Run: `npm run build`
|
|
509
|
+
Expected: exit 0 — 소스가 `runtime/` 으로 rsync (runtime/ 은 gitignore 라 커밋 대상 아님).
|
|
510
|
+
|
|
511
|
+
- [ ] **Step 2: 새 식별자 일관성 grep**
|
|
512
|
+
|
|
513
|
+
Run: `grep -rn "crossProjectDependencies\|CrossProjectDependency\|Cross-Project Dependencies\|cross-project" schemas/ templates/ validators/ prompts/ tests/ | grep -v runtime/`
|
|
514
|
+
Expected: schema·template·i18n·validator·profile·test 에 일관되게 등장, orphan(정의만 있고 사용 없음 / 사용만 있고 정의 없음) 없음.
|
|
515
|
+
|
|
516
|
+
- [ ] **Step 3: 통합 게이트**
|
|
517
|
+
|
|
518
|
+
Run: `npm run check`
|
|
519
|
+
Expected: exit 0 — JS 테스트 + pytest 전체 통과. (실패 시 해당 도메인만 재현: `python3 -m pytest tests/report tests/contract -v`.)
|
|
520
|
+
|
|
521
|
+
- [ ] **Step 4: 회귀 확인 — 다른 planning 데이터 구성처**
|
|
522
|
+
|
|
523
|
+
Run: `grep -rln "implementationPlanning" tests/ | grep -v runtime/`
|
|
524
|
+
Expected: `crossProjectDependencies` 를 required 로 올렸으므로 다른 planning data.json 을 직접 만드는 테스트가 있으면 `[]` 라도 넣어야 함 — Step 3 에서 실패로 드러나면 해당 fixture/빌더에 `"crossProjectDependencies": []` 추가 후 재실행.
|
|
525
|
+
|
|
526
|
+
- [ ] **Step 5: 최종 커밋 (회귀 수정이 있었을 때만)**
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
git add -A
|
|
530
|
+
git commit -m "test: backfill crossProjectDependencies in planning fixtures"
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## Self-Review
|
|
536
|
+
|
|
537
|
+
**1. Spec coverage:**
|
|
538
|
+
- 스펙 §3.1 데이터 모델 → Task 1. §3.2 렌더 → Task 2. §3.3 강제(모양/존재/헤딩) → Task 1(스키마 모양) + Task 3(교차 필드·헤딩). §3.4 프로파일 prose → Task 4. §3.5 excerpt 빌드 → `schema_excerpt.py` 가 `$ref` transitive closure 로 자동 포함(코드 변경 불필요, Task 5 grep 으로 확인). §6 검증 계획 → 각 Task 의 테스트 + Task 5. 갭 없음.
|
|
539
|
+
|
|
540
|
+
**2. Placeholder scan:** TBD/TODO/"적절히 처리" 없음. 모든 코드 step 은 실제 JSON/Jinja/Python 블록 포함.
|
|
541
|
+
|
|
542
|
+
**3. Type consistency:** `crossProjectDependencies`(필드), `CrossProjectDependency`(def), `XP-NNN`(id), `cross-project`(DM kind), `upstream-precondition`/`downstream-carry`(direction), i18n 키 `crossProjectFields.{requiredWork,verificationSignal,linkedWork,howToStart}` / `crossProjectRecognitionNote` / `sectionAside.crossProjectDependencies` / `emptyState.crossProjectDependencies` — Task 1~3 전반에서 동일 표기. `_validate_implementation_planning_cross_project` 함수명 Task 3 Step 1/3/4 일치. 헤딩 `### Cross-Project Dependencies` 템플릿·테스트·검증기 substring 일치.
|