Skip to content

Commit 7b22a8b

Browse files
committed
merge(dev): refresh pool-plan branch onto tip after lidge-jun#801
2 parents e33fece + 33caf33 commit 7b22a8b

116 files changed

Lines changed: 5674 additions & 407 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/scripts/enforce-pr-target.test.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ describe("enforce-pr-target workflow", () => {
6565
assert.match(workflow, /stackedBase/);
6666
assert.match(workflow, /github\.rest\.pulls\.list/);
6767
assert.match(workflow, /treating as stacked/);
68+
assert.match(workflow, /other\.base\?\.repo\?\.owner/);
6869
const qualityCall = workflow.match(
6970
/collectPrQualityFailures\(\{([\s\S]*?)\}\);/,
7071
);

.github/workflows/enforce-pr-target.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ jobs:
299299
other =>
300300
other.number !== pull_number &&
301301
other.head?.ref === pr.base.ref &&
302-
(other.head?.repo?.owner?.login ?? owner) === baseOwner &&
303-
(other.head?.repo?.name ?? repo) === baseName
302+
(other.base?.repo?.owner?.login ?? owner) === baseOwner &&
303+
(other.base?.repo?.name ?? repo) === baseName
304304
);
305305
if (stackedBase) {
306306
core.info(
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# 000 — PR·이슈 트리아지 라운드 계획
2+
3+
`260731_pr_merge_round`가 끝난 자리에서 이어간다. 그 라운드는 PR 13건과 이슈
4+
11건을 정리하고 `7132828b3`으로 dev에 들어갔다. 남은 것들은 "나중에"가 아니라
5+
**왜 안 태웠는지**가 기록된 채로 남았다. 이 유닛은 그 기록을 현재 트리 기준으로
6+
다시 검증하고, 다음 라운드에서 실제로 손댈 수 있는 목록을 뽑는다.
7+
8+
## 기준점 — 동결 인벤토리
9+
10+
| 항목 ||
11+
|---|---|
12+
| 인벤토리 동결 시각 | 2026-07-31T05:05Z |
13+
| dev HEAD | `7132828b3 merge: the 260731 PR round into dev` |
14+
| dev CI | Cross-platform CI `success`, Service lifecycle `success` (2026-07-31T04:40:05Z) |
15+
| 열린 PR | 27건 (전부 base=`dev`, 위반 0건. draft 5건: #557 #569 #644 #747 #750) |
16+
| 열린 이슈 | 41건 |
17+
| 직전 유닛 | `devlog/_plan/260731_pr_merge_round/` (`000`~`050`) |
18+
19+
숫자는 이번 실행에서 `gh pr list --state open --limit 100`
20+
`gh issue list --state open --limit 200`로 직접 세었다. 직전 라운드 문서의
21+
"43건"은 그 시점 값이고 지금은 다르다 — 그래서 문서 숫자를 인용하지 않고 다시 셌다.
22+
직전 라운드가 정리했다는 "PR 13건·이슈 11건"도 이 유닛에서 재대조하지 않은
23+
집계값이므로 근거로 쓰지 않는다. 필요한 건 `010`/`020`이 각 항목을 직접 확인한다.
24+
25+
**인벤토리는 동결값이다.** 트리아지 도중 새 PR·이슈가 열려도 이 라운드의 매트릭스는
26+
위 시각 기준으로 닫는다. 그렇지 않으면 `010``020`이 서로 다른 스냅샷을 보게 되고,
27+
이슈↔PR 상호참조가 어긋난다. 라운드 종료 시 `gh`를 다시 돌려 그 사이 생긴 항목만
28+
`040`에 이월 목록으로 남긴다.
29+
30+
행마다 기록하는 필드: PR은 번호·head SHA·base·draft 여부·**메인테이너 승인 상태**·
31+
필수 CI 결과·변경 경로·연결 이슈, 이슈는 번호·라벨·중복/업스트림 여부·
32+
보고자 증거 유무·덮는 PR.
33+
34+
## 이 유닛이 답해야 하는 것
35+
36+
1. 열린 PR 27건 각각: 지금 dev에서도 결함이 살아 있나, 이미 대체됐나, 무엇이 막고 있나.
37+
2. 열린 이슈 41건 각각: 재현되나, 랜딩된 커밋이 이미 고쳤나, 어느 PR이 덮나.
38+
3. 그중 **우리가 직접 고쳐서 머지할 수 있는 것**은 무엇인가. 의존 순서는.
39+
4. 다음 구현 라운드의 decade 문서 지도.
40+
41+
3번이 이 유닛의 목적이다. 1·2번은 3번의 근거일 뿐이다.
42+
43+
## 방법
44+
45+
**증거 기준.** 각 행은 이번 실행에서 읽은 앵커를 하나 이상 갖는다 — `path:line`,
46+
커밋 SHA, 또는 PR/CI 상태. 직전 라운드 문서에서 옮겨 적은 판단은 근거로 치지 않는다.
47+
같은 파일을 건드린 커밋이 있다는 사실도 근거가 아니다. 보고된 실패 모드가 실제로
48+
해소되는지까지 본다 (`260730_issue_triage_dev_head/000`에서 세운 기준을 유지한다).
49+
50+
**서브에이전트.** `gpt-5.6-luna`, reasoning effort max. 읽기 구획을 겹치지 않게
51+
나눠 파견하고, 쓰기 권한은 주지 않는다. 판단과 통합은 메인 세션이 한다.
52+
53+
**보안 경계.** `AGENTS.md`가 정한 대로, 미공개 취약점 분석·심각도 평가·재현 절차는
54+
`devlog/`에 쓰지 않는다. 트리아지 중 미수정 보안 결함이 나오면 추적 문서에는
55+
중립적 포인터만 남기고 분석은 `.tmp/`로 보낸다.
56+
57+
## 먼저 확인한 것: Windows CI 적색의 정체
58+
59+
열린 PR 27건 중 11건이 `windows-latest:FAILURE`다(동결 시각 기준). 이걸 PR별 결함으로
60+
읽으면 매트릭스가 왜곡되므로 먼저 확인했다.
61+
62+
PR #776(`+18/-2`, provider 프리셋에 baseUrl 선택지 하나 추가)의 실패 로그
63+
**발췌**(run `30594436278`, 실제로는 12건 실패):
64+
65+
```
66+
(fail) release helper > preflight runs typecheck, test suite, and privacy scan before version bump on main dry-runs
67+
(fail) release helper > dispatch pins the audited release SHA via expected-sha
68+
(fail) server local API auth > missing or non-string detail never authorizes a pool retry
69+
error: ENOENT: no such file or directory, uv_spawn 'C:\Users\runneradmin\.bun\bin\bun.exe'
70+
```
71+
72+
18줄짜리 provider 프리셋 변경이 릴리스 헬퍼와 로컬 API 인증을 깨뜨릴 수는 없다.
73+
그리고 dev HEAD의 같은 워크플로는 초록이다. 원인은 dev에 나중에 들어간 두 커밋이
74+
PR 브랜치에 없는 것이다:
75+
76+
- `371aa579d fix(windows): resolve PATH whatever casing the child was handed`
77+
(`src/lib/win-exec.ts`, `tests/release-helper.test.ts`, `tests/win-exec.test.ts`)
78+
- `0af17fbfd fix(windows): route every gh and release command through the shared launcher`
79+
(`scripts/release.ts` 외 launcher 경로)
80+
81+
### stale-base 판정 절차 (PR마다 개별 적용)
82+
83+
"윈도우가 빨가면 stale-base"로 뭉뚱그리면 PR 고유 결함을 놓친다. #793(run
84+
`30596144266`)은 release-helper 4건만 실패하고 `uv_spawn`은 없다 — #776과 실패
85+
집합이 다르다. 공유되는 건 **실패 클래스**지 실패 목록이 아니다. 그래서 행마다
86+
아래를 밟고, 결과를 `010`의 CI READING 칸에 적는다.
87+
88+
1. 실패한 테스트 파일 이름을 뽑는다.
89+
2. PR이 바꾼 경로와 대조한다(`gh pr diff <n> --name-only`).
90+
3. 실패가 전부 알려진 인프라 클래스(`tests/release-helper.test.ts`,
91+
`tests/win-exec.test.ts`, `bun.exe` `uv_spawn` ENOENT, 그리고 이들에
92+
딸린 `server-local-api-auth` 파생)에 속하고 PR 변경 경로와 무관하면
93+
`stale-base`.
94+
4. 하나라도 PR이 건드린 표면과 겹치면 → `real failure`로 분류하고 그 테스트
95+
이름을 근거로 남긴다.
96+
5. 판단이 갈리면 `stale-base`로 내리지 않는다. `UNVERIFIED — 리베이스 후 재확인`
97+
으로 남긴다. 안전한 쪽은 의심하는 쪽이다.
98+
99+
## 작업 단계 지도
100+
101+
선형 의존이다. WP2는 WP1에 의존한다 — 이슈 판정에 `COVERED-BY-PR`이 있고, 어느 PR이
102+
어느 이슈를 덮는지는 `010`이 확정해야 알 수 있다. 처음엔 둘을 독립으로 뒀는데
103+
감사가 이 교차 의존을 잡아냈다. 병렬로 돌리면 두 문서가 서로 다른 PR 상태를 보고
104+
상호참조가 어긋난다.
105+
106+
| 단계 | 문서 | 산출물 | 선행 |
107+
|---|---|---|---|
108+
| WP1 | `010_pr_triage_matrix.md` | 열린 PR 27건 전체 처분 | 동결 인벤토리 |
109+
| WP2 | `020_issue_triage_matrix.md` | 열린 이슈 41건 전체 처분 | WP1 (PR 커버리지) |
110+
| WP3 | `030_mergeable_bug_list.md` | 다음 라운드에 태울 수 있는 것, 의존 순서 | WP1, WP2 |
111+
| WP4 | `040_next_round_roadmap.md` | 구현 라운드 decade 지도 + 게이트 | WP3 |
112+
113+
`cxc-pabcd`의 one-work-phase-one-cycle 불변식에 따라 각 단계는 온전한 PABCD
114+
사이클로 돈다. 한 B에서 두 문서를 쓰지 않는다.
115+
116+
## 판정 어휘
117+
118+
PR:
119+
120+
| 판정 ||
121+
|---|---|
122+
| `MERGE-NOW` | 아래 머지 게이트를 **전부** 통과한 것 |
123+
| `NEEDS-REBASE` | 내용은 괜찮으나 충돌 또는 stale base |
124+
| `NEEDS-CHANGES` | 결함은 진짜지만 구현·테스트·증거가 미달 |
125+
| `SUPERSEDED` | dev가 이미 같은 문제를 해결함 |
126+
| `CLOSE` | 더 이상 유효하지 않음 |
127+
| `DEFER` | 유효하나 자체 사이클 또는 오너 판단 필요 |
128+
129+
### `MERGE-NOW`의 실제 조건
130+
131+
`MAINTAINERS.md:30-31`**메인테이너 1인 이상의 승인****필수 CI 통과**
132+
머지 전제로 요구한다. 처음 판정 정의에는 이게 빠져 있었고 감사가 잡았다. 기술적으로
133+
깨끗한 PR을 "지금 머지 가능"으로 적으면 통과하지 않은 게이트를 통과한 것처럼 만든다.
134+
135+
`MERGE-NOW`는 다음이 전부 참일 때만 쓴다:
136+
137+
1. 결함이 dev HEAD에 살아 있다(코드 앵커로 확인).
138+
2. 결함 없이는 실패하는 회귀 테스트가 있다.
139+
3. base가 `dev`다(스택 PR 예외는 아래).
140+
4. 필수 CI가 초록이거나, 적색이 stale-base로 **개별 입증**됐고 리베이스로 해소된다.
141+
5. 보안 리뷰 표면(`AGENTS.md`)을 건드리지 않는다. 건드리면 최대 `DEFER`다.
142+
6. 메인테이너 승인이 남아 있다면 그 사실을 행에 명시한다.
143+
144+
6번이 미충족이면 `MERGE-NOW (승인 대기)`로 적는다. 승인은 우리가 대신할 수 없다.
145+
146+
### base 브랜치 게이트
147+
148+
`AGENTS.md` 브랜치 정책상 모든 PR은 `dev`를 타깃해야 한다. 동결 시각 기준 27건
149+
전부 `base=dev`다(위반 0건). 다만 **열린 PR의 head를 타깃하는 스택 자식 PR은
150+
의도된 워크플로**이고 `enforce-target`이 그 경우 wrong-base 게이트를 건너뛴다.
151+
따라서 base가 `dev`가 아닌 PR을 발견하면 자동으로 위반 처리하지 않고, 부모 PR이
152+
아직 열려 있는지부터 확인한 뒤 `010`에 스택 관계를 적는다.
153+
154+
이슈:
155+
156+
| 판정 ||
157+
|---|---|
158+
| `FIXED-ON-DEV` | 랜딩된 커밋이 실제로 실패 모드를 해소 |
159+
| `OPEN` | 현재 코드에서 재현 |
160+
| `PARTIAL` | 일부만 해소, 잔여 있음 |
161+
| `COVERED-BY-PR` | 열린 PR이 덮음 |
162+
| `NEEDS-DECISION` | 제품 판단 필요 |
163+
| `UPSTREAM` | 업스트림 추적 목적 |
164+
165+
이슈 행에는 라벨, 중복 대상, 덮는 PR, 보고자 재현 증거 유무를 함께 적는다.
166+
`upstream-tracking` 라벨(#417 #241 #92 #418)은 오래됐다는 이유로 닫지 않는다 —
167+
추적 목적이 살아 있는 한 유효하다.
168+
169+
## 종료 게이트
170+
171+
문서만 바꾸지만 게이트는 돈다. `privacy:scan``devlog/`를 실제로 읽으므로
172+
(`AGENTS.md:47-50`) 공개 devlog에서는 형식적 절차가 아니다.
173+
174+
- `bun run typecheck` — 소스 미변경 회귀 가드
175+
- `bun run test tests/repo-hygiene.test.ts` — gitlink·제외 경로 재유입 차단
176+
- `bun run privacy:scan` — 공개 devlog 내용 스캔
177+
- `bun run test``AGENTS.md:105-106`이 비자명 변경에 요구하는 전체 스위트.
178+
이 유닛은 소스를 안 건드리므로 통과가 기대값이고, 실패하면 그건 우리 변경이
179+
아니라 트리 상태 문제다. 그 사실 자체가 `030`에 들어갈 정보다.
180+
- `git status`로 이 유닛 파일만 스테이징됐는지 확인
181+
182+
## 종료 처리
183+
184+
`devlog/README.md:26-28`대로, 터미널 결과가 기록되면 유닛은 `_fin/`으로 옮긴다.
185+
다만 이 유닛의 산출물은 **다음 구현 라운드의 입력**이다. 그 라운드가 `030`/`040`
186+
소비하기 전에는 `_plan/`에 남는다. 이월 조건: `040`의 모든 단계가 후속 유닛에
187+
반영되면 그때 `_fin/`으로 옮기고 터미널 결과(`DONE`/`NOOP`/`BLOCKED`/`NEEDS_HUMAN`)를
188+
적는다.
189+
190+
## 범위 밖
191+
192+
`src/`, `tests/`, `gui/`, `docs-site/`, `scripts/`, `.github/` 수정. push.
193+
머지·리베이스·브랜치 변경. GitHub 코멘트·라벨·클로즈·머지. 릴리스와 배포.
194+
195+
이 유닛은 문서만 만든다. 고치는 건 다음 라운드다.

0 commit comments

Comments
 (0)