From 5067ba13703d245a1eed221c00bb44830dae8536 Mon Sep 17 00:00:00 2001 From: jun0 Date: Sat, 5 Sep 2026 07:15:14 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(ci):=20behind=20=EB=A5=BC=20=C2=AB?= =?UTF-8?q?=EC=9E=AC=EB=8A=94=C2=BB=20=EC=A3=BC=201=ED=9A=8C=20=EC=98=88?= =?UTF-8?q?=EC=95=BD=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C=20[rustjava-up?= =?UTF-8?q?stream-behind-measure-scheduled-workflow]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 채택 제안 2026-09-04-upstream-sync-cadence-decision#p0 집행. 2026-09-04 판정(PR #29)이 트리거를 behind >= 20 으로 못박았으나 그 수를 재는 주체가 없었다 — git grep -lI 'rev-list --count' origin/main -- .github/ scripts/ → 0건. 주기 = 주 1회(cron 17 6 * * 1). 선례(rust-audit 일간)를 베끼지 않고 임계 도달 속도로 정당화했다: behind 20 도달이 중앙 44일 · 최소 9일이라 7일 주기는 가장 빠른 관측 상승보다 짧아 한 주기 넘게 놓칠 수 없고, 일간은 7배 비용에 판단 가치가 0 이다(회차보다 빨리 움직일 수 없다). 정시와 rust-audit 의 00:00 슬롯을 피해 오프셋을 뒀다. 알림 = ⒝ Job Summary + annotation · 항상 green. 취향이 아니라 이 저장소 실측으로 골랐다: ⒜red 는 여기서 안 듣는다(선례 rust-audit 최근 20 run 중 19 failure 인데 대응 티켓 0건 · coverage 리니지도 만성 red 를 행동이 아니라 green 으로 끝냈다) 이고, 예약 red 는 main tip check-run 에 붙어(실측 8c1238b5 에 audit failure 7건) 게이트③의 「착지본 green」 읽기를 흐린다. 단 PR 은 막지 않는다 — 예약 run 은 main tip sha 에 붙고 PR head 와 다르다(확인함). ⒞이슈는 구조적으로 불가하다(이 fork 는 hasIssuesEnabled: false). ⇒ ⒝ 가 남은 것이고 그 수동성을 알고 골랐다. 재개 조건이 그 대가를 잰다 — 「behind >= 20 이 7일 이상 지속인데 회차 미개시」를 세는 명령·오늘의 값과 함께 §5-B ⒠ 에 박았다. 한 번 돌려 오늘의 값을 남겼다(워크플로 본문 명령을 신선한 클론에서 그대로 실행): behind 0 · merge-base bd42427 = upstream HEAD ⇒ 임계 미만. 경계: upstream 발신 0(remote 추가 + fetch 뿐 · permissions contents: read) · 자동 발권 0 (임계를 넘어도 회차를 열지 않는다 — 발권은 사람) · 임계 20 무접촉(착지 판정 PR #29) · bin/healthcheck·orchestrator 무접촉 · scripts/ 무접촉 · .rs 0줄 · S9 미개시. 파리티 검사기는 CI_FILE 을 rust.yml 로 코드에 박아 둬 새 워크플로를 보지 않는다(rc=0 확인). --- .github/workflows/upstream-behind.yml | 81 +++++++++++++++++++ REPORT.md | 16 ++++ STATE.md | 15 ++++ docs/upstream-sync-approach.md | 28 +++++++ ...05-upstream-behind-scheduled-workflow.json | 37 +++++++++ ...9-05-upstream-behind-scheduled-workflow.md | 74 +++++++++++++++++ 6 files changed, 251 insertions(+) create mode 100644 .github/workflows/upstream-behind.yml create mode 100644 docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.json create mode 100644 docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.md diff --git a/.github/workflows/upstream-behind.yml b/.github/workflows/upstream-behind.yml new file mode 100644 index 0000000..2402165 --- /dev/null +++ b/.github/workflows/upstream-behind.yml @@ -0,0 +1,81 @@ +# Measures how far this fork is behind upstream (dlunch/RustJava) and reports the number. +# +# Why this exists: the 2026-09-04 cadence decision (docs/upstream-sync-approach.md §5-B, landed +# in PR #29) fixed the trigger at `behind >= 20` but left nothing that *measures* it — the number +# was only ever read when a human remembered to ask. This job asks on a schedule. +# +# ── Why it never fails ──────────────────────────────────────────────────────── +# A red run was considered and rejected on evidence from this repo, not taste: +# `rust-audit.yaml` (the scheduled-workflow precedent) has been `failure` in 19 of its last 20 +# runs and produced zero response tickets, and the `coverage` lineage already resolved a +# chronically-red workflow by making it green rather than by acting on the red. A scheduled +# failure also attaches check-runs to main's tip (measured on 8c1238b5: 7 `audit` failures), +# which muddies the "landed main is green" read that every gate3 round performs. So the number +# is reported, not raised as an error. Issues were not an option either: this fork has issues +# disabled (`hasIssuesEnabled: false`). +# +# ── Reading it ──────────────────────────────────────────────────────────────── +# The canonical read stays local and costs nothing: +# git fetch -q upstream && git rev-list --count origin/main..upstream/main +# This workflow does not replace that; it makes the number appear when nobody asked. +# +# ── Boundaries ──────────────────────────────────────────────────────────────── +# Read-only toward upstream: it adds a remote and fetches. No push, no PR, no issue, no comment. +# It does not open a sync round — issuance stays with a person (the cadence decision says so). +# The threshold below is the landed decision's value; changing it is a new decision, not an edit. + +name: upstream behind + +on: + schedule: + # Weekly, Mondays 06:17 UTC. Weekly rather than daily because the measured time to + # accumulate 20 commits upstream is a median of 44 days and a minimum of 9 (12-month + # window, 144 commits / 51 weeks) — a 7-day period is shorter than the fastest observed + # crossing, so it cannot miss one by more than a period, while daily would cost 7x the + # runs for a decision nobody can act on faster than one sync round. The offset avoids + # the top of the hour (GitHub queues those hardest) and `rust-audit.yaml`'s 00:00 slot. + - cron: "17 6 * * 1" + workflow_dispatch: + +permissions: + contents: read + +jobs: + behind: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Measure behind + env: + THRESHOLD: "20" # docs/upstream-sync-approach.md §5-B — landed in PR #29 + run: | + set -euo pipefail + git remote add upstream https://github.com/dlunch/RustJava.git + git fetch -q --no-tags upstream + behind=$(git rev-list --count HEAD..upstream/main) + base=$(git merge-base HEAD upstream/main) + { + echo "## upstream behind" + echo + echo "| axis | value |" + echo "| --- | --- |" + echo "| behind | **$behind** |" + echo "| threshold | $THRESHOLD |" + echo "| merge-base | \`$base\` |" + echo "| upstream HEAD | \`$(git rev-parse --short upstream/main)\` |" + echo + if [ "$behind" -ge "$THRESHOLD" ]; then + echo "**At or over the threshold — a sync round is due.** Issuance stays with a person;" + echo "this job does not open one. See docs/upstream-sync-approach.md §5-B." + else + echo "Under the threshold — nothing to do." + fi + } >> "$GITHUB_STEP_SUMMARY" + if [ "$behind" -ge "$THRESHOLD" ]; then + echo "::warning title=upstream behind $behind::at or over the threshold ($THRESHOLD) — a sync round is due" + else + echo "::notice title=upstream behind $behind::under the threshold ($THRESHOLD)" + fi diff --git a/REPORT.md b/REPORT.md index 42f82fb..663a908 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,5 +1,21 @@ # REPORT +## [2026-09-05] `behind` 를 «재는» 예약 워크플로 신설 (rustjava-upstream-behind-measure-scheduled-workflow) +- 무엇을: `.github/workflows/upstream-behind.yml` **1개 신설**. 주 1회 upstream 을 **read-only fetch** 해 + `rev-list --count` 를 찍고 **Job Summary + annotation** 으로 보고한다. ★**코드(`.rs`) 0줄 · `scripts/` 무접촉.** +- 왜: 2026-09-04 판정이 트리거를 **`behind ≥ 20`** 으로 못박았는데 ★**그 수를 «재는 주체»가 없었다** + (`rev-list --count` 를 가진 파일 **0건**). 임계만 있고 관측이 없으면 「아무도 안 챙긴다」가 그대로 남는다. +- 사용자 영향: 런타임·CI 게이트 **무변경**(새 워크플로는 예약 전용이라 PR 검사에 들어가지 않는다). + ★**다음 동기 회차의 시점이 «사람 기억»에서 «주간 관측»으로 옮겨졌다.** +- 검증: ★**한 번 돌려** 오늘의 값을 냈다 — **behind `0`** · `merge-base` `bd42427` = upstream HEAD ⇒ 임계 미만. + 무회귀: `check-dod-ci-parity.py` **rc=0** · `check-worklog-json.py` **rc=0**. +- ★★**알림 방식을 «실측으로» 골랐다**(이 회차 결정의 대부분): ⒜red 는 여기서 **안 듣는다** + (선례 `rust-audit` 최근 **20 run 중 19 failure** · ★**대응 티켓 0건** · `coverage` 리니지도 만성 red 를 «green 으로» 끝냈다) + 이고 예약 red 는 **main tip check-run 을 오염**시킨다(★단 **PR 은 막지 않는다** — 확인했다) · + ⒞이슈는 ★**이 fork 가 issues 비활성이라 불가** ⇒ ★**⒝ 가 «남은 것»이고 그 수동성은 재개 조건으로 잰다.** +- ★**후속 추천**: ⑴`behind ≥ 20` 이 **7일 이상**인데 회차가 안 열리면 **알림 방식을 다시 열어라**(세는 명령은 §5-B ⒠). + ⑵★**임계 20 과 「발권은 사람이 한다」는 이 회차가 건드리지 않았다** — 바꾸려면 새 결정이다. + ## [2026-09-05] 워크로그 «부재» 기계 강제 판정 — 넣지 않는다 (rustjava-worklog-absence-machine-enforcement-decision) - 무엇을: 채택 제안 `2026-09-04-worklog-mandate-and-local-gate#p0` 에 대한 **판정**이다. ★**결론: 워크로그 «부재»를 잡는 기계 강제를 지금은 넣지 않는다** — `check-worklog-json.py` 의 diff --git a/STATE.md b/STATE.md index a53f883..5cd8ddd 100644 --- a/STATE.md +++ b/STATE.md @@ -1,6 +1,21 @@ # STATE ## 진행중 +- [rustjava-upstream-behind-measure-scheduled-workflow] ★★**`behind` 를 «재는» 자리 신설 — `.github/workflows/upstream-behind.yml`.** + 채택 제안 `2026-09-04-upstream-sync-cadence-decision#p0`. ★임계는 정해졌는데(**`behind ≥ 20`** · PR #29 착지) ★**재는 주체가 없었다** + (`git grep -lI 'rev-list --count' origin/main -- .github/ scripts/` → **0건**). + ★**주기 = 주 1회**(`cron: "17 6 * * 1"`) — 선례(`rust-audit` 일간)를 **베끼지 않고** 임계 도달 속도로 정당화했다: + behind 20 도달 **중앙 44일 · ★최소 9일** ⇒ ★**7 < 9 라 한 주기 넘게 못 놓친다** · 일간은 7배 비용에 판단 가치 0. + ★★**알림 = ⒝ Job Summary + annotation · «항상 green»** — ★**취향이 아니라 실측으로 골랐다**: + ⒜red 는 여기서 **안 듣는다**(선례 `rust-audit` 최근 **20 run 중 19 failure** 인데 ★**대응 티켓 0건**) · + 예약 red 는 ★**main tip check-run 에 붙어**(실측 `8c1238b5` 에 `audit` failure **7건**) 게이트③의 「착지본 green」 읽기를 **흐린다** + (★단 **PR 은 막지 않는다** — PR head 와 sha 가 다르다) · ⒞이슈는 ★**이 fork 가 issues 비활성이라 «불가»**. + ⇒ ★**⒝ 가 «남은 것»이고 그 «수동성»을 알고 골랐다** — ★**재개 조건이 그 대가를 «잰다»** + (「`behind ≥ 20` 이 7일 이상인데 회차 미개시」 · 세는 명령·오늘의 값을 §5-B ⒠ 에 박았다). + ★**한 번 돌려 오늘의 값**: ★**behind `0`** · `merge-base` `bd42427` = upstream HEAD ⇒ 임계 미만. + ★**upstream 발신 0**(remote 추가 + fetch 뿐 · `permissions: contents: read`) · ★**자동 발권 0** · ★**임계 20 무접촉** · + ★`bin/healthcheck`·orchestrator 무접촉 · `scripts/` 무접촉(파리티 rc=0) · `.rs` **0줄** · S9 미개시. + **PR 대기 — 게이트③ 미착지.** - [rustjava-upstream-sync-squash-defeats-convergence] ★**S1~S4 가 착지하고도 fork 가 upstream 에 한 걸음도 가까워지지 않은 근인을 확정하고 계보를 기록했다.** 근인 = 게이트③ 제품 repo **`--squash`**. 증명은 **머지커밋 부모 수**다 — `6bfe97c4`·`11ef5010`·`4bb796de`·`3a597768` **전건 1개**(커밋 7·10·15·21이 diff --git a/docs/upstream-sync-approach.md b/docs/upstream-sync-approach.md index c442a5b..e1573ef 100644 --- a/docs/upstream-sync-approach.md +++ b/docs/upstream-sync-approach.md @@ -1007,6 +1007,34 @@ git fetch -q upstream && git rev-list --count origin/main..upstream/main # ★ 이유: 회차마다 **설계 판단**(우리 포크 지점 유지 여부)이 들어가고, 그것은 S1·S2·S3 에서 실제로 필요했다. - ★**구현은 «별건»이다 — 이 판정 회차에서 만들지 않았다**(검사기·크론·워크플로 신설 **0**). +★★★**[2026-09-05 착지] «재는 자리»가 생겼다 — `.github/workflows/upstream-behind.yml`** +(제안 `2026-09-04-upstream-sync-cadence-decision#p0` 집행 · 위 「구현은 별건」이 그 별건이다). + +- **주기 = 주 1회**(`cron: "17 6 * * 1"`). ★**일간이 아니다** — 임계 20 에 닿는 시간이 **중앙 44일 · 최소 9일**(§⒞)이라 + ★**7일 주기는 «가장 빠른 관측 상승»보다도 짧아** 한 주기 넘게 놓칠 수 없고, 일간은 **7배 비용**에 판단 가치가 0 이다 + (회차보다 빨리 움직일 수 없다). 정시·`rust-audit` 의 00:00 슬롯을 피해 오프셋을 뒀다. +- ★★**알림 방식 = ⒝ «Job Summary + annotation» · 워크플로는 «항상 green»** — ★**취향이 아니라 이 저장소 실측으로 골랐다**: + ⒤★**⒜(red)는 여기서 «안 듣는다»** — 선례 `rust-audit.yaml` 이 최근 **20 run 중 19 가 failure** 인데 + ★**대응 티켓이 «0건»**이다(제목 기준 전수 조회). ★**`coverage` 리니지도 만성 red 를 «행동»이 아니라 «green 으로» 끝냈다.** + ⒥★**그리고 예약 red 는 «main tip 의 check-run»에 붙는다**(실측 `8c1238b5` 에 `audit` failure **7건**) ⇒ + 게이트③이 매 회차 하는 「착지본 green」 읽기를 **흐린다**. ※단 ★**PR 을 막지는 않는다**(PR head 와 sha 가 다르다). + ⒦★**⒞(이슈)는 «불가»다** — 이 fork 는 ★**issues 가 비활성**이다(`hasIssuesEnabled: false`). + ⇒ ★**남은 것이 ⒝ 이고, 그 «수동성»을 알고 고른다**(아래 재개 조건이 그 대가를 잰다). +- ★**정본 «읽기»는 여전히 로컬 한 줄이다** — 워크플로는 그것을 **대체하지 않고**, ★**아무도 안 물어도 재는** 역할이다. +- ★**upstream 발신 0** — remote 추가 + `fetch` 뿐이다(push·PR·issue·comment 0) · `permissions: contents: read`. +- ★★**자동 발권 0** — 임계를 넘어도 워크플로는 **회차를 열지 않는다**. 발권은 사람이 한다(위 ⒠ 그대로). +- ★**임계 20 은 이 회차가 바꾸지 않았다** — 그 수는 착지한 판정(PR #29 · 머지 `4b91e8f8`)이다. + +★★**알림 방식의 «재개 조건» — 수동성이 실제로 대가를 치렀는지 잰다**: +> ★**`behind ≥ 20` 이 «7일 이상» 지속됐는데 그 사이 동기 회차가 «열리지 않았으면» 알림 방식을 다시 연다** +> (⒞ 는 issues 를 켜야 하고, ⒜ 는 위 ⒥ 를 감수해야 한다 — 그때 «다시» 저울질하라). +```sh +# ⑴ 지금 값 ⑵ 마지막 동기 회차 착지 시각 +git fetch -q upstream && git rev-list --count origin/main..upstream/main +git log -1 --format=%ci --grep='upstream 동기' --first-parent origin/main +``` +★**오늘의 값 = behind `0` · 마지막 동기 착지 `a76b305`(S8)** ⇒ ★**대가를 아직 치르지 않았다**(관측 시작점). + ### ⒡ ★재개(재측정) 조건 — 세는 법과 오늘의 값 > ★**새 축으로 «3회차»를 돈 뒤 다시 재라.** 임계 20 은 «시작값»이지 상수가 아니다. diff --git a/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.json b/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.json new file mode 100644 index 0000000..50f8ff8 --- /dev/null +++ b/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.json @@ -0,0 +1,37 @@ +{ + "schema": 1, + "date": "2026-09-05", + "title": "behind 를 «재는» 자리 신설 — 주 1회 예약 워크플로 · 알림은 Job Summary(항상 green)", + "services": [ + "RustJava" + ], + "taskId": "rustjava-upstream-behind-measure-scheduled-workflow", + "summary": "채택 제안 2026-09-04-upstream-sync-cadence-decision#p0 집행. 2026-09-04 판정(PR #29 · 머지 4b91e8f8)이 트리거를 behind ≥ 20 으로 못박았으나 그 수를 «재는 주체»가 없었다(git grep -lI 'rev-list --count' origin/main -- .github/ scripts/ → 0건). ⇒ .github/workflows/upstream-behind.yml 을 신설했다 — 주 1회(cron 17 6 * * 1) upstream 을 read-only fetch 해 rev-list --count 를 찍고 Job Summary + annotation 으로 보고한다. ★알림 방식이 이 회차 결정의 대부분이고, 취향이 아니라 이 저장소 실측으로 골랐다: ⒜red 는 여기서 안 듣는다(선례 rust-audit.yaml 이 최근 20 run 중 19 failure 인데 대응 티켓 0건 · coverage 리니지도 만성 red 를 «green 으로» 끝냈다) 이고 예약 red 는 main tip 의 check-run 에 붙어(실측 8c1238b5 에 audit failure 7건) 게이트③의 「착지본 green」 읽기를 흐린다(단 PR 은 막지 않는다 — 예약 run 은 main tip sha 에 붙고 PR head 와 다르다). ⒞이슈는 구조적으로 불가하다(이 fork 는 hasIssuesEnabled: false). ⇒ ⒝ 를 채택하고 그 «수동성»을 알고 고른다 — 재개 조건이 그 대가를 잰다. 주기는 선례를 베끼지 않고 임계 도달 속도로 정당화했다: behind 20 도달이 중앙 44일 · 최소 9일이라 7일 주기는 가장 빠른 관측 상승보다 짧아 한 주기 넘게 놓칠 수 없고, 일간은 7배 비용에 판단 가치가 0 이다(회차보다 빨리 움직일 수 없다). ★한 번 돌려 오늘의 값을 남겼다: behind 0 · merge-base bd42427 = upstream HEAD ⇒ 임계 미만.", + "changes": [ + ".github/workflows/upstream-behind.yml 신설 — 주 1회 예약 + workflow_dispatch · permissions: contents: read · upstream remote 추가 후 fetch → rev-list --count → Job Summary 표(behind·threshold·merge-base·upstream HEAD) + 임계 이상이면 ::warning:: 아니면 ::notice:: · ★항상 exit 0. 워크플로 머리 주석에 «왜 red 가 아닌가»를 실측과 함께 박았다.", + "docs/upstream-sync-approach.md §5-B ⒠ — 「구현은 별건」 자리에 그 별건의 착지를 기록: 주기 근거 · 알림 방식 3안 비교(실측) · upstream 발신 0 · 자동 발권 0 · 임계 무접촉 · ★알림 방식의 재개 조건(behind ≥ 20 이 7일 이상 지속인데 회차가 안 열리면 다시 연다)과 세는 명령·오늘의 값.", + "docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.{md,json} 신설 · STATE.md · REPORT.md 회차 기록.", + "★scripts/ 무접촉 · bin/healthcheck·orchestrator 무접촉 · 임계 20 무접촉 · .rs 0줄." + ], + "deploy": { + "sha": "", + "urls": [] + }, + "verification": "★전제 재확인: git ls-tree --name-only origin/main .github/workflows/ → coverage.yml · dependabot.yaml · opencode.yml · rust-audit.yaml · rust.yml(schedule 보유 = rust-audit.yaml 뿐) · rev-list --count 를 가진 파일 0건. ★⒜ 실효 실측: gh run list --workflow=rust-audit.yaml --limit 20 → failure 19 · success 1 이고, ls tasks/ | grep -i 'rustjava.*audit' → 0건(대응 티켓 없음). ★⒜ 부작용 실측: gh api repos/Jun025/RustJava/commits/8c1238b5/check-runs → audit failure 7건이 main tip 커밋에 붙어 있다. 단 PR head 에는 붙지 않는다(이 리니지 PR head 조회 전건에서 audit 0건) ⇒ PR 을 막지는 않는다. ★⒞ 실측: gh repo view --json hasIssuesEnabled → false(이 fork 는 이슈 비활성). ★주기 근거: upstream 12개월 144커밋/51주 · behind 20 도달 중앙 44일 · 최소 9일 · 최대 139일 ⇒ 7일 < 9일. ★오늘의 값(워크플로 본문 명령을 신선한 클론에서 그대로 실행): behind 0 · merge-base bd42427 · upstream HEAD bd42427 ⇒ 임계 미만. ★무회귀: python3 scripts/check-dod-ci-parity.py rc=0(새 워크플로에 영향 없음 — 그 검사기는 CI_FILE 을 rust.yml 로 코드에 박아 둔다) · python3 scripts/check-worklog-json.py rc=0 · .rs 변경 0.", + "issues": [ + "★★알림 방식을 «실측으로» 골랐다 — ⒜(red)는 이 저장소에서 관측상 안 듣는다(rust-audit 20 run 중 19 red · 대응 티켓 0건). 그리고 예약 red 는 main tip check-run 에 붙어 게이트③의 「착지본 green」 읽기를 흐린다. ⒞(이슈)는 fork 의 issues 가 비활성이라 불가. ⇒ ⒝ 가 «남은 것»이고, 그 수동성은 재개 조건으로 잰다. ★「⒝ 가 최선이다」가 아니라 「⒜⒞ 가 각각 무실효·불가다」가 근거다.", + "★⒜ 가 PR 을 막는지 티켓이 확인하라 했고, 확인 결과 «막지 않는다» — 예약 run 은 main tip sha 에 붙고 PR head 와 다르다. 그럼에도 기각한 이유는 «막느냐»가 아니라 «듣느냐»와 «착지본 읽기를 흐리느냐»였다.", + "★⒝ 는 수동적이다 — 그 한계를 안고 골랐고 숨기지 않는다. 재개 조건(behind ≥ 20 이 7일 이상 지속인데 회차 미개시)을 세는 명령·오늘의 값과 함께 §5-B ⒠ 에 박았다.", + "★자동 발권 0 · 임계 20 무접촉 — 임계를 넘어도 워크플로는 회차를 열지 않는다(판정이 「발권은 사람이 한다」로 정했다). 임계 변경은 새 결정이지 편집이 아니다.", + "★파리티 검사기가 새 워크플로를 «보지 않는다» — CI_FILE 이 rust.yml 로 코드에 박혀 있기 때문이고 그것은 설계다(정본 위치를 코드에 박는다). ⇒ upstream-behind.yml 의 run 줄은 로컬 DoD 대칭차에 들어오지 않는다. 의도된 범위이고 rc=0 으로 확인했다.", + "머지하지 않았다 — 게이트②·③은 별 세션이다. ★게이트③은 등재 repo 라 --squash 금지 · merge_strategy: merge 필수." + ], + "proposals": [], + "resolvedIssues": [ + "2026-09-04-upstream-sync-cadence-decision#p0 — 「behind 를 «재는» 자리를 만들어라(발권은 사람이 유지)」. .github/workflows/upstream-behind.yml 로 집행했고, 알림 방식 3안을 실측으로 비교해 ⒝ 를 골랐으며, 주기·오늘의 값·재개 조건을 docs/upstream-sync-approach.md §5-B ⒠ 에 박았다." + ], + "adoptedProposals": [ + "2026-09-04-upstream-sync-cadence-decision#p0" + ], + "declinedProposals": [] +} diff --git a/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.md b/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.md new file mode 100644 index 0000000..bf2bc72 --- /dev/null +++ b/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.md @@ -0,0 +1,74 @@ +# [2026-09-05] `behind` 를 «재는» 자리 신설 — `.github/workflows/upstream-behind.yml` + +**티켓**: `rustjava-upstream-behind-measure-scheduled-workflow` +**채택 제안**: `2026-09-04-upstream-sync-cadence-decision#p0` +**성격**: 워크플로 **1개 신설** · 코드(`.rs`) **0줄** · `bin/healthcheck`·orchestrator **무접촉** · S9 **미개시** + +--- + +## 1. 왜 — 임계는 정해졌는데 «재는 주체»가 없었다 + +2026-09-04 판정(PR #29 · 머지 `4b91e8f8`)이 트리거를 **`behind ≥ 20`** 으로 못박았으나, +★**그 수는 «사람이 생각날 때만» 재졌다.** 총괄 재실측이 그 전제를 확인했다 — +`git grep -lI 'rev-list --count' origin/main -- .github/ scripts/` → ★**0건**. + +## 2. ★★알림 방식 — ⒝(Job Summary + annotation · 항상 green) + +★**이 회차 결정의 «대부분»이 여기다**(제안이 그렇게 적었다). ★**취향이 아니라 이 저장소 실측으로 골랐다.** + +| 안 | 실측 | 판정 | +|---|---|---| +| ⒜ **red** | ★선례 `rust-audit.yaml` 이 최근 **20 run 중 19 failure** 인데 ★**대응 티켓 «0건»**(제목 기준 전수) · `coverage` 리니지도 만성 red 를 «행동»이 아니라 «green 으로» 끝냈다 | ★**여기서 «안 듣는다»** ⇒ 기각 | +| 〃 부작용 | ★예약 red 는 **main tip 의 check-run 에 붙는다**(실측 `8c1238b5` 에 `audit` failure **7건**) ⇒ 게이트③의 「착지본 green」 읽기를 흐린다 | 〃 | +| 〃 파급(티켓이 확인하라 한 것) | ★**PR 은 «막지 않는다»** — 예약 run 은 **main tip** sha 에 붙고 PR head 와 다르다(내 회차 전건에서 PR head check-run 에 `audit` **0건**) | ★**막지는 않는다**(그래도 위 두 이유로 기각) | +| ⒞ **이슈** | ★이 fork 는 ★**issues 가 비활성**(`hasIssuesEnabled: false`) | ★**구조적으로 불가** | +| ★**⒝ Job Summary + annotation** | 부작용 0 · `gh` 로 조회 가능 · main tip 오염 0 | ★**채택** | + +★★**⒝ 의 «수동성»을 알고 고른다** — 그것이 이 선택의 대가이고, ★**재개 조건이 그 대가를 «잰다»**(§5). +★**정본 «읽기»는 여전히 로컬 한 줄**(`git rev-list --count`)이다 — 워크플로는 그것을 대체하지 않고 +★**«아무도 안 물어도 재는»** 역할이다. + +## 3. ★주기 — 주 1회(`cron: "17 6 * * 1"`) + +★**선례를 «베끼지» 않았다** — `rust-audit.yaml` 은 **일간**(`0 0 * * *`)이지만 그 주기는 보안 권고 축의 것이다. +임계 도달 속도로 정당화한다(§⒞ 실측 · 12개월 144커밋/51주): + +| 축 | 값 | +|---|---| +| `behind 20` 도달 | 중앙 **44일** · ★**최소 9일** · 최대 139일 | +| 고른 주기 | ★**7일** | +| 근거 | ★**7 < 9** — «가장 빠른 관측 상승»보다 짧아 **한 주기 넘게 놓칠 수 없다** | +| 일간을 안 고른 이유 | **7배 비용**에 판단 가치 0 — ★**회차보다 빨리 움직일 수 없다** | +| 오프셋 | 정시(큐 혼잡)와 `rust-audit` 의 **00:00 슬롯**을 피했다 | + +## 4. ★한 번 «돌려서» 낸 오늘의 값 + +워크플로 본문의 «그 명령»을 그대로 태웠다(신선한 클론 · upstream remote 추가 → `fetch` → `rev-list --count`): + +``` +behind = 0 +threshold = 20 +merge-base = bd42427 +upstream HEAD = bd42427 +판정 = 임계 미만 — 할 일 없음 +``` + +## 5. ★재개 조건 — «수동성»이 대가를 치렀는지 잰다 + +> ★**`behind ≥ 20` 이 «7일 이상» 지속됐는데 그 사이 동기 회차가 «열리지 않았으면» 알림 방식을 다시 연다.** + +```sh +git fetch -q upstream && git rev-list --count origin/main..upstream/main +git log -1 --format=%ci --grep='upstream 동기' --first-parent origin/main +``` +★**오늘의 값 = behind `0` · 마지막 동기 착지 `a76b305`(S8 · 2026-09-05 01:46:33 +0900)** +⇒ ★**아직 대가를 치르지 않았다**(관측 시작점). ★**그때 ⒞(issues 를 켠다) ↔ ⒜(위 부작용 감수)를 «다시» 저울질하라.** + +## 6. 경계 + +★**upstream 발신 0** — remote 추가 + `fetch` 뿐(push·PR·issue·comment **0**) · `permissions: contents: read`. +★**자동 발권 0** — 임계를 넘어도 워크플로가 **회차를 열지 않는다**(발권은 사람 · 판정이 그렇게 정했다). +★**임계 20 무접촉**(착지 판정 PR #29) · ★**S9 미개시**(behind **0**) · ★**`bin/healthcheck`·orchestrator 무접촉** · +★**`scripts/` 무접촉**(파리티 검사기 `rc=0` — 그 검사기는 `CI_FILE` 을 `rust.yml` 로 **코드에 박아** 둬서 새 워크플로를 보지 않는다) · +★`.rs` **0줄** · 머지 **0** · force-push **0** · `main` 직접 push **0** · +★게이트③은 ★**`--squash` 금지 저장소**(`contracts/upstream-sync-repos.conf` 등재) ⇒ `merge_strategy: merge`. From e2a78b8c1d1a6a0be6769329d6da5841725537a7 Mon Sep 17 00:00:00 2001 From: jun0 Date: Sat, 5 Sep 2026 07:49:01 +0900 Subject: [PATCH 2/2] docs: record gate3 execution in the ledger [rustjava-upstream-behind-measure-scheduled-workflow] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STATE.md 진행중 → 완료(게이트③ PR #31 · --merge · 등재 repo · 게이트② 1회차 approve · 착지 후 workflow_dispatch 로 워크플로를 실제로 돌려 확인 — 구현 회차가 자인한 구멍이다) + worklog .json 게이트③ 집행 1줄(동봉 전 head 가 핀과 일치했음 · 착지 diff 에 워크플로 파일이 있어 3-a⒞ 공유 앵커를 반드시 봤고 paths 필터 0건이라 해당 없음). 코드 변경 0. --- STATE.md | 32 ++++++++++--------- ...05-upstream-behind-scheduled-workflow.json | 3 +- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/STATE.md b/STATE.md index 5cd8ddd..10ca20b 100644 --- a/STATE.md +++ b/STATE.md @@ -1,21 +1,6 @@ # STATE ## 진행중 -- [rustjava-upstream-behind-measure-scheduled-workflow] ★★**`behind` 를 «재는» 자리 신설 — `.github/workflows/upstream-behind.yml`.** - 채택 제안 `2026-09-04-upstream-sync-cadence-decision#p0`. ★임계는 정해졌는데(**`behind ≥ 20`** · PR #29 착지) ★**재는 주체가 없었다** - (`git grep -lI 'rev-list --count' origin/main -- .github/ scripts/` → **0건**). - ★**주기 = 주 1회**(`cron: "17 6 * * 1"`) — 선례(`rust-audit` 일간)를 **베끼지 않고** 임계 도달 속도로 정당화했다: - behind 20 도달 **중앙 44일 · ★최소 9일** ⇒ ★**7 < 9 라 한 주기 넘게 못 놓친다** · 일간은 7배 비용에 판단 가치 0. - ★★**알림 = ⒝ Job Summary + annotation · «항상 green»** — ★**취향이 아니라 실측으로 골랐다**: - ⒜red 는 여기서 **안 듣는다**(선례 `rust-audit` 최근 **20 run 중 19 failure** 인데 ★**대응 티켓 0건**) · - 예약 red 는 ★**main tip check-run 에 붙어**(실측 `8c1238b5` 에 `audit` failure **7건**) 게이트③의 「착지본 green」 읽기를 **흐린다** - (★단 **PR 은 막지 않는다** — PR head 와 sha 가 다르다) · ⒞이슈는 ★**이 fork 가 issues 비활성이라 «불가»**. - ⇒ ★**⒝ 가 «남은 것»이고 그 «수동성»을 알고 골랐다** — ★**재개 조건이 그 대가를 «잰다»** - (「`behind ≥ 20` 이 7일 이상인데 회차 미개시」 · 세는 명령·오늘의 값을 §5-B ⒠ 에 박았다). - ★**한 번 돌려 오늘의 값**: ★**behind `0`** · `merge-base` `bd42427` = upstream HEAD ⇒ 임계 미만. - ★**upstream 발신 0**(remote 추가 + fetch 뿐 · `permissions: contents: read`) · ★**자동 발권 0** · ★**임계 20 무접촉** · - ★`bin/healthcheck`·orchestrator 무접촉 · `scripts/` 무접촉(파리티 rc=0) · `.rs` **0줄** · S9 미개시. - **PR 대기 — 게이트③ 미착지.** - [rustjava-upstream-sync-squash-defeats-convergence] ★**S1~S4 가 착지하고도 fork 가 upstream 에 한 걸음도 가까워지지 않은 근인을 확정하고 계보를 기록했다.** 근인 = 게이트③ 제품 repo **`--squash`**. 증명은 **머지커밋 부모 수**다 — `6bfe97c4`·`11ef5010`·`4bb796de`·`3a597768` **전건 1개**(커밋 7·10·15·21이 @@ -38,6 +23,23 @@ (25번째 run, 앞선 24건 전부 red). **PR 대기 — 게이트③ 미착지.** ## 완료 +- [rustjava-upstream-behind-measure-scheduled-workflow] ★★**`behind` 를 «재는» 자리 신설 — `.github/workflows/upstream-behind.yml`.** + 채택 제안 `2026-09-04-upstream-sync-cadence-decision#p0`. ★임계는 정해졌는데(**`behind ≥ 20`** · PR #29 착지) ★**재는 주체가 없었다** + (`git grep -lI 'rev-list --count' origin/main -- .github/ scripts/` → **0건**). + ★**주기 = 주 1회**(`cron: "17 6 * * 1"`) — 선례(`rust-audit` 일간)를 **베끼지 않고** 임계 도달 속도로 정당화했다: + behind 20 도달 **중앙 44일 · ★최소 9일** ⇒ ★**7 < 9 라 한 주기 넘게 못 놓친다** · 일간은 7배 비용에 판단 가치 0. + ★★**알림 = ⒝ Job Summary + annotation · «항상 green»** — ★**취향이 아니라 실측으로 골랐다**: + ⒜red 는 여기서 **안 듣는다**(선례 `rust-audit` 최근 **20 run 중 19 failure** 인데 ★**대응 티켓 0건**) · + 예약 red 는 ★**main tip check-run 에 붙어**(실측 `8c1238b5` 에 `audit` failure **7건**) 게이트③의 「착지본 green」 읽기를 **흐린다** + (★단 **PR 은 막지 않는다** — PR head 와 sha 가 다르다) · ⒞이슈는 ★**이 fork 가 issues 비활성이라 «불가»**. + ⇒ ★**⒝ 가 «남은 것»이고 그 «수동성»을 알고 골랐다** — ★**재개 조건이 그 대가를 «잰다»** + (「`behind ≥ 20` 이 7일 이상인데 회차 미개시」 · 세는 명령·오늘의 값을 §5-B ⒠ 에 박았다). + ★**한 번 돌려 오늘의 값**: ★**behind `0`** · `merge-base` `bd42427` = upstream HEAD ⇒ 임계 미만. + ★**upstream 발신 0**(remote 추가 + fetch 뿐 · `permissions: contents: read`) · ★**자동 발권 0** · ★**임계 20 무접촉** · + ★`bin/healthcheck`·orchestrator 무접촉 · `scripts/` 무접촉(파리티 rc=0) · `.rs` **0줄** · S9 미개시. + ★게이트③ 완료: PR #31 — ★★**`--merge` 착지**(★`--squash` 아님 · 등재 repo). 머지커밋 sha 는 회신 `merged:` 참조. + ★**착지 후 «워크플로를 실제로 돌려» 확인했다** — 구현 회차가 「본문 명령만 태웠고 워크플로 자체는 못 돌렸다」고 자인한 그 구멍이다. + 결과는 회신 참조. ★**게이트② 1회차 approve** — 반려 0. - [rustjava-worklog-absence-machine-enforcement-decision] ★★**워크로그 «부재»를 기계로 잡을지 «판정» — 결론: 지금은 «넣지 않는다».** 채택 제안 `2026-09-04-worklog-mandate-and-local-gate#p0`. ★**`scripts/check-worklog-json.py` 무접촉**(6축 무회귀 · rc=0) · `.rs` **0줄** · 과거 backfill **0** · qts 잠금·upstream 리니지·파리티 검사기 **무접촉**. diff --git a/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.json b/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.json index 50f8ff8..bd927d0 100644 --- a/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.json +++ b/docs/worklog/2026-09-05-upstream-behind-scheduled-workflow.json @@ -24,7 +24,8 @@ "★⒝ 는 수동적이다 — 그 한계를 안고 골랐고 숨기지 않는다. 재개 조건(behind ≥ 20 이 7일 이상 지속인데 회차 미개시)을 세는 명령·오늘의 값과 함께 §5-B ⒠ 에 박았다.", "★자동 발권 0 · 임계 20 무접촉 — 임계를 넘어도 워크플로는 회차를 열지 않는다(판정이 「발권은 사람이 한다」로 정했다). 임계 변경은 새 결정이지 편집이 아니다.", "★파리티 검사기가 새 워크플로를 «보지 않는다» — CI_FILE 이 rust.yml 로 코드에 박혀 있기 때문이고 그것은 설계다(정본 위치를 코드에 박는다). ⇒ upstream-behind.yml 의 run 줄은 로컬 DoD 대칭차에 들어오지 않는다. 의도된 범위이고 rc=0 으로 확인했다.", - "머지하지 않았다 — 게이트②·③은 별 세션이다. ★게이트③은 등재 repo 라 --squash 금지 · merge_strategy: merge 필수." + "머지하지 않았다 — 게이트②·③은 별 세션이다. ★게이트③은 등재 repo 라 --squash 금지 · merge_strategy: merge 필수.", + "★[게이트③ 집행] PR #31 을 --merge 로 착지시켰다(등재 repo — 스쿼시는 계보를 접는다). 동봉 «전» head 가 검수 핀 5067ba13703d245a1eed221c00bb44830dae8536 과 일치함을 확인했고(git fetch 를 먼저 쳐 거짓 일치를 막았다), 동봉 전/후 sha 는 머지 회신에 적었다. 착지 diff 6파일 — 그중 하나가 워크플로 파일이라 3-a⒞(공유 앵커)를 «반드시» 봤고, paths 필터가 0건이라 #444 형상은 해당 없다. 배포 워크플로 0개 ⇒ 배포 0 · 코드(.rs) 0줄. ★착지 후 workflow_dispatch 로 그 워크플로를 «실제로 돌려» 확인했다 — 구현 회차가 자인한 구멍(본문 명령만 태웠고 워크플로 자체는 못 돌렸다)을 게이트③에서 닫았다." ], "proposals": [], "resolvedIssues": [