v0.18.0 G01 — 배포·역할·쓰기 계약이 문서마다 달리 말하던 것에서 HQ 운영 정본·ADR·라벨 병합 큐까지 (2026-09-07)
- 기간: 2026-09-07 ~ 2026-09-07 (세션 1개
4c81b0f9, 오너 지시 "#1279 진행해줘" → 분석·계획 게시 → "go, Phase 0부터 진행해줘") - 랜딩: PR #1296(Phase 0~4 한 PR, squash) — 마이그레이션·엣지 함수 없음, Vercel 앱 배포 없음(문서 사이트·
/admin/만 main 즉시 배포). 선행으로 총괄 문서 PR #1278을343e217a로 머지 - 설계서: 없음 — 분석·Phase 계획은 이슈 #1279 댓글("예상 효과·개선사항" 절 포함)
- 정본:
docs/process/v0-18-0-hq-operations.md(운영),docs/contracts/v0-18-0-adr.md(계약), 총괄 문서2026-09-07-v0-18-0-architecture-roadmap.md§7 장부·§12 G01 행 - 도구:
.github/workflows/landing-queue.yml(PR 라벨 검사), GitHub 라벨landing:queued·landing:active·admin-hold·admin-compat-confirmed - 게이트: 기존 앵커 테스트 5파일(
deploymentPipeline·branchWorkflowOwnership·ciScopePolicy·adminStandaloneShell·completedWorkoutWritePipelineContract) 33/33 유지 — 새 테스트는 만들지 않았다(문서·워크플로 변경, 이슈 지시 "구현을 복사한 테스트 양산 금지"). 마이그레이션·pgTAP·e2e 미접촉 - 버그리포트: 없음(수리 건 아님)
- 계약: 쓰기 파이프라인 §3·§11 두 문장 개정(행 단위 claim·fence 는 v0.18.0 계약, S01 공존 실험 전 비활성). ADR 신설
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 0 | 총괄 문서 PR #1278 머지(충돌 해소), 워크트리, 기준 SHA 고정 | ✅ 343e217a |
| Phase 1 | HQ 운영 정본 §1~§3 — 실측 기준선·진행 트랙 겹침·양자택일 근거·62개 소유 역할·공용 자원 통합 담당 | ✅ 85a17812 |
| Phase 2 | ADR — 보존 정책 P1~P10 / 변경 계약 5종 / 적용 시점·구형 writer 공존 / 쓰기 계약 §3·§11 개정 | ✅ ece68918 |
| Phase 3 | 배포·역할·랜딩·AGENTS 문서 정합 + 운영 정본 §4(병합 큐·슬롯·릴리스·관리자 게이트) + landing-queue 워크플로 + 라벨 | ✅ 24f992c3 |
| Phase 4 | 운영 정본 §5(Ready 판정·인계)·§6(오너 손), 총괄 문서 갱신, 등록, 이 기록, PR | ✅ PR #1296 |
1. 배경
v0.18.0 은 62개 작업을 6개 분야·5개 Phase 로 여러 세션(여러 PC 가능)이 병렬로 진행하는 리팩터링이다. 총괄 문서(PR #1278)가 목표·작업 지시서·의존성을 정했지만, "작업자들이 서로 다른 목표나 과거 규칙을 따르지 않도록 이번 릴리즈의 정본을 만드는" 첫 작업 G01 이 남아 있었다. 총괄 문서 자신도 세 곳에서 "G01 에서 정리한다"고 미뤄 두었다: 옛 main=production 설명, 모델 이름을 권한으로 오해할 역할 문서, 쓰기 계약의 CAS 금지 문장.
2. 문제 제기
배포 설명이 한 문서 안에서 둘이었다
deployment-pipeline.md 첫 문단 "main 머지가 Vercel 운영 배포를 시작한다"와 "Required Release Order" 6단계 "릴리스 브랜치를 main 에 머지하여 frontend activation"은 2026-08 환경 분리 전 규칙이고, 같은 문서의 환경 분리 절은 "main = staging, production = Production"이다. 새 세션이 어느 문단을 먼저 읽느냐에 따라 릴리스 절차를 다르게 이해한다.
역할 문서가 옛 도구 이름을 권한처럼 전제했다
branch-workflow.md 는 Claude Design / Windows Codex / MacBook Codex 와 역할 브랜치 4개를 전제한다. 실제로는 모든 세션이 같은 모델이고 scratchpad 워크트리의 feat/·fix/·docs/ 브랜치에서 일한다. AGENTS.md 제목도 "Codex Working Rules". 테스트 branchWorkflowOwnership.test.mjs 가 소유권 문장을 원문에서 읽으므로 통째로 고칠 수도 없었다.
쓰기 계약이 목표 구조와 충돌했다
쓰기 파이프라인 계약 §3 "탭 간 잠금·리더 선출은 두지 않는다", §11 "Web Lock·CAS·lineage·탭 간 리더 선출은 여전히 없다". 목표 구조(S01/S02/S04)는 대기열 행마다 원자 상태 전이 + 탭별 claim + fence 를 요구한다. 계약을 그대로 두면 S04 담당이 계약 위반으로 시작하고, 계약만 바꾸면 "새 writer 가 안전해졌다"로 읽힌다.
랜딩 잠금이 한 PC 전제였다
migration-landing.md 마지막 절이 "모든 세션이 이 PC 에서 돈다는 전제… 다른 PC 면 재설계 필요"라고 스스로 밝힌다. 총괄 문서 §6 은 "여러 PC 의 로컬 잠금은 서로를 잠그지 못한다. HQ 만 실제 landing"이라고만 적었다.
GitHub 설정이 문서와 달랐다 (실측)
main 에 보호 규칙이 없다(직접 push 가능, 필수 검사 없음). production 만 규칙셋. GitHub Environment production 에 required reviewer 가 없어 릴리스 PR 머지 즉시 배포된다(문서는 "설정돼 있으면 멈춘다"고만 적음). GitHub merge queue 는 private 저장소라 미제공(mergeQueue: null).
관리자 패널이 main 푸시 즉시 공개된다
/admin/ 은 barbelic-docs 프로젝트가 main 에서 바로 빌드한다. v0.18.0 동안 main 에 쌓이는 공용 코드가 Production DB 에 없는 함수를 부르면 관리자 화면만 먼저 깨질 수 있다.
3. 해결 방안
원칙 (오너 결정, 2026-09-07)
- 오너 지시 "이슈 진행할 때마다 결정사항이 너무 많이 딸려 나온다. 진짜 꼭 필요한 거 아니면 물어보지 마" → 전역 지침 §26 신설. 이 트랙의 D1~D6 은 오너 결정으로 올리지 않고 권장안을 전제로 진행했다: D1 총괄 문서 PR 먼저 머지(HQ 가 직접), D2 GitHub 라벨 큐 + HQ 단일 머저, D5 관리자 게이트는 규칙 + 검사(
admin-hold), D6 충돌은 사본 + 명시 복구(총괄 문서가 이미 적음). D3(main 보호 규칙)·D4(Production 승인자)는 GitHub 설정이라 "오너 손이 필요한 것" 목록(운영 정본 §6)으로 옮겼다. - 총괄 문서 D1~D3(개선사항 전체 포함·양자택일은 하나로·정본은 총괄 하나 + 개별 기록)과 전역 지침 §22(땜질 대신 구조)를 그대로 따랐다.
접근
| 안 | 내용 | 판정 |
|---|---|---|
| 정본 통합 + 최소 게이트 | 새 문서 2개(운영 정본·ADR), 기존 5개 문서의 충돌 문장을 그 자리에서 정정, 라벨 큐 + 워크플로 검사 | 채택 — G01 소유 경계(AGENTS.md·docs/contracts/**·docs/process/**·.github/workflows/**) 안에서 끝나고 되돌리기 쉽다 |
| 총괄 문서 안에 전부 적기 | 총괄 문서에 ADR·운영 규칙 추가 | 기각 — PR #1278 이 관리 중이라 동시 수정 금지, 계약 정본은 docs/contracts·docs/process 라는 기존 규칙과 충돌 |
| 원격 ref 잠금 스크립트 | landing-lock.mjs 를 GitHub ref CAS 로 확장 | 기각(이번 이슈) — scripts/** 는 G02 경계. 필요하면 G02/D13 후속 |
| GitHub merge queue | GitHub 기능 | 불가 — private 저장소 미제공, 플랜 변경은 비용 |
4. 적용한 내용
Phase 0 — 착수 준비
PR #1278 이 main 과 충돌 상태였다(브랜치가 workoutDraftArchive.test.mjs 를 #1291 과 다르게 고침). 브랜치의 Date 격리 버전을 택해 main 을 합치고(12/12 통과) squash 머지 → main 343e217a. 워크트리 feat/1279-g01-hq-contracts.
Phase 1 — 운영 정본 §1~§3 (85a17812)
docs/process/v0-18-0-hq-operations.md: §1 실측 기준선(브랜치·보호 규칙·환경·CI 워크플로 5개)과 진행 중 트랙 #1277(A07/B01 겹침)·#1256(D01/D04/A06 겹침) 처리, 재측정 명령. §2 양자택일 4건(F07·F09·F24·F30) 선택 근거, 62개 계획 ID 의 소유 역할·게시 번호·지시서 링크(사람 이름 배정 없음 — 착수 댓글의 세션 ID 가 담당). §3 공용 자원 12종의 "분리 경계 담당 / 새 목적지 구현 / 최종 병합 담당".
Phase 2 — ADR (ece68918)
docs/contracts/v0-18-0-adr.md: 유저 A 이야기 → 보존 정책 P1~P10 → 변경 계약 5종(§3-1 outbox 행별 원자 전이·claim·fence·durable successor, 전역 리더 선출 없음, 활성화는 S01 공존 실험 뒤·R02 최종; §3-2 typed 경계; §3-3 사본 + 명시 복구, 자동 field merge 미도입 근거 3가지; §3-4 통계 단일 작성자·generation; §3-5 기존 store typed 완성) → §4 개정하는 현행 계약 문장 표(두 곳만) → §5 호환 코드 경계 → §6 결정 장부 ADR-1~6. 쓰기 파이프라인 계약 §3·§11 개정.
Phase 3 — 문서 정합 + 운영 정본 §4 + 워크플로 (24f992c3)
deployment-pipeline.md: 첫 문단을 main=staging / production=Production 으로, 8단계를deploy.yml실제 순서(릴리스 PR → dry-run=release diff → db push → remote-schema → functions → frontend → smoke)로. 앵커(## Required Release Order·"DB 계약을 먼저 배포하고 검증"·"DB migration을 적용"·"Edge Functions를 배포"·"Vercel production deployment")는 유지.branch-workflow.md: 맨 앞 "v0.18.0 실행 모델" 절 — 역할 = 책임, 옛 모델 이름·역할 브랜치는 역사 표기, 실제 워크트리 관행, main 보호 규칙 미설정 실측, 정본 링크. 아래 절들은 소유권·머지 게이트 정본으로 유지(테스트 앵커 보존).migration-landing.md: "다른 PC" 조항을 "PC 안은 로컬 잠금, PC 사이는 라벨 큐"로. 1단계에landing:active선행 한 줄.AGENTS.md: 첫머리에 역할 = 책임 문장과 정본 링크, main=staging.- 운영 정본 §4: 4-1 원칙(큐 대상 = 마이그레이션 추가 PR + HQ 통합 슬롯 자원), 4-2 라벨 큐 절차와 검사 실패 조건 4개, 4-3 스테이징 슬롯(
deploy-main직렬), 4-4 릴리스 게이트, 4-5 관리자 게이트(감시 경로 7종·서버 함수 15개 실측·라벨 규칙), 4-6 배포 경로 정본 표. .github/workflows/landing-queue.yml: PRopened/synchronize/reopened/labeled/unlabeled마다gh pr list --label landing:active로 동시 개수,git diff --diff-filter=A로 새 마이그레이션, 관리자 경로 변경을 보고 4개 조건 판정. 마이그레이션 없는 PR 은 수 초.- GitHub 라벨 4개 생성(색·설명 포함).
Phase 4 — Ready 판정·인계·등록·기록 (PR #1296)
운영 정본 §5(상태 7단계와 Ready 조건 4가지, G01 → G02/G05/G03/D03/S·D 계열로 넘기는 것 표, 인계 규칙), §6 오너 손 O1·O2. 총괄 문서 헤더·Phase 1 진행률·§7 장부 첫 행·D5·§12 G01 행·G01 지시서 완료 증거 4항목. 사이드바 3곳·README 3곳 등록.
주요 결정과 그 근거
- 여러 PC 직렬화를 라벨로: 로컬 파일 잠금은 다른 PC 가 볼 수 없고 merge queue 는 미제공. 라벨은 GitHub 상태라 모든 PC 가 같은 것을 본다. 강제력은 검사 + (오너가 켜면) main 보호 규칙.
- 계약 문장은 두 곳만 개정: "선언만 바꾸고 새 writer 가 안전해졌다고 기록하지 않는다"(이슈 지시 4). 개정 문장 자체가 "S01 공존 실험 전 비활성"을 담는다.
- 사람 이름 배정 없음: 이슈 지시 5 "담당자 확정 전에는 이름을 추정해 배정하지 않는다" → 역할 표 + 착수 댓글의 세션 ID.
작업 중 드러난 것
- PR #1278 은 GitHub 이 CONFLICTING 이라고 했지만 로컬 첫 머지는 충돌 없이 됐다 — 원격 브랜치가 그 사이 3커밋 더 나아가 있었고(다른 세션이 계속 push), 최신으로 되돌린 뒤에야 실제 충돌(테스트 파일)이 나왔다. 다른 세션이 살아 있는 브랜치는
fetch직후에만 판단한다. - 로컬에 YAML 파서(js-yaml·PyYAML)가 없다 →
npx -y js-yaml로 검사. 워크플로 실제 실행은 PR #1296 에서 GitHub 이 처음 돌렸다. docs/node_modules가 본 체크아웃에도 없어 VitePress build 는 워크트리docs/에npm ci뒤 돌렸다(19.96s).- 관리자 셸의 서버 함수 목록은
rpc('…')문자열이 아니라barbelicApi경유라 문자열 grep 으로만 15개를 얻었다 — B01/A05 가 정확한 목록을 다시 만든다.
5. 적용 결과
| 항목 | 전 → 후 |
|---|---|
| 배포 설명 정본 | deployment-pipeline.md 안에 "main 머지 = 운영 배포" 문장 1 + 옛 6단계 1 → 0 (정정 메모 1개로 이력만 남김) |
| 역할 정의 | 모델 이름 3종 전제 → "v0.18.0 실행 모델" 절 1개가 앞에서 정의, 옛 이름은 역사 표기. 앵커 테스트 33/33 유지 |
| 쓰기 계약 vs 목표 구조 | "탭 간 잠금·CAS 없음" 무조건 문장 2 → 행 단위 claim·fence = v0.18.0 계약 + 활성 조건(S01→R02) 2문장. 게이트 테스트 5/5 |
| 보존/변경 계약 구분 문서 | 0 → ADR 1 (보존 10·변경 5·양자택일 2·결정 6) |
| 62개 작업 소유 | 총괄 §13 지시서의 소유 경계만 → 역할·게시 번호·통합 담당 표 62행 + 공용 자원 12종 통합 담당 |
| 여러 PC 마이그레이션 랜딩 직렬화 | 이 PC 파일 잠금만 → 라벨 큐(landing:active 1개) + landing-queue 검사(조건 4) + HQ 단일 머저 |
| 관리자 패널 선공개 게이트 | 없음 → admin-hold/admin-compat-confirmed 규칙 + 검사 |
| Ready 판정 절차 | 없음 → 운영 정본 §5(7단계·조건 4·인계 표 7행) |
| G01 완료 증거 4항목 | 미체크 → 근거와 함께 체크(총괄 §13 G01) |
| 검증 | node --import tsx --test 앵커 5파일 33/33 · check:deployment·당시 브랜드 검사(2026-09-08 폐지)·UTF-8 통과 · npm run check 전체 · node scripts/check-typescript-unused.mjs · VitePress build 성공 · landing-queue 검사 PR #1296 에서 실행(결과는 PR) |
| 미검증 | main 규칙셋은 랜딩 당일 저녁 오너가 적용해 landing-queue 가 필수 검사가 됐다. 라벨 2개 동시 부착·마이그레이션 추가 PR 시나리오는 실제 PR 로는 아직 안 돌렸다(첫 마이그레이션 PR 이 첫 실측) |
CI 생략 — 근거(전역 지침 §19): 문서·워크플로·AGENTS 만 변경. CI 잡(verify·migration-smoke·e2e·pgTAP)이 실행 경로로 검증하는 코드가 없고, 문서 앵커 테스트는 로컬 npm run check 에 포함돼 같은 검사를 돌렸다. 워크플로 파일 변경은 CI 분류상 full 레인을 켜지만 결과를 기다리지 않았다.
6. 이번 개선으로 향상된 것
새 세션이 어느 문서를 읽어도 같은 배포 절차를 본다
main=staging / production=Production 이 배포 문서·브랜치 문서·AGENTS·운영 정본 §4-6 표에서 같다. 옛 문장은 "2026-09-07 정정" 메모로만 남아 왜 바뀌었는지 보인다.
후속 11개 이슈가 무엇을 기다리고 무엇을 만들어야 하는지 표 하나로 안다
운영 정본 §5-2. G02/G05 는 지금 착수 가능(선행 = G01 머지), G03 은 G05 뒤, D03 은 G02 뒤. 각 행에 "확정된 것 / 스스로 만들 것"이 갈라져 있어 계약 미정인 코드를 추측해 만들지 않는다.
마이그레이션 랜딩이 PC 를 넘어 직렬화된다
라벨 하나(landing:active)가 저장소 전체의 "지금 랜딩 중"이고, 검사가 두 개째를 빨간불로 만든다. 로컬 잠금은 그대로 PC 안의 두 번째 자물쇠다.
구조적으로 남는 것
운영 정본·ADR 두 정본, landing-queue 검사와 라벨 4개, 배포·브랜치·랜딩·AGENTS·쓰기 계약의 정정 문장(앵커 테스트가 계속 지킨다), 총괄 문서 §7 장부의 첫 행과 D5.
남은 것
- 오너 손: O1 main 보호 규칙은 2026-09-07 오너가 적용(규칙셋
main-protection, 실측 확인). O2 Production 환경 required reviewer 는 2026-09-07 오너 확인 결과 설정 항목이 없다 — private 저장소의 배포 보호 규칙은 Pro/Team/Enterprise 플랜 전용(이 저장소는 Free). 유료 전환은 오너 결정, 그 전에는 "릴리스 PR 은 오너만 머지" 규칙이 승인 단계. 운영 정본 §6. - 후속 이슈 착수: G02(#1280)·G05(#1281) Ready — HQ 가 총괄 §12 상태 칸을 Ready 로 바꾸고 착수 세션이 §21 댓글. 이 세션이 HQ 역할을 유지한다(인계 시 §21).
- 검사의 실측: 첫 마이그레이션 PR 이 라벨 큐를 실제로 통과하는 것이
landing-queue의 첫 실측. 관리자 셸 함수 목록 정밀화는 B01/A05. - 원격 ref 잠금 스크립트(라벨보다 강한 직렬화)는 필요가 확인되면 G02/D13 후속 이슈로.