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/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) 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. 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. 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`. 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)] 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());