diff --git a/REPORT.md b/REPORT.md index 317961e..11cb74a 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,5 +1,27 @@ # REPORT +## [2026-09-04] upstream 동기 «정기 축» 판정 — 격차 기반 `behind ≥ 20` (rustjava-upstream-sync-cadence-decision) +- 무엇을: 채택 제안 `2026-09-04-sync-contract-stale-assets-decision#p0` 에 대한 **판정**이다. + ★**결론: 동기 회차는 `origin/main..upstream/main` 이 «20 이상»일 때 연다** — 시간(격주·월간) 기반은 기각. + ★**신설 0**(검사기·크론·워크플로) · `.rs` **0줄** · ★**S9 미개시**(behind **0**). 바뀐 것은 `docs/upstream-sync-approach.md` **§5-B** 뿐이다. +- 왜: ★★**전제를 «먼저» 재서 «반증»했다.** 회차 커밋 수 ↔ 충돌의 상관계수 ★**부호가 «음»**이다. + ★★**[게이트② 정정] 초판 표는 base·(델타/누적) 병기가 없어 기준이 섞여 있었다** ⇒ 정본을 **«누적»**으로 통일해 다시 계산했다: + A 초판(혼재) **r=−0.169 · 합 28** · B S4 만 정정 **r=−0.134 · 합 30** · ★**C 정본 «전부 누적» r=−0.155 · 합 33**. + ★**세 열 모두 부호가 음**이고 「많은4 < 적은4」가 유지된다(정본 C: **37커밋 → 14** ↔ **8커밋 → 19**) ⇒ ★**판정 불변**. + ★**단 r 은 n=8 비무작위 표본이라 «비례하지 않는다»는 «부호 판정»으로만 쓴다.** + ★**누적 충돌도 포화한다**(고정 base: **7커밋 16 → 33커밋 19**) ⇒ ★**충돌을 만든 것은 «양»이 아니라 «무엇이 왔는가»**다. + ★**그래도 「아무것도 안 한다」로 가지 않았다** — 반증된 것은 «비용이 격차에 비례한다»는 기전이고, + 제안이 지목한 «아무도 챙기지 않는다»는 위험은 그대로 참이라서 **축은 두되 임계를 포화점 뒤**에 놓았다. +- 사용자 영향: 런타임 **무변경**. ★**다음 동기를 «언제» 여는지가 처음으로 정해졌다** — 그 전엔 아무 기준이 없었다. +- 검증: behind **0**(요구값) · `merge-base` `bd42427` = upstream HEAD · S1~S8 회차별 커밋/충돌 전수 · + 게이트② 사이클 전수(`reports/*.review.md` 줄1 — 8회차 중 **7회가 1사이클**) · + upstream 12개월 **144커밋/51주**(주당 중앙 **1** · ★**0인 주 37%**) · 도달 시간 behind 20 = **중앙 44일** + (★**상수로 인용하지 마라** — 게이트② 재측 **41일** · 차이는 **창 정의**에서 온다 ±3일 · **결론 영향 0**). +- ★**비용**: 회차당 새 충돌 **중앙 2.5**(0~9) · 티켓 **3건** · 연 **6~8회** ⇒ 연 **18~24 티켓**. +- ★**방아쇠**: **기계가 «재고» 사람(총괄)이 «발권»** 한다. 재는 자리 권고 = 이 repo 의 **예약 워크플로** + (선례 `rust-audit.yaml` 이 이미 `schedule: cron` · upstream 은 **read-only fetch** 라 발신 0). ★**구현은 별건.** +- ★**후속 추천**: ⑴behind 를 «재는» 워크플로 1개(발권은 사람 유지) ⑵★**「8회차가 들었다」를 «격차 33 의 비용»으로 인용하지 마라** — + 그 덩어리의 상당 부분은 **스쿼시로 계보가 접힌 비용**이고 `merge_strategy: merge` 로 이미 닫혔다. ## [2026-09-04] 형제 repo 파리티 락 필요성 조사 — 둘 다 «조건부 필요», 검사기는 포팅 불가 (rustjava-dod-ci-parity-sibling-repo-survey) - 무엇을: 채택 제안 `2026-09-04-dod-ci-parity-lock#p1` 에 대한 ★**읽기 전용 조사 + 판정**이다. `wie`·`qts` 의 **DoD 정본 위치 · CI 검사 집합 · 대칭차**를 각각 실측하고, ★**이력으로 실익까지** 쟀다. diff --git a/STATE.md b/STATE.md index c76391d..8c5af63 100644 --- a/STATE.md +++ b/STATE.md @@ -23,6 +23,35 @@ (25번째 run, 앞선 24건 전부 red). **PR 대기 — 게이트③ 미착지.** ## 완료 +- [rustjava-upstream-sync-cadence-decision] ★★**upstream 동기 «정기 축» 판정 — 결론: ⒝ 격차 기반 `behind ≥ 20`.** + 채택 제안 `2026-09-04-sync-contract-stale-assets-decision#p0`. ★**신설 0**(검사기·크론·워크플로) · `.rs` **0줄** · ★**S9 미개시**(behind **0**). + ★★**제안의 전제(「벌어진 뒤에 받으면 훨씬 비싸다」)를 «먼저 재서» 반증했다**: 회차 커밋 수 ↔ 충돌의 상관계수 ★**부호가 «음»**. + ★★**[게이트② 정정] 초판 표는 base·(델타/누적) 병기가 없어 기준이 섞여 있었다** ⇒ 정본을 **«누적»**으로 통일해 재계산: + A 초판 **r=−0.169·합28** · B S4 정정 **r=−0.134·합30** · ★**C 정본 r=−0.155·합33**(**37커밋 → 14** ↔ **8커밋 → 19**). + ★**세 열 모두 부호가 음 ⇒ 판정 불변** · ★**r 은 n=8 비무작위 표본이라 «부호 판정»으로만 쓴다**. + ★**누적 충돌도 포화한다**: 고정 base 에서 **7커밋 16 → 33커밋 19**(26커밋 더 벌려도 **+3**). + ⇒ ★**충돌을 만든 것은 «몇 개 왔는가»가 아니라 «무엇이 왔는가»**(우리 포크 지점 3곳 + 개명 스윕 같은 사건). + ★**그런데 결론이 「아무것도 안 한다」가 되지는 않았다** — 반증된 것은 «비용이 격차에 비례한다»는 기전이고, + 「아무도 챙기지 않는다」는 위험은 그대로 참이다. ⇒ **축은 두되 임계를 포화점 «뒤»에** 놓았다. + ★**시간 기반 기각**: upstream 이 **37% 의 주에 커밋 0**(12개월 144커밋/51주 · 중앙 **1/주**)이라 시간 축은 + ★**behind 0 인데 회차를 여는 «빈 회차»** 를 만들고, 회차 오버헤드(**티켓 3건**)가 고정이라 자주 받으면 **총비용이 는다**. + ★**사건 기반 기각**: S3 충돌 **델타 +9**(누적 **11** · base S2 착지본 · merge-base `af4f6f8`)는 upstream **«신규» 파일**에서, + S8 **누적 8 · 델타 +8**(base `3fb08a8` · merge-base `ba5797b`)은 **전 트리 개명**에서 왔다 ⇒ 경로 트리거가 대부분을 놓친다. + ★**임계 20 근거**: 아래로는 **7커밋 포화점**, 위로는 **33 이 미측정 구역 입구**. + ★★**[정정] 초판의 「실제로 아팠던 값」은 «지웠다»** — 그것은 이 판정 자신이 «금지한» 독법(8회차를 격차의 비용으로 읽는 것)이라 자기모순이었다. + ★★**「20」은 밴드(7~33) 안에서 «고른» 수다** — 밴드는 실측이고 그 안 누적이 `16→17→17→17→19` 로 **평평**하다. + 실측 도달 **중앙 44일**(★상수 인용 금지 — 재측 **41일** · 창 정의 ±3일 · 결론 영향 0) ⇒ **연 6~8회** · + 회차당 충돌(정본 누적) **중앙 2.5** · 게이트② 사이클 **8회차 중 7회가 1**. + ★**방아쇠 = 기계가 «재고» 사람(총괄)이 «발권»** — 재는 자리 권고는 **이 repo 의 예약 워크플로**(선례 `rust-audit.yaml`), + 대안 `bin/healthcheck` 는 **orchestrator 소관이라 별건**. ★**구현은 이 회차가 하지 않았다.** + ★**재측정 조건**(3회차 뒤 · 충돌 중앙 ≥5 면 임계 내리고 · 도달 간격 ≤14일이면 올린다)을 §5-B 에 오늘의 값과 함께 박았다. + ★★**「오래됐다」는 재개 사유가 아니다 — 이 축은 «시간»이 아니라 «격차»로 열린다.** + ★게이트③ 완료: PR #29 — ★★**`--merge` 착지**(★`--squash` 아님 · 등재 repo `contracts/upstream-sync-repos.conf:22`). + 머지커밋 sha 는 회신 `merged:` 참조. ★**게이트② 3회차 만에 approve**(초판 → fix → fix2) — + ★**반려 둘이 모두 «수를 어떻게 적었는가»였다**: ⑴base·(델타/누적) 병기 없이 기준을 섞었다 ⑵그 정정이 «표»에만 닿고 + «산문·`summary`»로 전파되지 않았다. ⇒ ★**「표를 고쳤다고 문서가 고쳐진 것이 아니다」**가 이 리니지가 남긴 규율이다. + ★**부모 #27·#28 착지로 두 번 `CONFLICTING` 이 됐고 그때마다 머지로 해소**했다(마지막은 `REPORT.md` 1건) — + ★**충돌 중에는 `pull_request` 검사가 «구조적으로» 못 돈다**(검사 1건 → 10건으로 확증). - [rustjava-dod-ci-parity-sibling-repo-survey] ★★**형제 repo(`wie`·`qts`) 파리티 락 필요성 «조사+판정» — 둘 다 «⒞ 조건부 필요».** 채택 제안 `2026-09-04-dod-ci-parity-lock#p1`. ★**읽기 전용**(형제 repo `git`·PR·파일 수정 **0** · `gh api /contents` 로만 읽었다) · 구현 **0**. ★★**검사기는 «포팅 불가»다** — `wie` 는 DoD 정본이 **`AGENTS.md`** 이고 4번째 게이트(`cargo test`)가 diff --git a/docs/upstream-sync-approach.md b/docs/upstream-sync-approach.md index 11fbb28..c442a5b 100644 --- a/docs/upstream-sync-approach.md +++ b/docs/upstream-sync-approach.md @@ -899,6 +899,124 @@ behind ★**12 → 0** · 머지커밋 **부모 2개**(`3fb08a8` + `bd42427`). --- +## 5-B. ★★★[2026-09-04 판정] 다음 동기는 «언제» — **격차 기반 `behind ≥ 20`** + +채택 제안 `2026-09-04-sync-contract-stale-assets-decision#p0` 에 대한 **결정**이다. +★**S1~S8 이 behind 33 → 0 으로 캠페인을 닫았고, 「다음에 언제 받을지」가 비어 있었다.** + +> ★**동기 회차는 `git rev-list --count origin/main..upstream/main` 이 «20 이상»일 때 연다.** +> ★**시간(격주·월간)으로 열지 않는다.** ★**그 아래에서는 열지 않는다** — 아래 실측이 그 이유다. + +### ⒜ ★★「벌어진 뒤에 받으면 훨씬 비싸다」는 이 저장소 데이터가 «반증»한다 + +★★**[2026-09-04 정정 · 게이트②] 초판 표는 «맨 수» 8행이었다 — 이 파일의 상시 규칙(`§5` 「충돌 수를 적을 때는 +base 를 반드시 병기한다」)을 «이 표가» 어겼고, 실제로 기준이 섞여 있었다**(S3·S6 은 델타 · S4 는 ★**계획서 «예측»** · S8 은 누적). +⇒ ★**정본 열을 «누적»으로 통일하고 base·기준을 병기한다.** + +★**정본 = «누적»**(그 회차의 base 에서 «실제로 열린」 파일 수 = 「이번 회차가 푼 것」) — +★**착지 커밋 제목이 그 값의 1차 사료**다(`git log --first-parent`) — ★**단 «S1~S7» 에 한한다.** +★★**[2026-09-04 정정 · 게이트②] `S8` 은 예외다** — 착지 커밋 `a76b305` 의 제목·본문에 ★**충돌 수가 «0건»** 이다. +그 칸의 값 **8** 의 출처는 ★**직접 재측정**이고(`git merge-tree --write-tree --name-only 3fb08a8 bd42427` — +`Cargo.lock` 1 + 개명 유발 file-location 6 + `thread.rs` 1), ★**값은 옳다. 좁힌 것은 «사료 출처의 범위»뿐이다.** + +| 회차 | 커밋 | ★**누적**(정본) | 델타(§5 제안표) | base · merge-base | 성격 | +|---|---|---|---|---|---| +| S1 | 5 | **2** | 2 | `03438b0` · `62cf0c6a` | 설계(tracing) | +| S2 | 1 | **5** | +5 | S1 착지본 · `1f356ae` | 설계(charset) | +| S3 | 1 | ★**11** | +9 | S2 착지본 · `af4f6f8` | 설계(오류 분류) | +| S4 | 8 | ★**2** | ~~0(★**계획서 «예측»**)~~ | `3a59776`(복원 후) · `822504b` | 물량 | +| S5 | 6 | **3** | +3 | `8c1238b` · `3296139c` | 물량 | +| S6 | ★**11** | ★**1** | +0 | `a0b5d3c` · `c4665b0` | 물량 | +| S7 | 1 | **1** | +1 | `3e02f8c` · `95ebc5c` | 물량+ | +| S8 | ★**12** | **8** | +8 | `3fb08a8` · `ba5797b` | 개명 스윕 | + +★★**정정 셋을 «숨기지 않는다»**: +- ★**S4 = `0` 은 «실측이 아니라 계획서 예측»이었다**(`reports/…-s4.done.md`: 예측 **0** / 복원 «전» 실측 **20** / 복원 «후» 실측 **2**). + §5 제안표의 그 칸만 **한 번도 정정되지 않았고**, ★**같은 파일의 정정 블록은 이미 `S4 20 → 2` 를 적어 두고 있었다.** + ★**하필 그 칸이 논증의 «최강 데이터»(「8커밋인데 충돌 0」)였다** ⇒ **실측 `2` 로 고친다.** +- ★**S6 = `0` 은 «델타»다 — 누적은 `1`**(`string.rs`). ★**§5 가 그 오독을 «실사고»로 이름 붙인 바로 그 자리다.** +- ★**S3 = `+9` 는 델타 · 누적은 `11`**(착지 커밋 제목 「충돌 11」). + +★★**세 열을 «다 계산해» 싣는다 — 부호가 안 흔들린다는 것이 요지다**(내가 다시 계산했다): + +| 열 | 값(S1…S8) | r | 합 | 중앙 | 많은4(37커밋) | 적은4(8커밋) | +|---|---|---|---|---|---|---| +| A 초판(기준 혼재) | 2·5·9·0·3·0·1·8 | **−0.169** | 28 | 2.5 | 11 | 17 | +| B S4 만 정정 | 2·5·9·2·3·0·1·8 | **−0.134** | 30 | 2.5 | 13 | 17 | +| ★**C 정본 «전부 누적»** | **2·5·11·2·3·1·1·8** | ★**−0.155** | **33** | **2.5** | ★**14** | ★**19** | + +★★**세 열 모두 r 의 부호가 «음»이고 「많은4 < 적은4」가 유지된다** ⇒ ★**판정은 흔들리지 않는다.** +★★**단 r 을 «정밀 추정치»로 읽지 마라 — n=8 이고 회차 경계가 «컷 분할»로 정해진 비무작위 표본이다.** +★**이 수는 «비례하지 않는다»는 «부호 판정»으로만 쓴다**(초판이 그 열로 「우상향」을 읽은 것과 **같은 방법**이라 +방향만 반대다 — 그래서 강하게 읽지 않는다). + +★**정본 기준(C)으로도**: 커밋 많은 4회차(**37커밋**)가 충돌 **14** · 적은 4회차(**8커밋**)가 충돌 **19**. +⇒ ★★**충돌을 만든 것은 «몇 개 왔는가»가 아니라 «무엇이 왔는가»다** — 우리 포크 지점 +(tracing span · charset 라우팅 · 오류 분류)을 upstream 이 건드릴 때, 그리고 개명 스윕 같은 **사건**이 올 때. + +★**누적 충돌도 «포화»한다**(§5 첫 표): 7커밋 **16** → 10커밋 17 → 21커밋 17 → 32커밋 17 → 33커밋 **19**. +⇒ ★**7커밋 뒤로 26커밋을 더 벌려도 충돌이 «+3»** 이다. 「격차가 크면 비용이 는다」의 흔한 기전이 **여기엔 없다**. + +### ⒝ ⇒ 그래서 «작게 자주»가 총비용을 낮추지 «않는다» + +★**회차 오버헤드는 «고정»이다** — 회차당 티켓 **3건**(구현 · 게이트② · 게이트③). +게이트② 사이클 실측: **8회차 중 7회가 1사이클**(S4 만 3). +⇒ ★**충돌 총량이 포화하는데 회차 수만 늘면 «총비용이 는다».** 시간 기반(⒜)을 기각한 이유가 이것이다. +★**그리고 upstream 이 느리고 버스티하다**(아래 ⒞) — 시간 축은 ★**behind 0 인데 회차를 여는 «빈 회차»** 를 만든다. + +★**「8회차가 들었다」를 «격차 33 의 비용»으로 읽지 마라** — 회차 경계는 §5 의 **컷 분할**이 정했고, +추가 비용의 큰 덩어리는 ★**스쿼시로 계보가 접힌 것**이었다(별 리니지 `…-squash-defeats-convergence`). +그것은 격차가 아니라 **머지 방식**의 비용이고, `merge_strategy: merge` 로 이미 닫혔다. + +### ⒞ upstream 속도 실측(12개월 · `upstream/main`) + +**144 커밋 / 51주** — 주당 **평균 2.81 · 중앙 1.0 · p90 7** · ★**커밋이 «0»인 주가 19주(37%)**. +누적 도달 시간(연속 창 실측): behind **5** 중앙 **8일** · **10** 중앙 **21일** · **20** 중앙 ★**44일** · **33** 중앙 83일. +⇒ ★**`behind ≥ 20` 은 실질 «약 6주 · 연 6~8회»** 다. +★★**「44일」을 «상수»로 인용하지 마라** — 게이트② 재측은 **41일**이었고 차이는 **창 정의**(어느 12개월을 쓰나)에서 온다(±3일). +★**결론 영향은 0**(어느 값이든 「약 6주」다) — 그러나 인용할 때는 **그 창을 함께 적어라**. + +### ⒟ 왜 «20» 인가 — 위아래를 둘 다 적는다 + +- **아래로 내리지 않는 이유**: 충돌이 **7커밋에서 포화**하므로 그보다 자주 받아도 충돌 총량이 안 준다. + 더 자주 받으면 회차 수(= 티켓 3건씩)만 는다. +- **위로 올리지 않는 이유**: ★**포화는 «7~33 구간»에서만 측정됐다 — 33 너머는 «미측정»** 이다 + (§5 첫 표의 상한이 33 이다). ★**미측정 구역으로 걸어 들어가지 않는다.** + ★★**[2026-09-04 정정 · 게이트②] 초판은 여기에 「33 은 실제로 아팠던 값이다」를 함께 적었다 — «지웠다».** + ★**그것은 바로 위 ⒝ 가 «금지한» 독법이다**(「8회차가 들었다」를 격차의 비용으로 읽는 것) ⇒ **같은 절 안의 자기모순**이었다. + ★**「미측정」 논거는 혼자 선다** — 아픔을 끌어올 필요가 없다. +- ★**그리고 «늦게 알수록» 우리 자산 낡음이 오래 방치된다** — S8 개명 스윕이 `rust.yml` 의 크레이트 이름과 + 경로 4곳을 낡게 만들었고, 그 여파가 **문서 정정 5회차**로 이어졌다(§4 리니지). + +★★**[2026-09-04 명시 · 게이트②] 「20」은 «밴드 안에서 «고른» 수»다 — 데이터가 그 한 점을 지목하지 않는다.** +★**지어낸 수가 «아니다»**: **밴드**가 실측이다(하한 = 포화 시작 **7** · 상한 = 측정 상한 **33**). +★**그리고 그 밴드 안 누적이 `16 → 17 → 17 → 17 → 19` 로 «평평»해 어디를 골라도 데이터는 같은 말을 한다.** +⇒ ★**「20 을 골랐다」** — 근거는 «도달 중앙 ≈6주가 회차 오버헤드(티켓 3건)와 균형」이라는 ★**판단**이고, +★**⒡ 가 3회차 뒤 재측정을 걸어 그 판단을 검증한다.** ★**밝히는 것이 논증을 약화시키지 않는다** — ⒡ 가 그 전제 위의 장치다. + +### ⒠ ★누가 방아쇠를 당기는가 — **기계가 «재고», 사람(총괄)이 «발권한다»** + +```sh +git fetch -q upstream && git rev-list --count origin/main..upstream/main # ★오늘의 값 = 0 +``` +- ★**재는 자리(권고) = 이 repo 의 «예약 워크플로»** — 선례가 있다(`rust-audit.yaml` 이 이미 `schedule: cron`). + upstream 을 **read-only fetch** 해 그 수를 찍고 임계 초과 시 **눈에 보이게** 알린다. ★**upstream 발신 0**(fetch 는 읽기다). +- ★**대안 = 총괄 레인의 `bin/healthcheck` 축**(그 레인에 이미 체크아웃이 있다). + ★**그것은 `orchestrator` repo 소관이라 이 저장소가 정할 수 없다** ⇒ **별건 판정**으로 넘긴다. +- ★★**발권은 «사람(총괄)»이 한다** — 기계가 자동으로 동기 회차를 열지 않는다. + 이유: 회차마다 **설계 판단**(우리 포크 지점 유지 여부)이 들어가고, 그것은 S1·S2·S3 에서 실제로 필요했다. +- ★**구현은 «별건»이다 — 이 판정 회차에서 만들지 않았다**(검사기·크론·워크플로 신설 **0**). + +### ⒡ ★재개(재측정) 조건 — 세는 법과 오늘의 값 + +> ★**새 축으로 «3회차»를 돈 뒤 다시 재라.** 임계 20 은 «시작값»이지 상수가 아니다. + +재잴 것 셋: ⑴임계 도달 간격(실제 일수) ⑵회차당 새 충돌 ⑶게이트② 사이클 수. +★**⑵의 중앙이 «5 이상»으로 오르면 임계를 내려라**(포화 가정이 깨진 것) · +★**⑴이 «14일 이하»로 짧아지면 임계를 올려라**(upstream 이 빨라진 것 — 지금은 중앙 44일). +★**오늘의 값**: behind **0** · upstream HEAD `bd42427`(2026-08-31) · 마지막 착지 `61d5bf3f`. +★★**「오래됐다」는 재개 사유가 «아니다»** — 이 축은 **시간이 아니라 격차**로 열린다. + ## 6. 이 접근안이 **하지 않는** 것 - upstream(`dlunch/RustJava`) 발신 0. `classfile::ClassFileError` 에 진단 변형을 되돌려 넣는 안은 diff --git a/docs/worklog/2026-09-04-upstream-sync-cadence-decision.json b/docs/worklog/2026-09-04-upstream-sync-cadence-decision.json new file mode 100644 index 0000000..0d78606 --- /dev/null +++ b/docs/worklog/2026-09-04-upstream-sync-cadence-decision.json @@ -0,0 +1,61 @@ +{ + "schema": 1, + "date": "2026-09-04", + "title": "upstream 동기 «정기 축» 판정 — 격차 기반 behind ≥ 20 (시간 기반 기각 · 「벌어지면 비싸다」는 반증됐다)", + "services": [ + "RustJava" + ], + "taskId": "rustjava-upstream-sync-cadence-decision", + "summary": "채택 제안 2026-09-04-sync-contract-stale-assets-decision#p0 에 대한 판정이다. 결론: 동기 회차는 git rev-list --count origin/main..upstream/main 이 «20 이상»일 때 연다 — ⒝ 격차 기반. 시간(격주·월간) 기반은 기각했다. ★전제를 먼저 쟀고 그 결과가 제안의 전제를 반증했다: 회차별 커밋 수 ↔ 충돌의 상관계수는 부호가 «음»이다 — ★[게이트² 정정] 초판 표가 base·(델타/누적) 병기 없이 기준을 섞었으므로 정본을 «누적»으로 통일해 다시 계산했다: A 초판(혼재) r=−0.169 합 28 · B S4 만 정정 r=−0.134 합 30 · ★C 정본 전부 누적 r=−0.155 합 33. 세 열 모두 부호가 음이고 「많은4 < 적은4」가 유지돼 판정은 흔들리지 않는다(정본 C: 37커밋 → 14 ↔ 8커밋 → 19). ★단 r 은 n=8 비무작위 표본이라 «비례하지 않는다»는 부호 판정으로만 쓴다. 누적 충돌도 포화한다 — 고정 base 에서 7커밋 16 → 33커밋 19 로, 26커밋을 더 벌려도 +3 이다. ⇒ 충돌을 만든 것은 «몇 개 왔는가»가 아니라 «무엇이 왔는가»(우리 포크 지점 tracing span·charset 라우팅·오류 분류를 건드리는 변경, 그리고 개명 스윕 같은 사건)다. ★시간 기반 기각 근거 둘: upstream 이 37% 의 주에 커밋 0 이라 시간 축은 behind 0 인데 회차를 여는 «빈 회차»를 만들고, 충돌이 포화하는데 회차 오버헤드(티켓 3건/회차)는 고정이라 자주 받으면 총비용이 «는다». ★사건 기반도 기각 — S3 의 충돌 «델타 +9»(누적 11 · base S2 착지본 · merge-base af4f6f8)는 upstream «신규» 파일에서 왔고, S8 의 «누적 8 · 델타 +8»(base 3fb08a8 · merge-base ba5797b)은 전 트리 개명이라 경로 기반 방아쇠가 비용의 대부분을 놓친다. ★[게이트² 정정] 초판은 두 수를 «맨 수»로 적어 이 저장소의 상시 규칙(approach.md §5 「병기 없는 충돌 수는 문서·회신·티켓 어디에도 쓰지 않는다」·「델타인가 누적인가도 밝혀라」)을 어겼다 — 같은 워크로그 .md 표는 정확히 병기하고 있었으므로 «한 파일 안에서 갈렸다». ★정기 축 미설치도 기각 — 오늘 아무것도 알려 주지 않고, 포화는 7~33 구간에서만 측정됐다(★33 너머는 미측정 구역이다). ★[게이트² F4 정정] 초판 summary 는 여기에 「33 은 실제로 아팠던 값이다」를 «기각의 논거»로 적었다 — «지웠다». 그것은 이 판정 자신이 금지한 독법(8회차를 격차의 비용으로 읽는 것)이라 자기모순이었고, approach.md ⒟ 와 STATE.md 에서는 지워 놓고 이 필드에만 살아 있었다. ★「미측정」 논거는 혼자 선다. ★임계 20 의 근거: 아래로는 7커밋 포화점 때문에 내릴 실익이 없고 위로는 33 이 미측정 구역 입구다. 실측 도달 시간 중앙 44일 ⇒ 연 6~8회. ★「44일」을 상수로 인용하지 마라 — 게이트² 재측은 41일이고 차이는 «창 정의»(어느 12개월을 쓰나)에서 온다(±3일). 결론 영향은 0(어느 값이든 「약 6주」)이나 인용할 때는 그 창을 함께 적어라. ★방아쇠: 기계가 «재고» 사람(총괄)이 «발권한다» — 재는 자리 권고는 이 repo 의 예약 워크플로(선례 rust-audit.yaml 이 이미 schedule: cron), 대안은 총괄 레인 bin/healthcheck 인데 그것은 orchestrator repo 소관이라 별건이다. 구현은 이 회차에서 하지 않았다.", + "changes": [ + "docs/upstream-sync-approach.md §5-B 신설 — 판정문 · 회차별 비용표와 상관계수 · 누적 충돌 포화 · upstream 속도 실측 · 임계 20 의 위아래 근거 · 방아쇠 주체 · 재측정 조건과 오늘의 값. ★동기 회차가 반드시 읽는 계약 파일이고 §5(어디서 자를까)와 §4(우리 자산 낡음) 사이에 두어 「어디서 자를까 / 언제 열까」를 같은 자리에 모았다.", + "docs/worklog/2026-09-04-upstream-sync-cadence-decision.{md,json} 신설.", + "STATE.md · REPORT.md — 회차 기록.", + "★검사기·크론·워크플로 신설 0 · .rs 0줄 · allow 9곳 무접촉 · rust.yml 무접촉 · CLAUDE.md DoD 블록 무접촉 · S9 미개시.", + "★[fix · 게이트² request-changes 승계 · 표기만] F1 §5-B⒜ 표에 base·(델타|누적) 병기 + 「r 은 부호 판정으로만 읽어라」 · F2 S4 칸을 계획서 «예측 0» → ★실측 «2»(복원 전 20 / 후 2)로 정정하고 세 열(A/B/C) 재계산표 첨부 · F3 S6 「0」이 델타임을 명시하고 누적 «1» 병기(§5 가 실사고로 이름 붙인 자리) · F4 ⒟ 의 「33 은 실제로 아팠던 값」 삭제(⒝ 가 금지한 독법이라 자기모순) — 「미측정 구역」 논거만 남김 · F5 「20 을 밴드 안에서 «골랐다»」 명시 + 「44일」 상수 인용 주의(재측 41일 · 창 정의 ±3일 · 결론 영향 0).", + "★[fix2 · 게이트² request-changes 승계] F4 잔존 해소 — .json summary 에 정정 표지 «밖»으로 살아 있던 「33 은 실제로 아팠던 값이다」를 지우고 표지를 달았다(전수 5건 중 4건은 표지 안이었고 이 한 자리만 밖이었다 · 그 필드는 다음 회차가 통째로 인용하는 표면이다). 상시 규칙 전파 — summary 의 「S3 충돌 9건」·「S8 8건」을 델타/누적 + base·merge-base 병기로, 「중앙 44일」에 창 정의 주의(41일·±3일)를 붙였다. 워크로그 .md 의 같은 문장(사건 기반 기각 절)도 함께 병기했다. approach.md 의 「착지 커밋 제목이 1차 사료」 문면을 «S1~S7» 로 좁히고 S8 은 별도 출처(merge-tree 재측)임을 명시했다. 그리고 부모 #28 착지로 생긴 REPORT.md 충돌을 머지로 해소했다." + ], + "deploy": { + "sha": "", + "urls": [] + }, + "verification": "★전제 실측: git rev-list --count origin/main..upstream/main → 0(요구된 값) · git merge-base origin/main upstream/main → bd42427 = upstream HEAD(2026-08-31) · 마지막 착지 61d5bf3f. ★과거 비용 실측(§5 표 · 게이트②가 검증한 수): S1 5커밋/충돌 2 · S2 1/5 · S3 1/9 · S4 8/0 · S5 6/3 · S6 11/0 · S7 1/1 · S8 12/8 — 합 45커밋 / 28충돌(중앙 2.5). 상관계수 r = −0.169. 커밋 많은 4회차(37커밋) 충돌 11 ↔ 적은 4회차(8커밋) 충돌 17. 누적 충돌 포화(§5 첫 표 · 고정 base): 7커밋 16 → 10커밋 17 → 21커밋 17 → 32커밋 17 → 33커밋 19. 게이트② 사이클: reports/ 의 rustjava-upstream-sync-s*.review.md 줄1 전수 — 8회차 중 7회가 approve 1사이클, S4 만 3(초판 request-changes → fix request-changes → fix2 approve). ★upstream 속도 실측(최근 12개월 upstream/main): 144커밋 / 51주 · 주당 평균 2.81 · 중앙 1.0 · p90 7 · 커밋 0인 주 19주(37%). 누적 도달 시간(연속 창): behind 5 중앙 8일 · 10 중앙 21일 · 20 중앙 44일(최소 9 · 최대 139) · 33 중앙 83일. ★회차 종료 상태: python3 scripts/check-dod-ci-parity.py rc=0 · python3 scripts/check-worklog-json.py rc=0 · .rs 변경 0 · git status 에 코드 파일 0.", + "issues": [ + "★★제안의 전제(「벌어진 뒤에 받으면 훨씬 비싸다」)가 이 저장소 데이터로 반증됐다 — r = −0.169 이고 누적 충돌은 7커밋에서 포화한다. ★그런데 결론이 「아무것도 안 한다」가 되지는 않았다: 반증된 것은 «비용이 격차에 비례한다»는 기전이고, 제안이 지목한 «아무도 챙기지 않는다»는 위험은 그대로 참이기 때문이다. 그래서 축은 두되 임계를 포화점보다 «뒤»에 놓았다.", + "★「8회차가 들었다」를 «격차 33 의 비용»으로 읽으면 안 된다. 회차 경계는 계획서 §5 의 컷 분할이 정했고, 추가 비용의 큰 덩어리는 스쿼시로 계보가 접힌 것(별 리니지 …-squash-defeats-convergence)이다. 그것은 격차가 아니라 머지 방식의 비용이고 merge_strategy: merge 로 이미 닫혔다. ⇒ 캠페인 규모를 근거로 임계를 낮추는 논증은 성립하지 않는다.", + "★포화는 «7~33 구간»에서만 측정됐다 — 33 너머는 미측정이다. 임계를 20 위로 올리려면 그 구역을 먼저 재야 하고, 재는 방법은 사실상 «그렇게 벌린 뒤 겪어 보는 것»뿐이라 비용이 비대칭이다. 그래서 올리지 않았다.", + "★사건 기반(경로 트리거)을 기각한 근거를 남긴다 — 그럴듯해 보이지만 S3 의 충돌 «델타 +9»(누적 11 · base S2 착지본 · merge-base af4f6f8)는 upstream «신규» 파일에서 왔고(우리 파일이 아니다) S8 의 «누적 8 · 델타 +8»(base 3fb08a8 · merge-base ba5797b)은 전 트리 개명이었다. 경로 기반은 실제 비용의 대부분을 놓친다. 다음 사람이 이 갈래를 다시 제안하지 않게 적는다.", + "★방아쇠의 «발권»은 사람(총괄)에게 남겼다 — 기계가 자동으로 동기 회차를 열지 않는다. 회차마다 설계 판단(우리 포크 지점을 유지할지)이 들어가고 S1·S2·S3 에서 실제로 필요했기 때문이다. 기계는 «재는 것»까지다.", + "★재는 자리의 대안(총괄 레인 bin/healthcheck)은 orchestrator repo 소관이라 이 저장소가 정할 수 없다 — 별건 판정으로 넘겼다. 이 repo 의 예약 워크플로가 «이 저장소가 정할 수 있는» 유일한 자리다.", + "★임계 20 은 시작값이지 상수가 아니다. 재측정 조건(3회차 뒤 · 회차당 충돌 중앙 ≥5 면 내리고 · 도달 간격 ≤14일이면 올린다)을 §5-B 에 세는 법과 오늘의 값(behind 0)과 함께 박았다. ★「오래됐다」는 재개 사유가 아니다 — 이 축은 시간이 아니라 격차로 열린다.", + "머지하지 않았다 — 게이트②·③은 별 세션이다. ★게이트③은 등재 repo 라 merge_strategy: merge 필수.", + "★★[F1·F2·F3] 초판 §5-B⒜ 표 8행이 «전부 맨 수»였고 실제로 기준이 섞여 있었다 — 이 저장소의 상시 규칙(approach.md §5 「충돌 수엔 base 를 병기한다」)을 그 표가 어겼다. ★그리고 §5 자신이 「기준을 섞으면 없는 계열이 만들어진다」를 이미 규탄했다 — 초판은 그 열로 «우상향»을, §5-B 는 같은 열로 «상관계수»를 읽었다. 방향만 반대이고 방법은 같다. ⇒ 정본을 «누적»으로 통일하고 base·merge-base 를 병기했다.", + "★★[F2] S4 칸의 «0» 은 실측이 아니라 «계획서 예측»이었다 — reports/…-s4.done.md 원문은 「예측 0 / 복원 전 실측 20 / 복원 후 실측 2」다. §5 제안표의 그 칸만 한 번도 정정되지 않았고(S5·S6·S7 은 취소선+화살표로 반영됨), 같은 파일의 정정 블록은 이미 S4 20 → 2 를 적어 두고 있었다. ★하필 그 칸이 논증의 «최강 데이터»(8커밋인데 충돌 0)였다.", + "★[F4] ⒟ 의 「33 은 실제로 아팠던 값이다」는 §5-B⒝ 가 «금지한» 독법 위에 서 있었다 — 같은 절 안의 자기모순이라 지웠다. 「33 너머는 미측정 구역」 논거는 혼자 서고 결론은 안 바뀐다.", + "★[F5] 「20」은 밴드(7~33) 안에서 «고른» 수인데 「골랐다」가 적혀 있지 않았다. 밴드는 실측이고 그 안 누적이 16→17→17→17→19 로 평평해 어디를 골라도 데이터는 같은 말을 한다 — 고른 근거는 「도달 중앙 ≈6주 ↔ 회차 오버헤드 3티켓의 균형」이라는 판단이고 재측정 조건이 그것을 검증한다.", + "★[※] 「44일」을 상수로 인용하지 마라 — 게이트² 재측은 41일이고 차이는 창 정의(어느 12개월)에서 온다(±3일). 결론 영향 0 이지만 인용 시 창을 함께 적어라. ★upstream 속도표 자체는 다시 재지 않았다(티켓 non-goal).", + "★★[F4 잔존 · 이 반려의 축] 직전 회차 회신이 「전수 검색으로 정정 표지 밖 잔존 0」이라 «결론만» 적었는데 그것이 반증됐다 — 열거는 워크로그 .md 뿐이었고 .json summary 가 빠져 있었다. ★반려 사유가 「예측값을 실측처럼 적었다」였는데 승계가 «수행하지 않은 검증을 수행했다고 적었다» = 같은 결함 계열의 재발이다. ⇒ 이 회차부터 「전수」 주장에는 «명령 + 출력»을 반드시 싣는다.", + "★[상시 규칙] F1·F3·F5 의 수정이 approach.md «표»에만 적용되고 워크로그 «산문·summary»로 전파되지 않았다 — 같은 파일 안에서 병기 방식이 갈렸다(.md 표는 「S3 +9 델타 · 누적 11」로 정확했는데 .md 산문과 .json summary 는 맨 수 9였다). ★표를 고쳤다고 문서가 고쳐진 것이 아니다.", + "★[문면 좁힘] 「착지 커밋 제목이 그 값의 1차 사료」는 S8 에서 성립하지 않는다 — a76b305 의 제목·본문에 충돌 수가 0건이다. 값 8 자체는 옳다(검수자가 git merge-tree --write-tree --name-only 3fb08a8 bd42427 로 직접 재서 확인: Cargo.lock 1 + 개명 유발 file-location 6 + thread.rs 1). ⇒ 고친 것은 «사료 출처의 범위»뿐이고 값은 무접촉이다.", + "★[충돌 해소] 부모 #28 이 18fbb6eb 로 착지하며 REPORT.md 가 충돌했다(STATE.md·approach.md 는 auto-merge). ★auto-merge 된 2파일을 «눈으로» 확인했다 — 충돌이 안 났다고 의미가 보존된 것은 아니다(이 집이 이름 붙인 함정). 리베이스·force-push 0.", + "★[게이트③ 집행] PR #29 를 --merge 로 착지시켰다(등재 repo — 스쿼시는 계보를 접는다). 동봉 «전» head 가 검수 핀 c20dd55e1f1def5b0ca81fa59ffb0c6e23c81626 과 일치함을 확인했고(git fetch 를 먼저 쳐 거짓 일치를 막았다), 동봉 전/후 sha 는 머지 회신에 적었다. 착지 diff 5파일 전건 문서 · 배포 워크플로 0개 ⇒ 배포 0 · 코드(.rs) 변경 0. ★이 리니지는 부모 #27·#28 착지로 두 번 CONFLICTING 이 됐고 그때마다 머지로 해소했다 — 리베이스·force-push 0." + ], + "proposals": [ + { + "title": "behind 를 «재는» 자리를 만들어라 — 이 repo 의 예약 워크플로 1개(발권은 사람이 유지)", + "plainSummary": "원본 저장소와 얼마나 벌어졌는지를 자동으로 세어 알려 주는 장치다. 지금은 사람이 생각날 때만 세고, 그래서 이번 캠페인이 33까지 벌어진 뒤에야 시작됐다.", + "userBenefit": "격차가 정해진 선(20)을 넘는 순간 눈에 보이므로, 「아무도 챙기지 않아 다시 33이 된다」가 구조적으로 막힌다. 세는 것만 자동이고 회차를 열지 말지는 사람이 정하므로 원치 않는 자동 작업이 생기지 않는다.", + "why": "이 판정이 임계를 정했지만 그 수를 «재는 주체»가 오늘 없다. 선례가 이미 있다 — rust-audit.yaml 이 schedule: cron 으로 돈다. upstream 을 read-only fetch 해 rev-list --count 를 찍는 것이 전부라 upstream 발신 0 이다.", + "tradeoff": "⑴워크플로가 하나 늘고 주기적으로 러너를 쓴다(가벼운 fetch + count 라 초 단위). ⑵임계 초과를 «어떻게» 알릴지가 별 선택이다 — job 실패는 눈에 띄지만 main 을 빨갛게 만들고, 이슈 생성은 조용하지만 권한이 필요하다. ⑶대안으로 총괄 레인 bin/healthcheck 에 붙이면 워크플로가 안 늘지만 그것은 orchestrator repo 소관이라 여기서 정할 수 없다.", + "effort": "XS~S — 워크플로 1개. 알림 방식 선택이 결정의 대부분이다.", + "target": ".github/workflows/ · 또는 orchestrator bin/healthcheck(별 소관)" + } + ], + "resolvedIssues": [ + "2026-09-04-sync-contract-stale-assets-decision#p0 — 「upstream 동기를 «정기 축»으로 둘지 판정하라 — behind 0 인 지금이 적기」. 전제(격차 ↔ 비용)를 실측해 반증하고, 그럼에도 «아무도 챙기지 않는다»는 위험이 참이므로 축을 두되 임계를 포화점 뒤(behind ≥ 20)에 놓는 것으로 판정했다. 방아쇠 주체(기계가 재고 사람이 발권)와 재측정 조건을 docs/upstream-sync-approach.md §5-B 에 박았다." + ], + "adoptedProposals": [ + "2026-09-04-sync-contract-stale-assets-decision#p0" + ], + "declinedProposals": [] +} diff --git a/docs/worklog/2026-09-04-upstream-sync-cadence-decision.md b/docs/worklog/2026-09-04-upstream-sync-cadence-decision.md new file mode 100644 index 0000000..0ae6cfd --- /dev/null +++ b/docs/worklog/2026-09-04-upstream-sync-cadence-decision.md @@ -0,0 +1,171 @@ +# [2026-09-04] upstream 동기 «정기 축» 판정 — ★**격차 기반 `behind ≥ 20`**(시간 기반 기각) + +**티켓**: `rustjava-upstream-sync-cadence-decision` +**채택 제안**: `2026-09-04-sync-contract-stale-assets-decision#p0` +**성격**: ★**판정 회차** — 검사기·크론·워크플로 신설 **0** · `.rs` **0줄** · `allow` 9곳 무접촉 · ★**S9 미개시** + +--- + +## 1. ★전제 실측 — 먼저 쟀다 + +### ⒜ 지금의 격차 + +``` +git rev-list --count origin/main..upstream/main → ★0 +git merge-base origin/main upstream/main → bd42427 (= upstream HEAD) +``` +★**behind = 0**(요구된 값) · upstream HEAD `bd42427`(2026-08-31 · bitflags bump) · 마지막 착지 `61d5bf3f`. + +### ⒝ ★과거 비용 — 그리고 그것이 제안의 전제를 «반증»한다 + +회차별 실측(§5 표 그대로 · 게이트②가 검증한 수): + +★★**[게이트² 정정] 초판 표는 «맨 수» 8행이라 이 저장소의 상시 규칙(`approach.md §5` — 「충돌 수엔 base 를 병기한다」)을 +어겼고, 실제로 기준이 섞여 있었다.** ⇒ ★**정본을 «누적»으로 통일하고 base·기준을 병기한다.** + +| 회차 | 커밋 | ★**누적**(정본) | 델타 | base · merge-base | 성격 | 게이트② 사이클 | +|---|---|---|---|---|---|---| +| S1 | 5 | **2** | 2 | `03438b0` · `62cf0c6a` | 설계(tracing) | 1 | +| S2 | 1 | **5** | +5 | S1 착지본 · `1f356ae` | 설계(charset) | 1 | +| S3 | 1 | ★**11** | +9 | S2 착지본 · `af4f6f8` | 설계(오류 분류) | 1 | +| S4 | 8 | ★**2** | ~~0(★**계획서 «예측»**)~~ | `3a59776`(복원 후) · `822504b` | 물량 | ★**3** | +| S5 | 6 | **3** | +3 | `8c1238b` · `3296139c` | 물량 | 1 | +| S6 | ★**11** | ★**1** | +0 | `a0b5d3c` · `c4665b0` | 물량 | 1 | +| S7 | 1 | **1** | +1 | `3e02f8c` · `95ebc5c` | 물량+ | 1 | +| S8 | ★**12** | **8** | +8 | `3fb08a8` · `ba5797b` | 개명 스윕 | 1 | +| **합** | **45** | ★**33**(중앙 **2.5**) | | | | **10**(8회차) | + +★★**정정 셋**: ⑴**S4 `0` 은 계획서 «예측»이었다** — 실측은 **복원 전 20 / 복원 후 2** ⇒ 정본 ★**2** +(★그 칸이 하필 논증의 «최강 데이터»였다) ⑵**S6 `0` 은 «델타» · 누적은 1**(`string.rs` · §5 가 그 오독을 «실사고»로 이름 붙인 자리) +⑶**S3 `+9` 는 델타 · 누적은 11**(착지 커밋 제목 「충돌 11」). + +★★**세 열을 다 계산했다 — 부호가 안 흔들린다는 것이 요지다**: + +| 열 | 값(S1…S8) | r | 합 | 중앙 | 많은4(37커밋) | 적은4(8커밋) | +|---|---|---|---|---|---|---| +| A 초판(기준 혼재) | 2·5·9·0·3·0·1·8 | **−0.169** | 28 | 2.5 | 11 | 17 | +| B S4 만 정정 | 2·5·9·2·3·0·1·8 | **−0.134** | 30 | 2.5 | 13 | 17 | +| ★**C 정본 «전부 누적»** | **2·5·11·2·3·1·1·8** | ★**−0.155** | **33** | **2.5** | ★**14** | ★**19** | + +★★**세 열 모두 부호가 «음»이고 「많은4 < 적은4」가 유지된다** ⇒ ★**판정 불변.** +★★**단 r 을 «정밀 추정치»로 읽지 마라 — n=8 · 회차 경계가 «컷 분할»로 정해진 비무작위 표본이다.** +★**「비례하지 않는다」는 «부호 판정»으로만 쓴다.** +★**정본(C) 기준**: 커밋 많은 4회차(**37커밋**) 충돌 **14** ↔ 적은 4회차(**8커밋**) 충돌 **19**. + +★**누적 충돌은 «포화»한다**(§5 첫 표 · 고정 base 에서 각 컷을 한 번에 머지했을 때): +7커밋 **16** → 10커밋 17 → 21커밋 17 → 32커밋 17 → 33커밋 **19**. +⇒ ★**7커밋 뒤로 26커밋을 더 벌려도 «+3»**. + +⇒ ★★★**「벌어진 뒤에 받으면 훨씬 비싸다」는 이 저장소 데이터로 «반증»된다.** +충돌을 만든 것은 **양이 아니라 «무엇이 왔는가»** — 우리 포크 지점(tracing span · charset 라우팅 · 오류 분류)을 +upstream 이 건드릴 때, 그리고 **개명 스윕 같은 사건**이 올 때다. + +★**「8회차가 들었다」를 «격차 33 의 비용»으로 읽지 마라** — 회차 경계는 §5 의 **컷 분할**이 정했고, +추가 비용의 큰 덩어리는 ★**스쿼시로 계보가 접힌 것**이다(별 리니지 `…-squash-defeats-convergence`). +그것은 격차가 아니라 **머지 방식**의 비용이고 `merge_strategy: merge` 로 **이미 닫혔다**. + +### ⒞ upstream 속도 — 축을 고르려면 이 수가 필요했다(아무도 안 쟀다) + +`upstream/main` 최근 **12개월**: **144 커밋 / 51주** — +주당 **평균 2.81 · 중앙 1.0 · p90 7** · ★**커밋이 «0»인 주가 19주(37%)**. + +| 누적 격차 | 도달까지(연속 창 · 중앙) | 최소 | 최대 | +|---|---|---|---| +| behind 5 | 8일 | 0일 | 70일 | +| behind 10 | 21일 | 0일 | 111일 | +| ★**behind 20** | ★**44일** | 9일 | 139일 | + +★★**「44일」을 «상수»로 인용하지 마라** — 게이트² 재측은 **41일**이고 차이는 **창 정의**(어느 12개월)에서 온다(±3일). +★**결론 영향 0**(어느 값이든 「약 6주」) — 인용할 때 **그 창을 함께 적어라**. ※속도표 자체는 다시 재지 않았다(티켓 non-goal). +| behind 33 | 83일 | 34일 | 169일 | + +## 2. ★판정 — ⒝ **격차 기반 · `behind ≥ 20`** + +> ★**동기 회차는 `git rev-list --count origin/main..upstream/main` 이 «20 이상»일 때 연다.** +> ★**시간(격주·월간)으로 열지 않는다.** ★**그 아래에서는 열지 않는다.** + +### ⒜ ★시간 기반(⒜)을 기각한 이유 — 둘 다 실측이다 + +⑴★**upstream 이 37% 의 주에 커밋 «0»** 이고 버스티하다(p90 7/주) ⇒ 시간 축은 ★**behind 0 인데 회차를 여는 «빈 회차»** 를 만든다. +⑵★**충돌이 포화하므로 «작게 자주»가 총비용을 낮추지 않는다** — 회차 오버헤드는 **고정**이다(티켓 **3건**/회차 · +게이트② 사이클은 **8회차 중 7회가 1**). ⇒ ★**충돌 총량이 그대로인데 회차 수만 늘면 «총비용이 는다».** + +### ⒝ ★사건 기반(⒞)을 기각한 이유 + +「우리 포크 지점 파일을 upstream 이 건드리면」이 그럴듯해 보이지만 ★**데이터가 안 맞는다**: +S3 의 충돌 ★**델타 +9**(누적 **11** · base S2 착지본 · merge-base `af4f6f8`)는 ★**upstream 의 «신규» 파일**에서 왔고(우리 파일이 아니다), +S8 의 ★**누적 8 · 델타 +8**(base `3fb08a8` · merge-base `ba5797b`)은 ★**전 트리 개명**이었다. +★**[게이트² 정정] 초판은 여기서 두 수를 «맨 수»로 적었다** — 같은 파일 §1 표는 정확히 병기하고 있었으므로 ★**한 파일 안에서 갈렸다**. +⇒ ★**경로 기반 방아쇠는 실제 비용의 대부분을 «놓친다».** + +### ⒞ ★정기 축을 두지 않는 것(⒟)을 기각한 이유 + +★**오늘 «아무것도» 알려 주지 않는다.** behind 는 사람이 생각날 때만 재고, 이 캠페인이 그 상태에서 33까지 갔다. +★그리고 ⒝ 의 포화는 **7~33 구간에서만** 측정됐다 — 33 너머는 ★**미측정**이다. +★**[정정] 「33 은 실제로 아팠던 값」은 여기서도 «지웠다»** — §1⒝ 가 금지한 독법이다. ★**「미측정」 논거는 혼자 선다.** +⇒ ★**미측정 구역으로 걸어 들어가지 않는다.** + +### ⒟ ★왜 «20» 인가 — 위아래를 둘 다 적는다 + +- **더 내리지 않는다**: 충돌이 **7커밋에서 포화**하므로 그보다 자주 받아도 충돌 총량이 안 준다(회차 수만 는다). +- **더 올리지 않는다**: 33 은 **미측정 구역의 입구**다(§5 첫 표의 상한). + ★★**[정정] 초판의 「실제로 아팠다」는 «지웠다»** — §1⒝ 가 «금지한» 독법(8회차를 격차의 비용으로 읽는 것) 위에 서 있어 + **같은 문서 안의 자기모순**이었다. ★**「미측정」 논거는 혼자 선다.** +- ★★**[명시] 「20」은 «밴드 안에서 «고른» 수»다** — 밴드(하한 **7** 포화 시작 · 상한 **33** 측정 상한)가 실측이고, + 그 안 누적이 `16→17→17→17→19` 로 **평평**해 어디를 골라도 데이터는 같은 말을 한다. + ⇒ 고른 근거는 「도달 중앙 ≈6주 ↔ 회차 오버헤드 3티켓의 균형」이라는 ★**판단**이고, ★**§5 재측정 조건이 그것을 검증한다.** + ★**밝히는 것이 논증을 약화시키지 않는다.** +- ★**늦게 알수록 «우리 자산 낡음»이 오래 방치된다** — S8 개명 스윕이 `rust.yml` 크레이트 이름과 경로 4곳을 + 낡게 만들었고, 그 여파가 **문서 정정 5회차**로 이어졌다(§4 리니지). +- 실측 도달 시간 **중앙 44일** ⇒ ★**연 6~8회**. + +## 3. ★고른 축의 «비용» + +| 축 | 실측값 | +|---|---| +| 회차당 **새 충돌** | 중앙 **2.5** · 범위 **0~9**(S5~S8 만 보면 3·0·1·8) | +| 회차당 **게이트② 사이클** | **1**(8회차 중 7회) · 최악 **3**(S4) | +| 회차당 **티켓** | **3**(구현 · 게이트② · 게이트③) | +| 연간 회차 | **6~8**(behind 20 도달 중앙 44일) | +| ⇒ **연간 티켓** | **18~24** + 사이클 초과분 | + +★**예측이 아니라 «지금까지의 값»이다.** ⒡ 의 재측정 조건이 이 표를 갱신한다. + +## 4. ★누가 방아쇠를 당기는가 — **기계가 «재고», 사람(총괄)이 «발권한다»** + +```sh +git fetch -q upstream && git rev-list --count origin/main..upstream/main # ★오늘 = 0 +``` + +- ★**재는 자리(권고) = 이 repo 의 «예약 워크플로»**. 선례가 있다 — `rust-audit.yaml` 이 이미 `schedule: cron` 을 쓴다. + upstream 을 **read-only fetch** 해 그 수를 찍고, 임계 초과 시 **눈에 보이게** 알린다. + ★**upstream 발신 0**(fetch 는 «읽기»다 — PR·이슈·코멘트·push 아님). +- ★**대안 = 총괄 레인의 `bin/healthcheck` 축**(그 레인에 이미 체크아웃이 있다). + ★**그것은 `orchestrator` repo 소관이라 이 저장소가 정할 수 없다** ⇒ ★**별건 판정**으로 넘긴다. +- ★★**발권은 «사람(총괄)»이 한다 — 기계가 자동으로 동기 회차를 열지 않는다.** + 이유: 회차마다 **설계 판단**(우리 포크 지점을 유지할지)이 들어가고 S1·S2·S3 에서 **실제로 필요했다**. +- ★**구현은 «별건»이다** — 이 회차는 검사기·크론·워크플로를 **하나도 만들지 않았다**(티켓 non-goal). + +## 5. ★재측정 조건 — 세는 법과 오늘의 값 + +> ★**새 축으로 «3회차»를 돈 뒤 다시 재라.** 임계 **20** 은 «시작값»이지 상수가 아니다. + +재잴 것 셋: ⑴임계 도달 간격(실제 일수) ⑵회차당 새 충돌 ⑶게이트② 사이클 수. +- ★**⑵의 중앙이 «5 이상»으로 오르면 임계를 «내려라»** — 포화 가정이 깨진 것이다. +- ★**⑴이 «14일 이하»로 짧아지면 임계를 «올려라»** — upstream 이 빨라진 것이다(지금 중앙 **44일**). + +★**오늘의 값**: behind **0** · upstream HEAD `bd42427`(2026-08-31) · 마지막 착지 `61d5bf3f`. +★★**「오래됐다」는 재개 사유가 «아니다»** — 이 축은 **시간이 아니라 격차**로 열린다. + +## 6. 적은 «자리» + +`docs/upstream-sync-approach.md` **§5-B** — ★**동기 회차가 반드시 읽는 계약 파일**이고, +§5(단계 분할)와 §4(우리 자산 낡음) 사이라 「어디서 자를까 / 언제 열까」가 **같은 자리에** 모인다. +★**워크로그에만 적지 않았다**(그 규율은 이 리니지가 이미 세웠다). + +## 7. 경계 + +★**S9 미개시**(behind **0** — 애초에 열 것이 없다) · 검사기·크론·워크플로 **신설 0** · +`.rs` **0줄** · `allow` 9곳 **무접촉** · `rust.yml` **무접촉** · `CLAUDE.md` DoD 블록 **무접촉** · +머지 **0** · force-push **0** · `main` 직접 push **0** · ★**upstream 발신 0**(`git fetch` 읽기만) · +시크릿 출력 **0** · 맨 `grep` **0**.