From 53852a7696f37c69f431b9a57a5e9683682e6f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 4 Sep 2026 13:34:09 +0000 Subject: [PATCH 1/6] fix(runtime): use hot TLS for arena right-sizing --- crates/perry-runtime/src/gc/arena_right_size.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/perry-runtime/src/gc/arena_right_size.rs b/crates/perry-runtime/src/gc/arena_right_size.rs index adcacd15c5..6b892dac61 100644 --- a/crates/perry-runtime/src/gc/arena_right_size.rs +++ b/crates/perry-runtime/src/gc/arena_right_size.rs @@ -82,7 +82,7 @@ struct ArenaRightSizeState { last_usage: ArenaUsage, } -thread_local! { +crate::perry_thread_local! { static STATE: RefCell = RefCell::new(ArenaRightSizeState::default()); #[cfg(test)] From 460ca39c37eb5762673c9df73549ac203b4333ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 4 Sep 2026 13:42:21 +0000 Subject: [PATCH 2/6] docs(changelog): correct workload units --- changelog.d/9731-arena-right-sizing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.d/9731-arena-right-sizing.md b/changelog.d/9731-arena-right-sizing.md index 78109a41f7..d902f8937e 100644 --- a/changelog.d/9731-arena-right-sizing.md +++ b/changelog.d/9731-arena-right-sizing.md @@ -14,7 +14,7 @@ stays disarmed until utilization reaches 70% or capacity grows materially into a periodic full-GC loop. On the compiled Claude Code 2.1.112 workload from the report, arena capacity -fell from 96.5 MiB before the episode to 36.7 MiB, then 35.7 MiB and 35.7 MiB -across a five-minute idle soak with about 23 MiB live. RSS fell from 401 MiB at -the first census to 132 MiB at the last, and exactly one idle full was attributed +fell from 96.5 MB before the episode to 36.7 MB, then 35.7 MB and 35.7 MB +across a five-minute idle soak with about 23 MB live. RSS fell from 401 MB at +the first census to 132 MB at the last, and exactly one idle full was attributed to arena right-sizing. From 5271e6f0834674ed32c731eff98fb309ab69677b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 4 Sep 2026 08:58:37 +0000 Subject: [PATCH 3/6] docs(changelog): add PR 9704 fragment --- changelog.d/9704-fresh-instance-prototype-replacement.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog.d/9704-fresh-instance-prototype-replacement.md diff --git a/changelog.d/9704-fresh-instance-prototype-replacement.md b/changelog.d/9704-fresh-instance-prototype-replacement.md new file mode 100644 index 0000000000..ef841ede48 --- /dev/null +++ b/changelog.d/9704-fresh-instance-prototype-replacement.md @@ -0,0 +1,6 @@ +### Fixed + +- Fresh instances now observe class prototype methods replaced after class + registration. The method inliner keeps runtime dispatch for prototype chains + exposed or mutated anywhere in the module, including helper functions and + closures. (#9239) From ba340e23e0dc46dcacc5b46567023ae551b89bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 4 Sep 2026 13:58:33 +0000 Subject: [PATCH 4/6] docs(changelog): record shape descriptor pruning --- changelog.d/9733-uncarried-shape-descriptors.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog.d/9733-uncarried-shape-descriptors.md diff --git a/changelog.d/9733-uncarried-shape-descriptors.md b/changelog.d/9733-uncarried-shape-descriptors.md new file mode 100644 index 0000000000..3f57ae5025 --- /dev/null +++ b/changelog.d/9733-uncarried-shape-descriptors.md @@ -0,0 +1,7 @@ +**Full collections now retire shape descriptors that no live object or +restamping cache owns.** A full trace records every shaped receiver, and a +synchronous sweep prunes only records absent from that complete census. +Minor and budgeted cycles remain conservative. Generated-module ids and +shape/transition caches retain exact ownership, while unstable transition +entries validate their target before stamping. In the claude-code census, +uncarried descriptors without an owner fell from 34,501 to zero. From 3e8b890ff8bff1c2923f14dcf96692095969130b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 4 Sep 2026 13:36:49 +0200 Subject: [PATCH 5/6] test(gap): pin the self-recursive shapes the old scan ordering served The reordered initializer scan is a superset, not a replacement: a self-recursive arrow, a named function expression, and a binding whose initializer both creates a closure over it and is called immediately all passed before this change and must keep passing. Verified identical on both arms of the same-commit A/B. --- test-files/test_gap_9718_initializer_self_binding.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test-files/test_gap_9718_initializer_self_binding.ts b/test-files/test_gap_9718_initializer_self_binding.ts index 0caa1e3b20..ce7661c6e9 100644 --- a/test-files/test_gap_9718_initializer_self_binding.ts +++ b/test-files/test_gap_9718_initializer_self_binding.ts @@ -69,6 +69,16 @@ async function main(): Promise { report("nested-await", () => pending[6]!()); report("earlier-refs-later", h); + // The shapes the pre-pass's original ordering existed for. They passed + // before this fix and must keep passing: moving the initializer scan earlier + // is a superset, not a replacement. + const fact = (n: number): number => (n <= 1 ? 1 : n * fact(n - 1)); + const fib = function rec(n: number): number { return n < 2 ? n : rec(n - 1) + rec(n - 2); }; + const off = renderSync(() => off.u() + "/init-call-result"); + console.log("self-recursive-arrow=" + fact(5)); + console.log("named-fn-expr-recursion=" + fib(10)); + report("init-call-result", () => pending[7]!()); + // The declaration is complete by the time these run, so the direct calls // must agree with what the closures saw. report("direct-plain", () => a.u()); From f67ae03448386066f0efa5906621d66dee3513d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 4 Sep 2026 17:16:03 +0200 Subject: [PATCH 6/6] docs: record the stale-head trap and the patch-id landing audit --- MERGE_GUIDE.md | 21 +++++++++++++++++++ .../9738-merge-guide-stale-head-audit.md | 10 +++++++++ 2 files changed, 31 insertions(+) create mode 100644 changelog.d/9738-merge-guide-stale-head-audit.md diff --git a/MERGE_GUIDE.md b/MERGE_GUIDE.md index bf448fe92d..2d1c5451a7 100644 --- a/MERGE_GUIDE.md +++ b/MERGE_GUIDE.md @@ -56,6 +56,27 @@ wrong code silently — the only tell is the diffstat drifting from what `gh pr list` reports. Re-fetch immediately before building the train, and compare `git rev-list --count origin/main..t` against the PR's commit count. +**Re-fetch again at assembly time, not just at audit time.** Auditing a PR and +building the train are minutes-to-hours apart, and reviewed follow-ups land in +that window. Trains 119 and 120 shipped the pre-review version of #9731 — +losing a `thread_local!` → `perry_thread_local!` conversion and adding a +`tls-budget` failure to `main` — plus two changelog fragments and a follow-up +gap test from other PRs. The author had to open #9736 to fix it. + +**After landing, audit what actually arrived — by PATCH-ID.** + +```bash +git fetch -q origin refs/pull//head:chk --force +git cherry -v origin/main chk # lines starting '+' are NOT in main +``` + +`git rev-list origin/main..chk` is the wrong tool: rebase-merge rewrites +every SHA, so it reports *every* landed PR as unlanded. `git cherry` compares +patch-ids and survives that. It still yields false positives when a train +reshaped commit boundaries — confirm each hit with +`git diff origin/main chk -- ` before believing content is missing. +(#9732 flagged both commits that way and was fully landed.) + ## 3. Auditing a PR Read the diff, not the PR body. The body says what the author meant; the diff diff --git a/changelog.d/9738-merge-guide-stale-head-audit.md b/changelog.d/9738-merge-guide-stale-head-audit.md new file mode 100644 index 0000000000..be4049452b --- /dev/null +++ b/changelog.d/9738-merge-guide-stale-head-audit.md @@ -0,0 +1,10 @@ +**`MERGE_GUIDE.md`: re-fetch at assembly time, and audit landed trains by +patch-id.** Trains 119/120 shipped the pre-review version of #9731 because the +PR head was fetched at audit time and not re-fetched when the train was built; +that cost a `thread_local!` conversion (a new `tls-budget` failure on `main`, +fixed by #9736), two changelog fragments, and a follow-up gap test. The guide +now says to re-fetch immediately before assembly, and to verify what landed with +`git cherry -v origin/main ` — patch-id, because rebase-merge rewrites +every SHA and `git rev-list` therefore reports every landed PR as unlanded. +It also notes `git cherry`'s own false positives when a train reshaped commit +boundaries, and how to confirm one with a file-scoped `git diff`.