Skip to content

Commit d823071

Browse files
author
jun0
committed
[rustjava-adopt-class-format-mutation-audit-p0-fix] test(fixtures): 판정식을 given 에서 파생시킨다 — 14/18 의 공허를 끊는다
1 parent 377d58b commit d823071

5 files changed

Lines changed: 222 additions & 124 deletions

File tree

‎REPORT.md‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
11
# REPORT
2+
## [2026-09-17] 「전건 single-defect」는 «측정»이 아니었다 — 판정식을 `given` 에서 파생시킨다 (rustjava-adopt-class-format-mutation-audit-p0-fix)
3+
- 무엇을: 게이트② **request-changes** 승계(PR #63 · 핀 `377d58b1`). ★**검수자 지적이 옳았다** — 고친 것은 감사 스크립트 **1파일**이고 제품 Rust 는 **0줄**이다.
4+
- ★★**급소는 한 줄이다**: `repaired` 를 **정본 인자로 다시 짓고** 있었다 ⇒ 픽스처에 둘째 결함이 무엇이 들어오든 `repaired` 와 `canonical` 이 **똑같이 버려서** 항상 같았다.
5+
판정식이 「single-defect 인가」를 묻는데 **입력이 이미 single-defect 로 만들어져 있었다**(순환). ★18건 중 **14건**이 그 형태였다.
6+
- ★**처방은 «발명»이 아니라 «옮겨오기»다** — `indy` 근접실패 4건이 이미 옳은 형태(`dict(given, **{축: 정본})`)였다.
7+
그것을 `add()` 한 곳으로 모아 **4족 전건**이 지나게 했고, `given` 은 `inspect.signature(...).bind(*spec)` 로 **이름에 묶는다**
8+
⇒ 생성기에 인자가 하나 늘어도 `repaired` 가 **자동으로 물려받는다**(종전엔 튜플 인덱스라 조용히 빠졌다).
9+
- ★★**비대칭이 이 검사의 전부다**: `repaired` 는 `given` 에서 파생하고 `canonical` 은 **파생하지 않는다**.
10+
둘 다 파생하면 둘째 결함이 양쪽에 실려 다시 상쇄된다. ⇒ 그 비대칭을 지키는 가드로 **`AUDIT SPEC STALE`(rc=2)** 을 넣었다 —
11+
스펙이 «정본을 모르는 인자»를 설정하면 **판정하지 않고 멈춘다**(추측하지 않는다).
12+
- ★★**족마다 개악을 놓았다**(한 족의 red 를 전체로 일반화하지 않는다 — 그 일반화가 이번 결함을 살렸다):
13+
14+
| 개악(둘째 결함) | 종전 | ★이 회차 |
15+
|---|---|---|
16+
| **M-A** `ldc` · `LdcDynamicOldMajor` 에 중복 BSM | rc=0 통과 | ★**rc=1 MULTI-DEFECT**(13B) |
17+
| **M-B** `cp` · 생성기에 `major` 추가 + `UnreferencedTag13` major 45 | rc=0 통과 | ★**rc=2 AUDIT SPEC STALE**(정본 미상 ⇒ 판정 거부) |
18+
| **M-B2** 위 + 감사기에 `major` 정본을 알려 줌 | — | ★**rc=1 MULTI-DEFECT**(1B) |
19+
| **M-C** `indy` 근접실패 · `NotInvokeStaticFactory` 에 잘못된 메서드명 | rc=1 | rc=1 (★**일하던 족은 그대로 일한다**) |
20+
| **M-D** `indy` LINKED · `MakeConcatWrongDescriptor` 에 정적 인자 | rc=0 통과 | ★**rc=1 MULTI-DEFECT**(4B) |
21+
22+
- ★★**잃은 것을 숨기지 않는다 — 그리고 «수가 내려가지 않았다»는 사실도 그대로 적는다.**
23+
⒜커밋된 픽스처 18건은 **여전히 전건 single-defect**(rc=0)다. ★**내려간 것은 «통과 수»가 아니라 «통과할 수 있는 입력의 집합»**이다 —
24+
구조적으로 실패 불가이던 사례가 **14 → 0**. ⒝대신 판정이 **`CANON` 표가 옳다는 것에 의존**하게 됐다: 표가 틀리면 종전엔 조용했을 자리가 이제 **거짓 MULTI-DEFECT** 로 운다.
25+
⒞생성기가 자라면 `AUDIT SPEC STALE` 이 **게이트를 막는다**(고의다 — 「모르면 멈춘다」). ⒟시험 시간이 늘었다: 4족 개악 5회 = 스크래치 사본 5벌 · 감사 10회(전/후) — **실측 약 40초**.
26+
- ★**상시 CI 검사는 «이 회차에서 만들지 않았다»** — 공허한 검사를 DoD 에 박는 것이 가장 비싼 결말이라, 판정식을 먼저 조이고 검사 신설은 다음 회차로 둔다.
27+
- 검증: 정상 형상 **검사 18 · 무결함 5 · rc=0** · 개악 5종 전건 red · `cargo test --all` **Rust 무접촉이라 불변**.
28+
229
## [2026-09-17] 픽스처가 «정확히 한 곳만» 망가졌는가 — 감사의 «나머지 반쪽» (rustjava-adopt-class-format-mutation-audit-p0)
330
- 무엇을: 채택 제안 `2026-09-16-class-format-mutation-audit#p0`. ★**제품 코드 0줄**(감사 스크립트 1개).
431
- 왜: 종전 감사는 「테스트가 자기 가지의 개악에 죽는가」를 물었다. 이 회차는 ★**「픽스처가 정확히 한 곳만 망가졌는가」**를 묻는다.
@@ -7,10 +34,10 @@
734
생성기가 결함을 **인자로 받으므로** 더 강한 검사가 더 싸게 된다:
835
`generator(결함) == 커밋본`(생성기가 여전히 그 파일을 설명한다) **그리고** `generator(수리) == generator(표준)`(그 밖에 차이가 없다).
936
★**강한 이유**: 우리 로더가 «마침» 신경 쓰지 않는 둘째 결함은 로드 검사를 통과하지만 이 검사는 통과하지 못한다. ★**싼 이유**: JVM 이 필요 없다.
10-
- ★★**결과: 검사 18건 «전건» single-defect · 결함 없는 유효 파일 5건 · rc=0.**
11-
(`indy` 근접실패 6 · `ldc` 9 · `cp` 3 / 무결함 5 = `MakeConcat`·`LdcMethodHandle`·`LdcMethodType`·`LdcDynamic`·`Ldc2WDynamic`)
12-
⇒ 제안이 예고한 「'이미 괜찮다'는 한 줄」이 맞았다. ★**그래도 값한다** — 그 규율은 회차마다 «적용»돼 왔을 뿐
13-
★**코퍼스 전체로 «측정»된 적이 없어**, 지금까지 그것은 «사실»이 아니라 «습관»이었다.
37+
- ★★**[교정 2026-09-17 · 게이트② request-changes · `-fix` 회차] 이 줄의 «전건»은 «측정»이 아니었다.**
38+
당시 판정식은 18건 중 ★**14건에서 `repaired` 를 정본 인자로 «다시 지어»** 둘째 결함을 버렸다 ⇒ 구조적으로 MULTI-DEFECT 를 낼 수 없었다.
39+
★**실측된 것은 4/18**(`indy` 근접실패)이고 나머지 14는 공허하게 통과했다. 정정된 결과와 근거는 **맨 위 `-fix` 항목**에 있다.
40+
(족 구성은 그대로다: `indy` 근접실패 6 · `ldc` 9 · `cp` 3 / 무결함 5 = `MakeConcat`·`LdcMethodHandle`·`LdcMethodType`·`LdcDynamic`·`Ldc2WDynamic`)
1441
- ★**덮지 않는 것을 스크립트 머리에 «적었다»**(조용히 건너뛰지 않았다): javac 산출물(결함 0) · 적법하나 미구현(결함 0) ·
1542
★**테스트 «안»에서 바이트 패치로 만드는 픽스처**(디스크에 없어 읽을 수 없다 — 그쪽 근거는 각 주석이고 **더 약하다**).
1643
- ★**개악 2종 전건 red**: **M1** 둘째 결함 심기 → `MULTI-DEFECT` **rc=1** · **M2** 커밋본 1바이트 반전 → `GENERATOR DRIFT` **rc=2**.

‎STATE.md‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,22 @@
44
(없음 — 2026-09-16 실측: 착수 시 진행 티켓 0 · 열린 PR 0. ※「열린 PR 0」은 ★**이 회차 PR 착지 시점 기준**이다 — 회신 시점에는 그 PR 자신이 열려 있다)
55

66
## 완료
7-
- [rustjava-adopt-class-format-mutation-audit-p0] ★★**픽스처 «단일 결함» 감사 — 18/18 통과.** 채택 제안
7+
- [rustjava-adopt-class-format-mutation-audit-p0-fix] ★★**판정식을 `given` 에서 파생시킨다 — 게이트② 반려 승계(PR #63).**
8+
★**급소 한 줄**: `repaired` 를 정본 인자로 **다시 짓고** 있어 둘째 결함을 버렸다 ⇒ 「single-defect 인가」를 묻는데 **입력이 이미 single-defect** 였다(순환 · 18중 **14건**).
9+
★처방은 발명이 아니라 **옮겨오기** — 이미 옳던 `indy` 근접실패 형태를 `add()` 한 곳으로 모아 **4족 전건**이 지나게 했고,
10+
`given` 을 `inspect.signature(...).bind(*spec)` 로 **이름에 묶어** 생성기에 인자가 늘어도 자동 승계된다.
11+
★**비대칭이 검사의 전부**(`repaired` 만 파생 · `canonical` 은 아니다) ⇒ 그 비대칭을 지키는 **`AUDIT SPEC STALE`(rc=2)** 가드 신설 — 모르면 «판정하지 않는다».
12+
★★**족마다 개악**(한 족 red 의 일반화가 이번 결함을 살렸다): **M-A** ldc rc=0→**rc=1** · **M-B** cp rc=0→**rc=2(SPEC STALE)** ·
13+
**M-B2** 정본 고지 후 →**rc=1(1B)** · **M-C** indy 근접실패 rc=1→**rc=1**(일하던 족 유지) · **M-D** indy LINKED rc=0→**rc=1**.
14+
★★**잃은 것**: 커밋 픽스처 18건은 **여전히 전건 통과**다 — 내려간 것은 «통과 수»가 아니라 **«통과 가능한 입력 집합»**(실패 불가 사례 **14 → 0**).
15+
대신 판정이 `CANON` 표에 의존하게 됐고(틀리면 거짓 MULTI-DEFECT), 생성기가 자라면 SPEC STALE 이 **막는다**(고의). 시험 시간 **약 40초** 증가.
16+
★**상시 CI 검사는 만들지 않았다** — 공허한 검사를 DoD 에 박지 않으려고 판정식을 먼저 조였다. ★제품 Rust **0줄**.
17+
- [rustjava-adopt-class-format-mutation-audit-p0] ★★**픽스처 «단일 결함» 감사.** ★**[교정 · `-fix` 회차] 종전 제목의 「18/18 통과」는 «측정»이 아니었다** — 아래 교정 줄 참조. 채택 제안
818
`2026-09-16-class-format-mutation-audit#p0`(worklog json `adoptedProposals` 기록). ★**제품 코드 0줄**(감사 스크립트 1개).
919
★**판정식을 «로드»가 아니라 «바이트 동일»로** 잡았다(생성기가 결함을 인자로 받으므로 더 강하고 더 싸다):
1020
`generator(결함)==커밋본` **그리고** `generator(수리)==generator(표준)`.
11-
★결과 **검사 18 전건 single-defect · 무결함 유효 파일 5 · rc=0** ⇒ 회차마다 «적용»돼 온 규율을 처음으로 «측정»했다.
21+
★★**[교정 2026-09-17 · 게이트② request-changes] 그 「전건」은 «측정»이 아니었다** — 18건 중 **14건**이 `repaired` 를 정본 인자로
22+
«다시 지어» 둘째 결함을 버렸다(구조적으로 MULTI-DEFECT 불가). ★**실측된 것은 4/18**. 정정은 `-fix` 항목이 진다.
1223
★**덮지 않는 것을 스크립트에 적었다**(javac 산출물 · 적법-미구현 · 테스트 안 바이트 패치분).
1324
★개악 2종 red(둘째 결함 심기 **rc=1** · 커밋본 1바이트 반전 **rc=2**) · `--all` **576/0/1**(Rust 무접촉이라 불변).
1425
- [rustjava-adopt-link-stringconcatfactory-p0] ★★**`StringConcatFactory.makeConcat` 도 링크한다 — 단 «이유는 제안이 적은 것이 아니다».**
Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,48 @@
11
{
2-
"schema": "worklog/v1",
3-
"date": "2026-09-17",
4-
"taskId": "rustjava-adopt-class-format-mutation-audit-p0",
5-
"summary": "Audit the hand-assembled fixtures for single-defectness — is each file broken in exactly one way? Answered by byte equality against the canonical build rather than by loading, which is both stronger and cheaper. Result: 18 defective fixtures checked, all single-defect; 5 carry no defect at all.",
6-
"changes": [
7-
"test-data/src/audit-fixture-single-defect.py: repairs each generated fixture's named defect and requires the bytes to equal the canonical build"
8-
],
9-
"verification": [
10-
"audit: 18 checked / 18 single-defect / 5 no-defect (valid files) / rc=0",
11-
"mutation M1: plant a second defect (NotFactoryDescriptor also gets reference kind 7) -> MULTI-DEFECT, rc=1",
12-
"mutation M2: flip one byte of a committed fixture -> GENERATOR DRIFT, rc=2",
13-
"cargo test --all unchanged (this round adds no Rust)"
14-
],
15-
"issues": [
16-
"The audit is a standalone script, not wired into DoD or CI. An audit nobody runs rots; whether this property is worth a permanent check is a separate decision, argued in the proposal below.",
17-
"Three fixture classes are outside its reach and are named in the script header rather than silently skipped: javac output (no defect), legal-but-unimplemented files (no defect), and fixtures byte-patched inside a test (not on disk)."
18-
],
19-
"adoptedProposals": [
20-
"2026-09-16-class-format-mutation-audit#p0"
21-
],
22-
"proposals": [
23-
{
24-
"title": "Decide whether single-defectness is a standing check or a one-off",
25-
"plainSummary": "The audit passes today. Nobody runs it tomorrow.",
26-
"userBenefit": "A fixture that grows a second defect stops covering what its test claims, and the test keeps passing — which is exactly the silence this round measured away.",
27-
"why": "Every fixture is generated from a parameterised generator, so the check costs one python3 invocation and no JVM. The repository's own doctrine says a rule kept only in prose is the worst state: 'the rule exists and has no effect'.",
28-
"tradeoff": "It would be an eighth DoD command, and check-dod-ci-parity.py then has to carry it in both directions — the parity lock is deliberately strict about that. Against: the property is stable by construction (the generators take the defect as a parameter), so the check may earn nothing for years.",
29-
"effort": "S",
30-
"target": "CLAUDE.md, .github/workflows/rust.yml"
31-
}
32-
]
2+
"schema": "worklog/v1",
3+
"date": "2026-09-17",
4+
"taskId": "rustjava-adopt-class-format-mutation-audit-p0",
5+
"summary": "Audit the hand-assembled fixtures for single-defectness — is each file broken in exactly one way? Answered by byte equality against the canonical build rather than by loading, which is both stronger and cheaper. CORRECTED after gate-2 review (round rustjava-adopt-class-format-mutation-audit-p0-fix): the original claim \"18 checked, all single-defect\" was not a measurement. 14 of the 18 rebuilt `repaired` from canonical arguments instead of deriving it from the fixture's own, so a second defect was discarded by both sides and the verdict could not fail. Only the 4 indy near-misses were really measured. The fix derives `repaired` from `given` for all four families; the 18 committed fixtures still pass, but now that is a result rather than a tautology.",
6+
"changes": [
7+
"test-data/src/audit-fixture-single-defect.py: repairs each generated fixture's named defect and requires the bytes to equal the canonical build",
8+
"test-data/src/audit-fixture-single-defect.py (p0-fix): `repaired` is now `given` with only the named axis overwritten, for all four families, via a single add() helper",
9+
"test-data/src/audit-fixture-single-defect.py (p0-fix): `given` is bound to the generator's named parameters with inspect.signature(...).bind(*spec), so a parameter added to a generator is carried into `repaired` instead of being dropped by tuple indexing",
10+
"test-data/src/audit-fixture-single-defect.py (p0-fix): new AUDIT SPEC STALE verdict (rc=2) when a spec sets a parameter no CANON table names — the guard on the repaired/canonical asymmetry",
11+
"test-data/src/audit-fixture-single-defect.py (p0-fix): docstring gains a fourth coverage limit (the canonical arguments and the named axis are human annotations) and a note that the MULTI-DEFECT byte count is a loudness figure, not an edit distance"
12+
],
13+
"verification": [
14+
"audit: 18 checked / 18 single-defect / 5 no-defect (valid files) / rc=0 — SUPERSEDED: gate 2 showed 14 of those 18 verdicts could not fail, so this line reports 4 measurements and 14 vacuous passes. Re-run after the p0-fix round gives the same numbers, and they now mean what they say.",
15+
"mutation M1: plant a second defect (NotFactoryDescriptor also gets reference kind 7) -> MULTI-DEFECT, rc=1",
16+
"mutation M2: flip one byte of a committed fixture -> GENERATOR DRIFT, rc=2",
17+
"cargo test --all unchanged (this round adds no Rust)",
18+
"p0-fix: baseline unchanged on the real tree — 18 checked / 18 single-defect / 5 no-defect / rc=0",
19+
"p0-fix mutation M-A (ldc, LdcDynamicOldMajor also gets a duplicate BootstrapMethods): before rc=0, after rc=1 MULTI-DEFECT 13 bytes",
20+
"p0-fix mutation M-B (cp, generator grows a `major` parameter and UnreferencedTag13 gets major 45): before rc=0, after rc=2 AUDIT SPEC STALE — the audit refuses to judge a parameter it does not know the canonical of",
21+
"p0-fix mutation M-B2 (M-B plus telling the audit that major 52 is canonical): rc=1 MULTI-DEFECT 1 byte — the cp family is judgeable once the CANON table is current",
22+
"p0-fix mutation M-C (indy near-miss, NotInvokeStaticFactory also gets the wrong method name): rc=1 before and after — the family that already worked still works",
23+
"p0-fix mutation M-D (indy LINKED, MakeConcatWrongDescriptor also gets a static argument): before rc=0, after rc=1 MULTI-DEFECT 4 bytes",
24+
"p0-fix: each mutation ran in a scratch copy with the fixture regenerated, so built()==committed still holds and the run is not a GENERATOR DRIFT"
25+
],
26+
"issues": [
27+
"CORRECTED: the original 18/18 was 4/18 measured. See the p0-fix entries above.",
28+
"The audit is a standalone script, not wired into DoD or CI. An audit nobody runs rots; whether this property is worth a permanent check is a separate decision, argued in the proposal below.",
29+
"Three fixture classes are outside its reach and are named in the script header rather than silently skipped: javac output (no defect), legal-but-unimplemented files (no defect), and fixtures byte-patched inside a test (not on disk).",
30+
"p0-fix: the verdict now depends on the CANON tables in the script being right. A wrong canonical produces a confident false MULTI-DEFECT where the old form was silently green — louder, but a new way to be wrong.",
31+
"p0-fix: growing a generator now blocks the audit with AUDIT SPEC STALE until its CANON table is updated. Deliberate — the alternative is guessing — but it is a standing cost on anyone adding a generator parameter.",
32+
"p0-fix: the committed count did not go down (18 still pass). What went down is the set of inputs that could pass: structurally-unfailable cases 14 -> 0."
33+
],
34+
"adoptedProposals": [
35+
"2026-09-16-class-format-mutation-audit#p0"
36+
],
37+
"proposals": [
38+
{
39+
"title": "Decide whether single-defectness is a standing check or a one-off",
40+
"plainSummary": "The audit passes today. Nobody runs it tomorrow.",
41+
"userBenefit": "A fixture that grows a second defect stops covering what its test claims, and the test keeps passing — which is exactly the silence this round measured away.",
42+
"why": "Every fixture is generated from a parameterised generator, so the check costs one python3 invocation and no JVM. The repository's own doctrine says a rule kept only in prose is the worst state: 'the rule exists and has no effect'.",
43+
"tradeoff": "It would be an eighth DoD command, and check-dod-ci-parity.py then has to carry it in both directions — the parity lock is deliberately strict about that. Against: the property is stable by construction (the generators take the defect as a parameter), so the check may earn nothing for years.",
44+
"effort": "S",
45+
"target": "CLAUDE.md, .github/workflows/rust.yml"
46+
}
47+
]
3348
}

‎docs/worklog/2026-09-17-fixture-single-defect-audit.md‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# 2026-09-17 — Is each fixture broken in exactly one way?
22

3+
> ★★**[교정 2026-09-17 · 게이트② request-changes · 승계 회차 `…-p0-fix`]**
4+
> 이 문서가 적은 **「검사 18건 전건 single-defect」는 «측정»이 아니었다.**
5+
> 판정식이 18건 중 **14건**에서 `repaired` 를 **정본 인자로 다시 지어** 둘째 결함을 버렸다
6+
> ⇒ 그 14건은 **구조적으로 MULTI-DEFECT 를 낼 수 없었다**. ★**실측된 것은 4/18**(`indy` 근접실패)이다.
7+
> ★승계 회차가 `repaired` 를 `given` 에서 파생시켜 **4족 전건**의 순환을 끊었고,
8+
> 커밋된 18건은 **여전히 전건 통과**하지만 그것은 이제 «항진»이 아니라 «결과»다.
9+
> 근거·개악 5종의 전/후 표는 `REPORT.md` 의 `…-p0-fix` 항목에 있다.
10+
311
`taskId: rustjava-adopt-class-format-mutation-audit-p0` ·
412
adopts `2026-09-16-class-format-mutation-audit#p0`
513

@@ -29,7 +37,7 @@ and fail this one. Cheaper, because it needs no JVM — it is bytes against byte
2937
검사 18건 · 결함 없음(유효 파일) 5건 · 합계 23건 · rc=0
3038
```
3139

32-
**All 18 defective fixtures are single-defect.** The five others carry no defect at all and are
40+
**All 18 defective fixtures are single-defect.** ★**[교정] 이 문장은 «쓰인 시점에는 4/18 만 측정한 것»이었다** — 위 머리의 교정 블록 참조. 승계 회차가 판정식을 고친 뒤 같은 수가 다시 나왔고, 이제 그 수는 문장 그대로를 뜻한다. The five others carry no defect at all and are
3341
listed rather than skipped, so the census stays complete:
3442

3543
| family | checked | axis examples |

0 commit comments

Comments
 (0)