Skip to content

Commit 0fe03dc

Browse files
author
jun0
committed
[rustjava-jvm-exception-throws-instead-of-unwrap] merge origin/main — 승인된 원장 2파일 합집합
형제 착지(#75 ddc6c4c · #78 97d2258)로 생긴 원장 재충돌. 게이트③ 2-c⒜ 승인 범위(원장 파일 한정). 양쪽 항목 전건 보존·시간순(최신 상단) · 한쪽 통째 채택(--ours/--theirs) 0. 보존 증명: ours추가 REPORT 18 / STATE 9 · theirs추가 37 / 15 — 소실 0/0 · 충돌 마커 0. 계약 12: 해소면 밖 변경 0 — jvm/src/jvm.rs · jvm/tests/test_exception_construction.rs · scripts/check-named-exception-classes-are-loadable.py · worklog 쌍이 핀(394184b) 대비 바이트 동일.
2 parents 394184b + 97d2258 commit 0fe03dc

7 files changed

Lines changed: 381 additions & 5 deletions

‎REPORT.md‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,42 @@
1616
※그 술어가 ★**«주석 안의 토큰»도 센다**는 것도 이때 드러났다(주석 한 줄 때문에 2로 읽혔다 → 문구 교체). 형제 제안을 이어받는 회차 몫이다.
1717
- 검증: DoD 9명령 · 아래 절.
1818
- ★후속 추천: **퇴화 재귀에 바닥을 깔 것인가**(M · 재진입 가드 ↔ 비-예외 실패 표현 — 후자는 460자리를 조용히 바꾼다). 상세 = `docs/worklog/2026-09-19-exception-reports-instead-of-aborting.md`.
19+
## [2026-09-19] 부분 클론도 «거절»할까 — ★**아니다. 모호했던 것은 «환경»이 아니라 «호출 하나»였다**(rustjava-partial-clone-refusal-decision)
20+
- 무엇을: 채택 제안 `2026-09-18-merge-drops-no-silent-git-failure#p0`(worklog json 기록). ★**거절하지 않는다** — 대신 `symbols()` 가 실패한 `git show` 를 «부재»로 읽기 «전»에 `git ls-tree` 로 그 경로가 트리에 있는지 묻는다. ★결정을 `preflight()` docstring 에 못박았다(다음 사람이 다시 묻지 않도록).
21+
- ★★**추측하지 않고 «진짜 부분 클론»을 만들어 쟀다**(`--filter=blob:none` · 범위는 알려진 사고 머지 `e53b2142^..e53b2142`):
22+
23+
| 클론 | promisor | 결과 | 시간 |
24+
|---|---|---|---|
25+
| 완전 | — | `6 dropped` · **rc 1** | 2.5s |
26+
| blobless | ★**도달 가능** | `6 dropped` · **rc 1** — ★**완전히 동일** | 15.7s |
27+
| blobless(신선) | ★**도달 불가** | `0 dropped` · ★★**rc 0 = green** | 8.3s |
28+
29+
- ★★**그래서 제안은 «절반만» 맞다**: 「부분 클론이면 아무것도 없는 것처럼 보인다」는 **그 자체로는 거짓**이다 — promisor 가 닿으면 git 이 blob 을 투명하게 받아 와 답이 **바이트 동일**하다. ⇒ ★**거절은 «돌아가는 설정»을 막는 것**이고, shallow 와 달리 부분 클론은 **없는 것을 가져올 수 있다**.
30+
★**그러나 조용한 green 은 «실재»한다** — 조건이 좁을 뿐(promisor **도달 불가**). 그 상태에서 검사기는 `✓ … (3 file(s) examined)` 를 찍고 **rc 0** 으로 끝냈다(완전 클론은 6건 보고).
31+
- ★**방법 주의(자기정정)**: 첫 오프라인 측정은 **오염됐다** — 앞선 온라인 실행이 그 blob 들을 이미 캐시해 «맞는 답»이 나왔다. 위 수는 **신선한 클론 + 읽기 «전»에 원격을 깨뜨린** 형상에서 다시 잰 것이다.
32+
- ★**위험이 실현되나 — 실측 0**: `.github` 어디에도 `filter:` 가 **없고**(`merge_drops` 는 `fetch-depth: 0`) ⇒ 오늘 이 거짓 green 은 «개발자 노트북 + blobless + 오프라인»에서만 난다. ★그것이 **거절을 고르지 않은 이유**이지, **모호함을 남길 이유는 아니다** — 닫는 비용이 git 호출 하나로 드러났기 때문이다.
33+
- ★**왜 `ls-tree` 인가(대안 둘을 각각 기각한 근거)**: ⒜**거절** — 탐지는 된다(`remote.origin.partialclonefilter = blob:none` · ★`rev-parse --is-shallow-repository` 는 **false** 라 현행 preflight 가 못 잡는다)지만 **맞는 답을 내는 경우까지 막는다** ⒝**에러 문면 대조** — 두 실패는 문면으로 갈리지만(`does not exist in` ↔ `could not fetch … from promisor remote`) **둘 다 exit 128** 이고, preflight 를 만든 회차가 이미 «git 판올림에 약하다»며 미뤘다 ⒞★**`ls-tree` 는 «트리 객체»로 답한다** — 부분 클론은 blob 이 없어도 **트리는 갖는다**. 두 클론에서 **동작 동일** 실측(있으면 1항목·없으면 빈 출력·rc 0).
34+
- ★**양방향 축**(제품 스크립트): 거짓 green 형상 — 전 **rc 0 `0 dropped`** ↔ 후 ★**rc 2 `cannot measure: …:jvm-bytecode/src/class_definition.rs is in that tree but its content could not be read…`**. ★**과차단 0**: blobless + promisor 도달 가능은 고친 뒤에도 **rc 1 · 6 dropped** · 완전 클론도 **불변**.
35+
- ★**비용 유의차 없음**: 같은 범위 3회씩 — 전 **7.78/7.99/7.05s** ↔ 후 **6.39/6.72/8.06s**(구간 겹침) · DoD 기본 범위 **0.46s → 0.33s**. 추가 호출은 `show` 가 **이미 실패한** 경로에서만 돈다.
36+
- ★**재다가 발견했고 «고치지 않았다»**: 이 검사기의 **출력 순서가 실행마다 다르다**(findings 가 set 에서 나온다). `origin/main` 판본을 `PYTHONHASHSEED=random` 으로 5회 돌려 **순서 2종**(4+1). rc·집합은 동일하고 **줄 순서만** 움직인다 ⇒ ★**선행 결함**이고, 전/후 diff 에서 잠깐 «회귀»처럼 보였기에 적는다(후속 카드).
37+
- 검증: DoD 9명령 · 아래 절.
38+
- ★후속 추천: **findings 를 정렬해 두 실행을 비교 가능하게 할 것인가**(S). 상세 = `docs/worklog/2026-09-19-partial-clone-blob-vs-absence.md`.
39+
40+
## [2026-09-18] 리터럴이 «아닌» 이름으로 exception() 을 부르는 자리는 몇 개인가 — ★**0 이다**(rustjava-count-nonliteral-exception-call-sites)
41+
- 무엇을: 채택 제안 `2026-09-18-named-exception-classes-are-loadable#p0`(worklog json `adoptedProposals` 기록). ★**순수 측정 회차 — `.rs` 0줄 · `scripts/` 0줄.** 산출은 «수»와 «술어»다.
42+
- ★**답**: bare `exception(` **847** = 정의 **1** + ★**리터럴(java/javax) 846** + 리터럴(그 밖) **0** + ★★**비리터럴 «0»**.
43+
⇒ 검사기가 보는 집합과 실제 호출부 집합이 **지금은 일치한다** — 사각의 «크기»는 **0**이다.
44+
- ★`literal_other` 도 **0** 이라 따로 적는다: 검사기는 `java/` 접두가 아닌 리터럴(`org/rustjava/…`)도 건너뛰는데 **그런 것도 없다** ⇒ 새는 축은 «접두»가 아니라 «런타임 조립»뿐이고, 그것이 0이다.
45+
- ★★**술어를 검사기의 것과 «같게» 맞췄다**(수가 비교 가능해야 한다): 같은 파일 집합(`target/`·`.git` 가지치기) · 같은 전파일 매칭(rustfmt 줄바꿈을 넘는다). 다른 것은 둘뿐 — ⑴`java/` 요구를 **뺐다**(「실을 수 있나」가 아니라 「이름이 있기는 한가」를 묻는다) ⑵★`exception(` 부분일치가 **다른 함수 8종**(`assert_exception(`·`suppress_io_exception(` 등 **41자리**)을 함께 쓸어담는다 — 그 첫 인자는 `jvm` 이지 클래스 이름이 아니다. ⇒ **분리했다**. 안 갈랐으면 ★**M=33 이라는 «틀린 답»**이 나온다.
46+
- ★★**「내가 찾은 게 전부다」로 주장하지 않았다 — 술어를 양방향으로 시험했다**:
47+
⒜**대조군** — 한 줄에 든 리터럴만 세면 **812**, 이는 검사기 docstring 이 적은 **자기 초판 수**(846 − rustfmt 가 쪼갠 34)와 **정확히 일치**한다 ⇒ 파일 집합·앵커가 같다는 증거.
48+
⒝**개악 주입**(제품 파일 `jvm/src/jvm.rs` · 측정 후 원복 · 트리 클린): 변수 · `&format!` · `const` · **raw string** → 전건 `nonliteral`(**0→4**) · 비-java 리터럴 → `literal_other`(**0→1**). ★raw string 이 «리터럴»이 아니라 «사각»으로 잡히는 것이 의도한 편향이다 — **모르는 철자는 안전 칸이 아니라 사각 칸으로 떨어진다**.
49+
⒞**음성 탐침**: `exception (`(공백) **0** · `Jvm::exception` 값·UFCS 전달 **0** · `macro_rules!` 보유 파일 **2**(어느 쪽도 식별자를 조립하지 않는다).
50+
- ★**못 보는 것**: ⑴★**매크로는 «본문에서 한 번» 세어진다** — `arrays.rs` 의 매크로 4개가 `exception(` **3자리**를 갖고 **22회** 전개되므로 전개 기준이면 **865**다(846 아님). ★이름은 전부 리터럴이라 **답(M=0)은 안 바뀐다** — 사각이 아니라 «단위» 차이이고, 검사기도 이 회차도 소스 단위다. ⑵토큰 붙이기 매크로가 식별자 `exception` 을 조립하면 어떤 텍스트 술어도 못 본다(이 트리에선 0 — 바닥이지 증명이 아니다) ⑶「리터럴인데 오타」는 검사기의 기존 한계 그대로 ⑷`new_class(`·`find_class(` 는 제안의 요지 밖이라 세지 않았다.
51+
- ★**제안 판정**: 전제(「아무도 크기를 모른다」)는 **참이었다** — 아무도 재지 않았다. 답이 0이라는 것은 검사기의 바닥이 «허구»라는 뜻이 아니라 ★**지금은 «딱 맞는다»**는 뜻이다. 다음 회차가 `jvm.exception(&name, …)` 을 쓰는 것을 막는 것은 아무것도 없고, 위 술어가 그것을 알아챌 물건이다.
52+
- ★**게이트로 «승격하지 않았다»** — 제안이 요구한 것은 «계수»이지 «관문»이 아니고, 베이스라인 0 인 관문은 **자기 대가**(변수로 이름을 넘기는 정상 리팩터가 red 가 된다)를 갖는 별 결정이다. ⇒ 후속 제안 카드로 남겼다(계약 「범위를 넓히지 마라」).
53+
- 검증: 아래 «검증» 절 참조(DoD 9명령).
54+
- ★후속 추천: **베이스라인이 0인 지금 이 술어를 관문으로 올릴 것인가**(S). 상세 = `docs/worklog/2026-09-18-nonliteral-exception-call-sites.md`.
1955

2056
## [2026-09-18] 「조용한 실패」를 잡는 검사기에 «조용히 통과하는 길»이 있었다 (rustjava-merge-dropped-symbols-checker-swallows-git-failures)
2157
- 무엇을: `scripts/check-merge-dropped-symbols.py` 의 `run()` 이 git 실패를 `None` 으로 삼키고 호출부가 전부 `(… or "")` 로 받아 ★**「git 이 못 답했다」가 「없다고 답했다」로 접혔다** ⇒ `✓ (0 file(s) examined)` · **rc=0**.

‎STATE.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@
1616
★**대가**: 실패 시 **다른 클래스의 예외**가 온다 ⇒ 클래스로 분기하는 **제품 12자리**는 못 잡고 전파한다(죽는 것보다 낫지만 무해하지 않다).
1717
★**불변**: 퇴화 경우(폴백 클래스 자체 부재)는 **무한 재귀**이고 옛 unwrap 도 못 막았다 — ★호출그래프에서 읽었고 **실측 아님**(후속 카드).
1818
★**자기 diff 밖 파급 둘**: ⑴검사기 문면 3자리가 거짓이 돼 **문면만** 고쳤다(술어 무접촉 · 축 양방향 재검증 rc=1/rc=0) — 잠금의 이유가 「죽는다」에서 ★**「틀린 예외가 온다」**로 바뀐다 ⑵★형제 회차의 비리터럴 **0 → 1**(이 테스트가 그 1이다) — ★**그 회차가 관문을 «안» 건 판단이 하루 만에 값을 했다**(걸었으면 이 테스트가 막혔다).
19+
- [rustjava-partial-clone-refusal-decision] ★★**부분 클론을 «거절하지 않는다» — 모호했던 것은 환경이 아니라 «호출 하나»였다.** 채택 제안 `2026-09-18-merge-drops-no-silent-git-failure#p0`.
20+
★**진짜 blobless 클론으로 쟀다**: promisor **도달 가능**이면 답이 **완전 클론과 동일**(rc 1 · 6 dropped · 15.7s vs 2.5s) ⇒ ★거절은 «돌아가는 설정»을 막는 것.
21+
★**그러나 조용한 green 은 실재**: 신선한 blobless + promisor **도달 불가** → `0 dropped` · ★**rc 0**(완전 클론은 6건).
22+
★**처방**: `symbols()` 가 실패한 `show` 를 부재로 읽기 전에 **`ls-tree`** 로 트리 존재를 묻는다(부분 클론도 **트리는 갖는다**) ⇒ 문면 대조 없이 갈린다.
23+
★**양방향**: 전 rc 0(거짓 green) ↔ 후 ★**rc 2 «못 쟀다»** · ★과차단 0(도달 가능 blobless 는 여전히 rc 1) · 완전 클론 불변 · **비용 유의차 없음**(구간 겹침).
24+
★**위험 실현 0**: `.github` 에 `filter:` **0건**(`merge_drops` 는 `fetch-depth: 0`) — 그것이 «거절 안 함»의 근거이지 «모호함을 남길» 근거는 아니다.
25+
★**선행 결함 발견(미수정)**: 출력 순서가 실행마다 다르다(set) — `origin/main` 판본 5회에 순서 2종. rc·집합 불변(후속 카드).
26+
★결정을 `preflight()` docstring 에 못박았다 — 다음 회차가 같은 질문을 다시 하지 않도록.
27+
- [rustjava-count-nonliteral-exception-call-sites] ★★**사각의 «크기»를 쟀다 — 비리터럴 exception() 호출부는 «0» 이다.** 채택 제안 `2026-09-18-named-exception-classes-are-loadable#p0`. ★**순수 측정 · `.rs` 0줄 · `scripts/` 0줄.**
28+
★**수**: bare `exception(` **847** = 정의 1 + **리터럴 846** + 그 밖 리터럴 **0** + ★**비리터럴 0** ⇒ 검사기가 보는 집합 = 실제 호출부 집합(지금은 일치).
29+
★★**술어를 갈라야 답이 맞는다** — `exception(` 부분일치가 `assert_exception(` 등 **다른 함수 8종 41자리**(첫 인자가 `jvm`)를 쓸어담는다. 안 갈랐으면 ★**M=33 이라는 틀린 답**이었다.
30+
★**양방향으로 술어를 시험했다**: 대조군 = 한 줄 리터럴만 세면 **812** = 검사기 초판 수와 정확히 일치 · 개악 주입(변수·`format!`·`const`·raw string) → 전건 `nonliteral` **0→4**, 비-java 리터럴 → `literal_other` **0→1**, 원복 후 **0/0**·트리 클린.
31+
★**단위 주의**: 매크로 본문 **3자리 × 22전개** ⇒ 전개 기준이면 **865**(소스 기준 846). 이름이 전부 리터럴이라 **답은 불변** — 사각이 아니라 단위 차이다.
32+
★**잃는 것**: 토큰 붙이기 매크로는 어떤 텍스트 술어도 못 본다(이 트리 0) · 「리터럴인데 오타」는 종전 한계 그대로 · `new_class(`·`find_class(` 는 요지 밖이라 미계수.
33+
★**게이트 승격은 «안 했다»** — 제안이 요구한 것은 계수이고, 베이스라인 0 관문은 별 결정이라 후속 카드로 남겼다.
1934
- [rustjava-merge-dropped-symbols-checker-swallows-git-failures] ★★**「조용한 실패」 검사기에 «조용히 통과하는 길»이 있었다 — 닫았다.**
2035
★**재현 = 진짜 얕은 클론**(`--depth 10`): 전 **rc=0** `✓ 56bb54fa (0 file(s) examined)` ↔ ★완전 클론에선 **examined 20** ⇒ 20→0 으로 접히고 green. 후 **rc=2 `cannot measure: shallow clone…`**.
2136
★raise 경로도 쟀다 — 범위 오류·루프 내 diff 실패·비-git **전부 rc=2**(git stderr 동봉).
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"date": "2026-09-18",
3+
"taskId": "rustjava-count-nonliteral-exception-call-sites",
4+
"summary": "Counted every exception( call site and classified its first argument. Literal java/javax: 846 (exactly what the checker reads). Non-literal (name built at run time): 0. The blind spot the adopted proposal asked about is empty today; the predicate that says so was validated by a control (reproduces the checker's pre-fix 812) and a five-shape mutation probe.",
5+
"measurements": {
6+
"bare_exception_sites_total": 847,
7+
"definition": 1,
8+
"literal_java": 846,
9+
"literal_other": 0,
10+
"nonliteral": 0,
11+
"suffixed_helper_sites_excluded": 41,
12+
"suffixed_helper_nonliteral_if_wrongly_counted": 33,
13+
"single_line_literal_control": 812,
14+
"macro_body_sites": 3,
15+
"macro_invocations": 22,
16+
"expansion_basis_total": 865,
17+
"literal_java_in_product": 781,
18+
"literal_java_in_tests": 65
19+
},
20+
"verification": [
21+
"control: single-line-only literal count = 812 = the checker's own pre-fix figure (846 - 34 rustfmt-split), same file set and anchor",
22+
"mutation probe in jvm/src/jvm.rs: variable / format! / const / raw-string -> nonliteral 0->4; non-java literal -> literal_other 0->1; reverted, tree clean, back to 0/0",
23+
"negative probes: 'exception (' with space = 0, Jvm::exception as value or UFCS = 0, macro_rules! files = 2 and neither pastes an identifier"
24+
],
25+
"changes": [
26+
"docs/worklog/2026-09-18-nonliteral-exception-call-sites.{md,json} (this pair)",
27+
"REPORT.md, STATE.md — round record",
28+
"no .rs and no scripts/ changes: this is a measurement round"
29+
],
30+
"issues": [
31+
"Counts are in source units, not expansion units: 3 exception( sites live in macro bodies invoked 22 times, so an expansion-basis total would be 865. All literal either way, so the answer M=0 is unaffected.",
32+
"A token-pasting macro that builds the identifier `exception` would be invisible to any text predicate; measured 0 in this tree but it is a floor, not a proof."
33+
],
34+
"adoptedProposals": [
35+
"2026-09-18-named-exception-classes-are-loadable#p0"
36+
],
37+
"proposals": [
38+
{
39+
"title": "Decide whether nonliteral exception() call sites should be a check, now that the baseline is 0",
40+
"plainSummary": "Right now every place that raises a Java error spells the class name out in full, so the existing safety check sees all of them. Nothing stops someone from building a name at run time in future, which would slip past unseen.",
41+
"userBenefit": "Keeps the guarantee that an unknown class name throws a Java exception instead of crashing the whole runtime, even as new code is written.",
42+
"why": "This round measured the blind spot at exactly 0 and produced the predicate that detects it (control-tested against the checker's own numbers, plus a five-shape mutation probe). A gate is therefore cheap to add and would start green. The reason it was not added here: the adopted proposal asked for a count, not a gate, and a gate whose baseline is 0 has its own cost — it turns a legitimate future refactor (passing a name through a variable) into a red that must be argued down, and this repo's rule is that a lock should be decided on its own merits rather than added because the number happened to be convenient.",
43+
"tradeoff": "Adding it costs one more DoD command and makes run-time-assembled names a build failure rather than a review comment; not adding it means the floor stays unmeasured between rounds and the next non-literal call site lands silently.",
44+
"effort": "S",
45+
"target": "scripts/check-named-exception-classes-are-loadable.py, .github/workflows/rust.yml, CLAUDE.md"
46+
}
47+
]
48+
}

0 commit comments

Comments
 (0)