Skip to content

Commit cfbb833

Browse files
author
jun0
committed
[2026-09-19-partial-clone-blob-vs-absence-p0] fix(scripts): 경로 루프를 정렬한다 — 두 실행을 비교할 수 있게
채택 제안 2026-09-19-partial-clone-blob-vs-absence#p0. 전제 재측(오늘 origin/main · 범위 e53b214^..e53b214 · PYTHONHASHSEED=random 8회): 순서 2종(6+2) — 같은 6건을 다른 차례로 찍는다. 제안은 옳고 이유도 옳다 (이 리니지가 실제로 그 대가를 치렀다 — 전/후 diff 가 회귀처럼 보였고 원판본이 «자기와 불일치»함을 보여서야 갈렸다). ★다만 자리가 제안과 다르다: 제안은 「set 에 모아 iteration order 로 찍는다」고 했는데 찍는 자리는 이미 정렬돼 있었다(sorted(theirs_symbols - …) ⇒ 경로 «안»의 이름은 늘 정렬). 흔들린 것은 바깥 «경로» 루프이고, changed 는 git diff --name-only 둘을 합친 set 이다. ⇒ 고친 자리는 print 가 아니라 루프다. 1파일 +7/−1(코드 1줄 + 측정 기록 주석). 양방향(제품 스크립트): 그 줄만 되돌리면 8회에 순서 2종 ↔ 넣으면 8/8 동일. 내용은 양방향 불변 — findings 6 · rc=1 · 요약 줄 동일. ★rc 로는 red 를 만들 수 없다(순서만 바뀐다) — 축을 «순서 종수»로 세웠고 그 사실을 적는다. 대가: 보고가 순회 순서를 더는 반영하지 않는다(아무도 안 읽어서 안전했고 그래서 안 들켰다) · ★«보고를 비교 가능»하게 할 뿐 «검사를 결정적»으로 만들지는 않는다(저장소 상태 의존 그대로) · 이 검사기만 고쳤다(형제는 보기엔 안전하나 재지 않았다). 비용 유의차 없음(전 2.09/2.51/2.41s ↔ 후 2.52/1.90/1.65s · 구간 겹침).
1 parent ad9eb1a commit cfbb833

5 files changed

Lines changed: 128 additions & 1 deletion

File tree

‎REPORT.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
# REPORT
2+
## [2026-09-19] 검사기가 «실행마다 자기와 불일치»했다 — `sorted()` 한 줄(2026-09-19-partial-clone-blob-vs-absence-p0)
3+
- 무엇을: 채택 제안 `2026-09-19-partial-clone-blob-vs-absence#p0`(worklog json 기록). `check-merge-dropped-symbols.py` 의 **경로 루프**를 정렬한다. ★**1파일 +7/−1**(코드 1줄 + 측정 기록 주석 6줄).
4+
- ★**전제 재측**(오늘 `origin/main` · 같은 범위 · `PYTHONHASHSEED=random` **8회**): ★**순서 2종**(6 + 2) — 같은 **6건**을 다른 차례로 찍는다. ⇒ 제안은 옳고, ★**그 이유도 옳다**: 이 리니지가 실제로 그 대가를 치렀다(전/후 diff 가 «회귀»처럼 보였고, **원판본이 자기와 불일치**함을 보여서야 갈렸다).
5+
- ★★**다만 «자리»는 제안이 지목한 곳이 아니다**: 제안은 「set 에 모아 iteration order 로 «찍는다»」고 했는데 ★**찍는 자리는 이미 정렬돼 있었다**(`sorted(theirs_symbols - …)` ⇒ 경로 «안»의 이름은 늘 정렬). 흔들린 것은 ★**바깥 경로 루프** — `for path in filter(None, changed)` 이고 `changed` 는 `git diff --name-only` 둘을 합친 **set** 이다. ⇒ 고친 자리는 **print 가 아니라 루프**다.
6+
- ★**양방향 축(제품 스크립트)**: 그 줄만 되돌리면 8회에 **순서 2종** ↔ 넣으면 **8/8 동일**. ★**내용은 양방향 불변** — findings **6** · **rc=1** · 요약 줄 동일.
7+
★**Acceptance 문면 고지**: 「되돌리면 red」를 요구하는데 ★**이 변경에는 뒤집을 판정이 없다**(순서만 바꾸고 rc 는 설계상 전후 1) ⇒ 축을 **«순서 종수»**로 세웠고 그 사실을 적는다.
8+
- ★**대가**: ⒜보고가 **순회 순서를 더는 반영하지 않는다**(아무도 안 읽어서 안전했고, 그래서 여태 안 들켰다) ⒝★**«보고를 비교 가능»하게 할 뿐 «검사를 결정적»으로 만들지는 않는다** — `examined` 수·등장 머지 등 저장소 상태 의존은 그대로다(과장 금지) ⒞★**이 검사기만** 고쳤다 — 형제 검사기는 **보기엔** 안전했으나(정렬된 missing 목록·정렬된 파일 순회) **재지는 않았다**.
9+
- ★**비용 유의차 없음**: 전 **2.09/2.51/2.41s** ↔ 후 **2.52/1.90/1.65s**(구간 겹침).
10+
- ★**노력도**: 제안 추정 **S** 가 맞았다 — 실제 일은 **측정**(8회 × 2방향)이었고, 그것이 「정렬해야 한다」를 「정렬 안 돼 있었고 그 크기는 이만큼」으로 바꾼다.
11+
- 검증: DoD 9명령 · 아래 절.
12+
213
## [2026-09-19] 적재 가능 집합을 «로더에서 읽을까» — ★**아니다, 재유도를 유지한다**(rustjava-loadable-set-from-loader-vs-rederive-decision)
314
- 무엇을: 채택 제안 `2026-09-18-named-exception-classes-are-loadable#p2`(worklog json 기록). ★**순수 결정 회차 — `.rs` 0줄 · `scripts/` 0줄.** 산출 = `docs/loadable-set-source-of-truth.md`(선례 = `docs/test-data-target-policy.md`).
415
- ★★**전제부터 확인했다 — 「4결함 중 3이 재유도」는 «참»이다.** 산문이 아니라 **고침 커밋 `89c2e83c` 에서** 갈랐다: ⑴`as_proto` 전용 → `list_proto` 3건 누락 ⑵한 `impl` 의 첫 `name:` 오귀속 ⑶짧은 이름 키 충돌 = **재유도(loadable) 3건** · ⑷줄 단위 스캔이 rustfmt 가 쪼갠 34건 누락 = ★**호출부 스캔(named) 1건**.

‎STATE.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
(둘 다 이것보다 오래됐고 MERGEABLE/CONFLICTING 처분이 이미 걸려 있다). 겹침은 전부 **append 형 합집합**이라 해소는 기계적이다)
88

99
## 완료
10+
- [2026-09-19-partial-clone-blob-vs-absence-p0] ★★**검사기가 실행마다 자기와 불일치했다 — 경로 루프 `sorted()` 한 줄.** 채택 제안 `2026-09-19-partial-clone-blob-vs-absence#p0`. ★**1파일 +7/−1.**
11+
★**전제 재측**: 오늘 main 에서 8회에 **순서 2종**(6+2) · 같은 6건.
12+
★★**자리가 제안과 다르다**: 「print 가 set 순서」가 아니라 ★**바깥 «경로» 루프**가 set 이었다(이름은 이미 `sorted`).
13+
★**양방향**: 되돌리면 **2종** ↔ 넣으면 **8/8 동일** · 내용 불변(6건 · rc=1 · 요약 동일). ★rc 로는 red 를 못 만든다(순서만 바뀐다) — 축을 «순서 종수»로 세웠다.
14+
★**대가**: 순회 순서 미반영 · ★**«비교 가능»일 뿐 «결정적»이 아니다**(저장소 상태 의존 그대로) · **이 검사기만** 고쳤다(형제는 미측정).
1015
- [rustjava-loadable-set-from-loader-vs-rederive-decision] ★★**적재 가능 집합은 «재유도»를 유지한다 — 로더에서 읽지 않는다.** 채택 제안 `2026-09-18-named-exception-classes-are-loadable#p2`. ★**순수 결정 · 코드 0줄** · 산출 = `docs/loadable-set-source-of-truth.md`.
1116
★**전제 확인**: 「4중 3이 재유도」는 **참**(고침 커밋 `89c2e83c` 에서 갈랐다 — loadable 3 · named 1).
1217
★★**그 1건이 결정한다**: `named`(코드가 무엇을 넘기는가)는 **로더가 답할 수 없다** ⇒ 로더 읽기는 **파서 하나를 없앨 뿐 파싱을 못 없앤다**(그 절반에서 형제 회차가 ★**4시간 31분** 뒤에 또 잡았다 — ★**「다른 함수 8종 41자리」**를 쓸어담는 앵커 함정이고, 세면 **33** · 맞는 답은 **0** 이다).
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"date": "2026-09-19",
3+
"taskId": "2026-09-19-partial-clone-blob-vs-absence-p0",
4+
"summary": "The merge-dropped-symbols check emitted its findings in hash-seed order, so two runs of the same version disagreed. Re-measured on today's origin/main: eight runs produced two distinct orderings of the same six findings. Sorting the path loop makes the report comparable; content, rc and summary are unchanged.",
5+
"measurements": {
6+
"runs_per_direction": 8,
7+
"orderings_before": 2,
8+
"orderings_after": 1,
9+
"findings": 6,
10+
"rc_before": 1,
11+
"rc_after": 1,
12+
"files_changed": 1,
13+
"lines_added": 7,
14+
"lines_removed": 1,
15+
"runtime_before_seconds": [2.09, 2.51, 2.41],
16+
"runtime_after_seconds": [2.52, 1.90, 1.65]
17+
},
18+
"verification": [
19+
"premise re-measured on origin/main @ ad9eb1a6: 8 runs under PYTHONHASHSEED=random over e53b2142^..e53b2142 gave 2 orderings (6 + 2) of the same 6 findings",
20+
"axis, both directions on the product script: with the sort reverted 8 runs give 2 orderings; with it, 8/8 identical",
21+
"content unchanged in both directions: 6 findings, rc=1, identical summary line",
22+
"cost: before 2.09/2.51/2.41s vs after 2.52/1.90/1.65s, overlapping"
23+
],
24+
"changes": [
25+
"scripts/check-merge-dropped-symbols.py - the path loop iterates sorted(changed) instead of the set, with the measurement recorded in a comment",
26+
"docs/worklog/2026-09-19-sorted-findings.{md,json}, REPORT.md, STATE.md"
27+
],
28+
"issues": [
29+
"The proposal located the problem at the print site ('accumulated in a set and printed in iteration order'); names were already sorted there. The unordered part was the outer path loop over a set built from two git diff --name-only results.",
30+
"This makes the report comparable, not the check deterministic: examined counts and which merges appear still depend on repository state.",
31+
"Only this checker was changed. The sibling check appeared safe on inspection (sorted missing list, sorted file walk) but was not measured."
32+
],
33+
"adoptedProposals": [
34+
"2026-09-19-partial-clone-blob-vs-absence#p0"
35+
],
36+
"proposals": []
37+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# 2026-09-19 — The check disagreed with itself between runs. One `sorted()` fixed it.
2+
3+
Round: `2026-09-19-partial-clone-blob-vs-absence-p0`
4+
Adopted proposal: `2026-09-19-partial-clone-blob-vs-absence#p0` —
5+
*"Sort the checker's findings so two runs can be compared."*
6+
7+
## The premise, re-measured on today's `origin/main`
8+
9+
Eight runs of the **unchanged** checker over the same range (`e53b2142^..e53b2142`), under
10+
`PYTHONHASHSEED=random`, hashing the output:
11+
12+
```
13+
6 16efef4228f50b1147cb57f8e1aced09
14+
2 9b1105a77918f30b31c42c890815d10c
15+
```
16+
17+
Two orderings of the same six findings. The proposal is right, and it is right for the reason it
18+
gives: this cost a real round — a before/after diff of this checker looked like a regression until
19+
the *unchanged* version was shown to disagree with itself.
20+
21+
## Where it came from, which is not quite where the proposal looked
22+
23+
The proposal says *"Findings are accumulated in a set and printed in iteration order."* Close, but
24+
the print site was already fine: names are emitted through `sorted(theirs_symbols - …)`, so findings
25+
**within a path** were always ordered. What was unordered is the **path loop** —
26+
`for path in filter(None, changed)`, where `changed` is a `set` built from two `git diff --name-only`
27+
results. So the fix is on the outer loop, not at the print:
28+
29+
```python
30+
for path in sorted(filter(None, changed)):
31+
```
32+
33+
**Changed: 1 file, +7/−1 lines** (one of them code, six a comment recording the measurement).
34+
35+
## Axis — bidirectional, on the product script
36+
37+
| form | 8 runs, `PYTHONHASHSEED=random` |
38+
|---|---|
39+
| that line reverted | **2 orderings** (6 + 2) |
40+
| with it | **1 ordering** (8 / 8) |
41+
42+
Content is untouched in both directions: 6 findings, `rc=1`, and the same summary line
43+
`8 merge(s) in e53b2142^..e53b2142: 6 definition(s) dropped without a trailer`.
44+
45+
**On the acceptance wording**: it asks for "revert it and get red". This change has no verdict to
46+
flip — it changes output *order*, not outcome, so `rc` is 1 before and after by design. The axis is
47+
therefore the ordering count above, measured in both directions on the real script.
48+
49+
## What this costs
50+
51+
- **The report no longer reflects traversal order.** Nothing reads it, which is exactly why this was
52+
safe — and also why it was never noticed.
53+
- **It makes the report comparable, not the check deterministic.** `examined` counts, which merges
54+
appear, and everything else that depends on repository state still vary with the repository. A
55+
future round that diffs two runs across *different* trees will still see real differences; this
56+
only removes the false ones.
57+
- **It fixes this checker only.** The sibling checks were not audited for the same shape in this
58+
round; `check-named-exception-classes-are-loadable.py` emits its missing list through `sorted(…)`
59+
and walks files through a sorted `rust_files()`, so it appeared safe, but that is an observation
60+
in passing rather than a measurement.
61+
- Runtime: **no significant change** — before 2.09 / 2.51 / 2.41 s, after 2.52 / 1.90 / 1.65 s
62+
(overlapping). Sorting a set of a few dozen paths is not where this check spends its time.
63+
64+
## Effort
65+
66+
The proposal estimated **S** and that was right: the change is one line. The round's actual work was
67+
the measurement — 8 runs × 2 directions — which is what turns "should be sorted" into "was unordered,
68+
here by how much".

‎scripts/check-merge-dropped-symbols.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,13 @@ def check(merge):
243243
accounted = excused(merge)
244244
if excuses_everything(accounted):
245245
return [], 0
246-
for path in filter(None, changed):
246+
# Sorted because `changed` is a set: without this the findings come out in whatever order the
247+
# hash seed produced, and two runs of the *same* version disagree. Measured on `origin/main`
248+
# before this line: eight runs under PYTHONHASHSEED=random gave two distinct orderings of the
249+
# same six findings (6 + 2). That cost a real round -- a before/after diff of this check looked
250+
# like a regression until the unchanged version was shown to disagree with itself. Names are
251+
# already sorted within a path below; this makes the whole report comparable.
252+
for path in sorted(filter(None, changed)):
247253
theirs_symbols = symbols(theirs, path)
248254
if theirs_symbols is None:
249255
continue

0 commit comments

Comments
 (0)