Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,16 @@ jobs:
python3 scripts/gc_runtime_root_holders.py --self-test
python3 scripts/gc_runtime_root_holders.py

# #7877. A deleted GC knob left executable CI arms that still looked
# distinct but selected the same collector. Derive the accepted names
# from live runtime/codegen parsers; historical journals are path-exact
# exemptions and cannot license a current script or reference page.
- name: GC environment-knob drift audit
if: ${{ !cancelled() }}
run: |
python3 scripts/check_gc_env_knobs.py --self-test
python3 scripts/check_gc_env_knobs.py

# #7341 layer 3. A RuntimeHandleScope gives an object liveness; it does
# nothing for a raw pointer already read out of the slot. Every rooting bug
# in the quarantine sweep had rooting ALREADY -- what was missing was
Expand Down Expand Up @@ -2279,10 +2289,10 @@ jobs:
# of allocate-and-discard (would catch a future block-pinning /
# cache-leak / tenuring-trap regression in the gen-GC work), and
# (2) crashes when gc() is forced aggressively during JSON parse,
# deep recursion, or closure init. Each test runs under default,
# PERRY_GEN_GC=1, and PERRY_GEN_GC=1 PERRY_WRITE_BARRIERS=1 so a
# regression in any GC mode is caught. Linux-only because /usr/bin/time
# availability + RSS reporting differs on Windows runners.
# deep recursion, or closure init. Each test runs under the default,
# full mark-sweep, explicit generational, and forced-evacuation verifier
# configurations. Linux/macOS only because /usr/bin/time availability +
# RSS reporting differs on Windows runners.
- name: Memory stability tests
if: runner.os == 'Linux' || runner.os == 'macOS'
env:
Expand Down
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ TypeScript (.ts) → Parse (SWC) → AST → Lower → HIR → Transform → Cod
| **perry-hir** | HIR types and data structures, plus AST→HIR lowering |
| **perry-transform** | IR passes (closure conversion, async lowering, inlining) |
| **perry-codegen** | LLVM-based native code generation |
| **perry-runtime** | Runtime: value.rs, object.rs, array.rs, string.rs, gc.rs, arena.rs, thread.rs |
| **perry-runtime** | Runtime: value.rs, object/, array/, string/, gc/, arena/, thread/ |
| **perry-stdlib** | Node.js API support (mysql2, redis, fetch, fastify, ws, etc.) |
| **perry-ui** / **perry-ui-macos** / **perry-ui-ios** / **perry-ui-tvos** | Native UI (AppKit/UIKit) |

Expand All @@ -129,9 +129,9 @@ Key functions: `js_nanbox_string/pointer/bigint`, `js_nanbox_get_pointer`, `js_g

## Garbage Collection

Generational mark-sweep GC in `crates/perry-runtime/src/gc.rs` (default since v0.5.237 / Phase D). Two regions in the per-thread arena: nursery (`ARENA`, fills with new allocations, swept on minor GC) and old-gen (`OLD_ARENA`, holds tenured/evacuated objects). Precise shadow-stack roots + ~55 registered side-table scanners (`gc/mod.rs:298+`); a conservative stack scan exists but production mode resolves to SkipDisabled, so liveness rests on codegen shadow-stack spilling plus `RuntimeHandleScope` in runtime helpers. Write barriers populate a remembered set so minor GC can avoid retracing the old-gen. Two-bit aging (`HAS_SURVIVED` / `TENURED`) promotes nursery survivors after 2 minor cycles; the C4b evacuation policy moves non-pinned tenured objects into old-gen with full reference rewriting only when generated write barriers are active and nursery/RSS pressure plus measured movable candidates justify the work. Idle nursery blocks observed empty for 2 GC cycles are `dealloc`'d back to the OS (C4b-δ, v0.5.235), and the next-trigger calc is hard-capped at the initial threshold (64 MB) so >90%-freed step-doubling can't blow up peak occupancy (C4b-δ-tune, v0.5.236). Triggers on arena block allocation (1 MB blocks since v0.5.196), malloc count threshold, or explicit `gc()` call. 8-byte GcHeader per allocation.
The current collector source of truth is `docs/src/internals/garbage-collector.md`. Implementation lives in the `crates/perry-runtime/src/gc/` and `arena/` module trees. Native RS4GC roots are the target-aware default where the runtime can walk frames; shadow frames are the fallback on unsupported targets. The conservative native-stack scan is diagnostic-only by default (`Auto` resolves to `SkipDisabled`).

**Escape hatches**: `PERRY_GEN_GC=0`/`off`/`false` reverts to full mark-sweep (bisection only). (`PERRY_GEN_GC_EVACUATE` was **deleted** in #7611 — it moved 0 of 96 gc-ratchet cells, and its one unique effect was vetoing forced evacuation, i.e. silently disarming the #7154 stress instrument (`PERRY_GC_SCHEDULE_SEED`). Policy evacuation is unconditional now except on budgeted low-pause cycles, which is the arm with a behavioural test.) `PERRY_GC_FORCE_EVACUATE=1` stress-copies every marked non-pinned nursery object when generated write barriers are active, **including on the explicit `gc()` path** — since #6946 a manual `gc()` under this knob runs an evacuating minor before its full mark-sweep, instead of a full sweep that moved nothing. `PERRY_GC_VERIFY_EVACUATION=1` panics if any mutable live slot still points at a forwarded nursery object after an evacuation/rewrite cycle. `PERRY_WRITE_BARRIERS=0`/`off`/`false` disables codegen-emitted write barriers at compile time and runtime exact helper barriers at runtime for benchmark/debug bisection; unset, `=1`/`on`/`true` keep barriers enabled. `PERRY_GC_DIAG=1` prints per-cycle diagnostics, including evacuation-policy decisions for considered cycles and `barriers_inactive` skips.
**Escape hatches**: `PERRY_GEN_GC=0`/`off`/`false` reverts to full mark-sweep (bisection only). #7611 deleted the ambient evacuation-policy veto after it moved 0 of 96 gc-ratchet cells and could silently disarm the #7154 stress instrument (`PERRY_GC_SCHEDULE_SEED`). Policy evacuation is unconditional now except on budgeted low-pause cycles, which has a behavioural test. `PERRY_GC_FORCE_EVACUATE=1` stress-copies every marked non-pinned nursery object when generated write barriers are active, **including on the explicit `gc()` path** — since #6946 a manual `gc()` under this knob runs an evacuating minor before its full mark-sweep, instead of a full sweep that moved nothing. `PERRY_GC_VERIFY_EVACUATION=1` panics if any mutable live slot still points at a forwarded nursery object after an evacuation/rewrite cycle. `PERRY_WRITE_BARRIERS=0`/`off`/`false` disables codegen-emitted write barriers at compile time and runtime exact helper barriers at runtime for benchmark/debug bisection; unset, `=1`/`on`/`true` keep barriers enabled. `PERRY_GC_DIAG=1` prints per-cycle diagnostics, including evacuation-policy decisions for considered cycles and `barriers_inactive` skips.

### Rooting-bug instruments (#7154 family) — what each knob ACTUALLY gates

Expand All @@ -142,7 +142,7 @@ A "GC value live but not rooted across a collection point" bug is invisible at c
| `PERRY_GC_PROTECT_FROMSPACE=1` (or `poison`) | the from-space reset performed by the **copying minor** (`arena::copying_reset_from_spaces_and_flip`). Retired Eden + active-survivor blocks are detached into a bounded quarantine, poison-filled (`0xDEADBEEFBAADF0DE`, `obj_type = 0xDE`) and, at `=1`, `mprotect(PROT_NONE)`d. A stale deref then SIGSEGVs at the faulting instruction; the installed reporter names the address, the retiring minor, and the last-known object's `obj_type`/size, then restores `SIG_DFL` and re-faults so a core/debugger still sees the real site. `poison` skips `mprotect`. | change the non-moving minor's `arena_reset_empty_blocks`, the full mark-sweep's reclaim, old-gen defrag, or the malloc sweep. **A run with zero copying minors protects nothing** — check that `PERRY_GC_DIAG=1` prints a `[gc-fromspace-protect] retired_set=#N` line. |
| `PERRY_GC_PROTECT_FROMSPACE_DEPTH=N` (default 4) | how many retired page-sets stay quarantined. Evicted sets are restored to RW and **recycled back into Eden**, never `dealloc`'d, so footprint is bounded at `N × from-space bytes`. `0` is clamped to 1 — a depth of 0 would read as ON and protect nothing. **Raise this when a suspected bug does not fault**: a value can cross hundreds of collections between its last valid observation and its stale use (one per back-edge poll at `PERRY_GC_SCHEDULE_RATE=1`). #7154's `new C(…)` reproducer needs `800` — its constructor crosses 600 polls, so the default 4 misses it silently. | — |
| `PERRY_GC_FROMSPACE_SCAN_ABORT=1` | now **implies** `PERRY_GC_FROMSPACE_SCAN=1`. It used to be inert alone (the scan never ran, so nothing aborted, and the run reported success). | — |
| `PERRY_GC_SCHEDULE_SEED=<u64>` | seeded GC-schedule fuzzing — the collection schedule as a knob, from normal pacing up to a collection at every handled safepoint (`PERRY_GC_SCHEDULE_RATE=1`). Three things, exactly: (1) `js_gc_loop_safepoint` stops requiring `GC_SAFEPOINT_PENDING` before descending into `gc_safepoint_moving_minor`; (2) inside `gc_safepoint_moving_minor`, **past the entry guards**, a per-thread safepoint counter advances once per handled safepoint and, when `gc_budgeted_due_trigger()` reports nothing due, a minor runs anyway iff `splitmix64(splitmix64(seed) ^ counter) < threshold`; (3) `gc_force_evacuate_enabled()` becomes true, so survivors MOVE. **A value that does not parse as `u64` reads as OFF, not as seed 0.** The seed is printed at startup, from the process-exit teardown funnel every exit path routes through (`report_exit_summary`, on the collection-side-allocation release — perry's `_exit` paths never reach `atexit`, which is only a libc-return backstop), and on panic/SIGSEGV/SIGBUS/SIGABRT/SIGILL/SIGTRAP — the signal reporter chains to (and is re-layered on top of) the from-space quarantine's, so the two compose. Live-subject counters: `gc::gc_schedule_safepoints()` / `gc::gc_schedule_forced_collections()`. | bypass `gc_safepoint_moving_minor`'s entry guards — and a blocked safepoint deliberately does **not** tick the counter, so the ordinal sequence tracks the program's safepoints rather than its allocation state. The forced-evacuation implication is **unconditional**, per #7611 (the `PERRY_GEN_GC_EVACUATE` veto is deleted). Nor emit loop polls — those are a compile-time property (`PERRY_GC_MOVING_LOOP_POLLS`, default ON since #7721; a binary compiled with `=0` has none), and without them a seeded run only fires at event-loop boundaries and a compute-only loop never collects. Nor *suppress* pressure-driven collections — the rate is additional density, never less. Determinism is **per-thread**: the counter is thread-local, so a single-threaded program replays exactly, while a `perry/thread` program is only as reproducible as its OS scheduling. Say which you measured. |
| `PERRY_GC_SCHEDULE_SEED=<u64>` | seeded GC-schedule fuzzing — the collection schedule as a knob, from normal pacing up to a collection at every handled safepoint (`PERRY_GC_SCHEDULE_RATE=1`). Three things, exactly: (1) `js_gc_loop_safepoint` stops requiring `GC_SAFEPOINT_PENDING` before descending into `gc_safepoint_moving_minor`; (2) inside `gc_safepoint_moving_minor`, **past the entry guards**, a per-thread safepoint counter advances once per handled safepoint and, when `gc_budgeted_due_trigger()` reports nothing due, a minor runs anyway iff `splitmix64(splitmix64(seed) ^ counter) < threshold`; (3) `gc_force_evacuate_enabled()` becomes true, so survivors MOVE. **A value that does not parse as `u64` reads as OFF, not as seed 0.** The seed is printed at startup, from the process-exit teardown funnel every exit path routes through (`report_exit_summary`, on the collection-side-allocation release — perry's `_exit` paths never reach `atexit`, which is only a libc-return backstop), and on panic/SIGSEGV/SIGBUS/SIGABRT/SIGILL/SIGTRAP — the signal reporter chains to (and is re-layered on top of) the from-space quarantine's, so the two compose. Live-subject counters: `gc::gc_schedule_safepoints()` / `gc::gc_schedule_forced_collections()`. | bypass `gc_safepoint_moving_minor`'s entry guards — and a blocked safepoint deliberately does **not** tick the counter, so the ordinal sequence tracks the program's safepoints rather than its allocation state. The forced-evacuation implication is **unconditional** since #7611 deleted the ambient veto that could disarm it. Nor emit loop polls — those are a compile-time property (`PERRY_GC_MOVING_LOOP_POLLS`, default ON since #7721; a binary compiled with `=0` has none), and without them a seeded run only fires at event-loop boundaries and a compute-only loop never collects. Nor *suppress* pressure-driven collections — the rate is additional density, never less. Determinism is **per-thread**: the counter is thread-local, so a single-threaded program replays exactly, while a `perry/thread` program is only as reproducible as its OS scheduling. Say which you measured. |
| `PERRY_GC_SCHEDULE_RATE=<0..1>` (default `0.05`) | **only** the threshold `PERRY_GC_SCHEDULE_SEED`'s hash is compared against — the expected fraction of handled safepoints that collect. Out-of-range values clamp (a `2` reads as 1.0); unparseable and NaN fall back to the default. | do anything at all without a seed. It is inert alone. `=0` is an on-but-selects-nothing control (banner and reporters still install), `=1` collects at every handled safepoint — the maximum-density endpoint, where the seed stops mattering because every ordinal is selected whatever it hashes to. There is deliberately **no allocation-point level**: the alloc-point arm forces a conservative stack scan, which makes the copying minor ineligible, so an "every allocation" density would run non-moving minors and move nothing. |
| `PERRY_GC_SCHEDULE_ALLOC_KB=N` (default 4) | how much NEW nursery material must accumulate before a loop back-edge poll becomes a candidate the seed may select (#7728). A high-water mark measured AFTER each collection, not a delta, so a collection that frees nothing cannot loop. `0` restores the literal every-poll candidate set — right for a small fixture or a window that executes once, and far slower. | change the schedule itself: the seed still decides which candidates collect, so `(seed, counter)` replay is unaffected. Nor apply to microtask-pump safepoints — it paces the loop arm only. |

Expand Down
1 change: 0 additions & 1 deletion benchmarks/gc_ratchet/baseline/gc-ratchet-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"env": {
"PERRY_NO_AUTO_OPTIMIZE": null,
"PERRY_GEN_GC": null,
"PERRY_GEN_GC_EVACUATE": null,
"PERRY_WRITE_BARRIERS": null
},
"binaries": {
Expand Down
1 change: 0 additions & 1 deletion benchmarks/gc_ratchet/gc_ratchet.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,6 @@ def toolchain_description(perry: Path) -> dict[str, Any]:
"env": {
"PERRY_NO_AUTO_OPTIMIZE": os.environ.get("PERRY_NO_AUTO_OPTIMIZE"),
"PERRY_GEN_GC": os.environ.get("PERRY_GEN_GC"),
"PERRY_GEN_GC_EVACUATE": os.environ.get("PERRY_GEN_GC_EVACUATE"),
"PERRY_WRITE_BARRIERS": os.environ.get("PERRY_WRITE_BARRIERS"),
},
"binaries": binary_fingerprints(perry),
Expand Down
20 changes: 20 additions & 0 deletions changelog.d/7883-current-gc-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
### GC documentation now has one current source of truth and rejects deleted knobs

The generational collector accumulated several chronological plans whose opening
decisions no longer matched the shipped implementation. A deleted
`PERRY_GEN_GC_EVACUATE` setting also remained in required memory-stability arms,
ratchet metadata, current documentation, and the generated translation catalogs;
those test arms looked distinct while selecting the same runtime behavior.

A dated collector architecture/operations page now records the shipped collection
paths, target-specific root lowering, barriers and weak processing, pressure and
pooling behavior, supported controls, old-generation defragmentation status, and
the CI contexts that are actually required. The experiment journals are explicitly
historical, and stale source paths, checker status, engine-plan authority, and gate
freshness cadence are corrected.

The dead setting is removed from every live/generated claim. A new CI audit derives
accepted GC knob names from uncommented production runtime, codegen, and compiler
parsers, while allowing only three path-exact historical journals. Its self-test
plants a deleted knob behind a commented-out parser and proves that neither can make
a live claim pass.
1 change: 0 additions & 1 deletion crates/perry/tests/gc_array_prototype_hole_read_6981.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ const ORACLE_RELOCATE: &str = "120,105,125,679,142,125,133,128,115,135,126,109,1
/// the unfixed compiler. Clear the whole family, then apply the arm's own vars.
const GC_ENV_OVERRIDES: &[&str] = &[
"PERRY_GEN_GC",
"PERRY_GEN_GC_EVACUATE",
"PERRY_GC_SCAVENGE",
"PERRY_GC_SCAVENGE_NURSERY_MB",
"PERRY_GC_MOVING_SAFEPOINT",
Expand Down
1 change: 0 additions & 1 deletion crates/perry/tests/gc_closure_self_pointer_root_7055.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ fn relocating_minor_does_not_replay_an_async_loop_iteration() {
// settings.
const GC_ENV_OVERRIDES: &[&str] = &[
"PERRY_GEN_GC",
"PERRY_GEN_GC_EVACUATE",
"PERRY_GC_SCAVENGE",
"PERRY_GC_SCAVENGE_NURSERY_MB",
"PERRY_GC_MOVING_SAFEPOINT",
Expand Down
7 changes: 6 additions & 1 deletion docs/ecs-perf-case-study.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Closing the perry/bun ratio on ECS workloads

> **Historical performance case study.** Collector paths and source locations
> below describe the May 2026 implementation. Use the
> [current collector page](src/internals/garbage-collector.md) for today's
> defaults and controls.

**Status:** complete. Written 2026-05-03 after landing the final commit
on `wip/ecs-loop-fixes`.
**Goal:** every workload in [`@codehz/ecs`](https://github.com/codehz/ecs)
Expand Down Expand Up @@ -471,7 +476,7 @@ order of effort/impact:
could short-circuit common `arr.length` / `str.length` / `map.size`
paths via gc_type tag inline, before falling through to
`js_object_get_field_by_name`. ~15-30 ms potential.
4. **Finish the evac path (`PERRY_GEN_GC_EVACUATE=1`).** Off by default,
4. **Finish the then-experimental evacuation path.** It was off by default,
and broken — fails after ~1 round with "Component type 1 is not in
this archetype." The blocker is correctness in `rewrite_forwarded_references` /
`drain_trace_worklist_inner` (gc.rs). Multi-day work; would let
Expand Down
20 changes: 11 additions & 9 deletions docs/engine-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

**Goal (owner):** best performance, best RSS footprint, minimal binary size.

**Tracker:** #7294 (routing only — this document is authoritative). **History:**
every dated status section, incident narrative and superseded sequencing lives
in [`engine-plan-history.md`](engine-plan-history.md); this file holds only the
current state and the remaining work so it stays readable across context loads.
Last synced **2026-08-08** (v0.5.1350). Since v0.5.1345: the gc-ratchet is
**Status:** a performance worklist, not an architecture source of truth. The
routing tracker #7294 is closed. Current collector architecture and operations
live in [`src/internals/garbage-collector.md`](src/internals/garbage-collector.md);
dated incident narrative and superseded sequencing live in
[`engine-plan-history.md`](engine-plan-history.md). Last audited for GC drift
**2026-08-11**. The status narrative below is retained as measured history:
the gc-ratchet is
repaired, re-pinned, and liveness-proven (#7609 — fail open per cell, fail
closed on the verdict; owner action: promote to required after its first green
`main` run); the element-shape invariant gained a real revocation matrix
Expand Down Expand Up @@ -396,10 +398,10 @@ already working, on a workload that happens to reach it through `JSON.parse`.
- ~~#7477 DirectParser float divergence~~ — **fixed** (#7483, single
correctly-rounded division per Clinger; all three of `PERRY_JSON_TAPE=0`,
`=1` and node produce the same checksum). #7478 is unblocked.
- **The statepoint lowering has no static root-dominance checker.** The
restored gates (#7452, #7460) verify the shadow-stack lowering only; the
checker anchors on `@js_shadow_slot_bind`, which statepoint IR does not
emit. Named at the call sites rather than papered over with a lowered floor.
- ~~The statepoint lowering has no static root-dominance checker.~~ **Closed by
#7663.** `gc-root-dominance-statepoints` reads the production statepoint
rewrite and checks `gc.statepoint` `"gc-live"` bundles. The shadow and native
arms remain separate contexts because they inspect different IR contracts.
Comment on lines +401 to +404

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

rg -n -C 3 'Statepoint-side static checker|gc_root_dominance_check|gc-root-dominance-statepoints|`#7663`' docs/engine-plan.md docs/src/internals/garbage-collector.md

Repository: PerryTS/perry

Length of output: 2022


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- relevant plan sections ---'
sed -n '380,415p;545,570p' docs/engine-plan.md
printf '%s\n' '--- checker references and implementation ---'
fd -i 'gc_root_dominance_check.py|gc-root-dominance-statepoints' .
rg -n -C 5 'gc_root_dominance_check|gc-root-dominance-statepoints|relocation bundles|gc-live|statepoint' scripts docs .github 2>/dev/null | head -240
printf '%s\n' '--- recent file metadata ---'
git status --short
git diff --stat

Repository: PerryTS/perry

Length of output: 24520


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- all exact checker references ---'
rg -n -C 4 'gc-root-dominance-statepoints|gc_root_dominance_check|Statepoint-side static checker|relocation bundles' .
printf '%s\n' '--- checker outline ---'
ast-grep outline scripts/gc_root_dominance_check.py
printf '%s\n' '--- checker source ---'
wc -l scripts/gc_root_dominance_check.py
sed -n '1,260p' scripts/gc_root_dominance_check.py
printf '%s\n' '--- workflow and script references ---'
rg -n -C 6 'root-dominance|dominance_check|gc-live|reloc' .github scripts | head -300

Repository: PerryTS/perry

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

plan = Path("docs/engine-plan.md").read_text()
checker = Path("scripts/gc_root_dominance_check.py").read_text()
wiring = Path("scripts/gc_gate_wiring_check.py").read_text()
workflow = Path(".github/workflows/gc-root-dominance.yml").read_text()

checks = {
    "plan_marks_statepoint_gate_closed": bool(re.search(
        r"statepoint lowering has no static root-dominance checker.*?Closed by\s+#7663.*?gc-root-dominance-statepoints",
        plan, re.S)),
    "plan_lists_checker_work_as_open": bool(re.search(
        r"\*\*Statepoint-side static checker\*\*.*?gc_root_dominance_check\.py.*?read relocation bundles",
        plan, re.S)),
    "wiring_maps_statepoint_gate_to_same_script": bool(re.search(
        r'gc-root-dominance-statepoints".*?gc_root_dominance_check\.py --statepoints',
        wiring, re.S)),
    "wiring_describes_relocation_bundles": "gc.statepoint relocation" in wiring,
    "checker_has_statepoints_mode": "--statepoints" in checker,
    "checker_documents_relocation_bundles": "gc.statepoint" in checker and "relocation bundles" in checker,
    "workflow_defines_statepoint_job": "gc-root-dominance-statepoints" in workflow,
}

for name, value in checks.items():
    print(f"{name}={value}")

assert all(checks.values()), checks
print("CONCLUSION=the open worklist item is stale; `#7663` closed relocation-bundle support in the same checker")
PY

printf '%s\n' '--- exact supporting source excerpts ---'
sed -n '64,83p' scripts/gc_gate_wiring_check.py
sed -n '168,190p' scripts/gc_root_dominance_check.py
sed -n '3178,3190p' scripts/gc_root_dominance_check.py
rg -n -C 2 'gc-root-dominance-statepoints|--statepoints' .github/workflows/gc-root-dominance.yml

Repository: PerryTS/perry

Length of output: 5305


Close the stale statepoint-checker work item.

gc-root-dominance-statepoints is the --statepoints mode of gc_root_dominance_check.py and checks gc.statepoint relocation bundles. Mark Lines 562–563 as closed by #7663.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/engine-plan.md` around lines 401 - 404, Update the statepoint-checker
work item at Lines 562–563 to mark it closed by `#7663`, referencing
gc_root_dominance_check.py’s --statepoints mode and its gc.statepoint
relocation-bundle checks.

- **Ratchet probe coverage gap**: all GC-ratchet probes run at the default
nursery cap; a large-Eden arm would have caught both #7472 and the #7481
residual.
Expand Down
Loading
Loading