Gododin Gododin Documentation

Linked repository document

Native reload lifetime regression workload

fixtures/native_components/reload-README.md

This document retains its design, example or evidence context. References outside this guide show their repository paths.

View original Markdown · Download · Return to the guide

reload.py builds a disposable native-components consumer and keeps one exact locked Godot editor process alive through reconstruction. It exercises a native Node and Resource at a configurable count, an unsaved scene, stored integers and owned strings, equal-size field reordering, fresh private state, a private procedure pointer, and an existing Godot signal connection. It also exercises a compiler error/repair, an in-flight superseding save, and rejection/repair of an equal-width incompatible stored-property type.

Run with an explicitly built current CLI and the exact locked Godot executable:

sh
python3 fixtures/native_components/reload.py \
  --cli /absolute/path/to/gododin \
  --godot /absolute/path/to/Godot \
  --evidence-dir /absolute/path/to/gododin-checkout/build/reload-lifetime-small \
  --count 1 --reloads 30

Use a second fresh evidence directory and --count 64 for scale. The harness performs the requested number of successful reloads plus one final repair. Bounds are written before launch: one settled Host and image, at most two of each in transient samples, 1 GiB RSS, and 128 MiB settled growth over startup. vmmap captures actual library paths/build identities after each reconstruction and during execution; RSS is sampled alongside it. Compressed snapshots and JSON records distinguish observations from owner counters and loader-release logs. Sampled maxima are not a guarantee of the instantaneous peak. The receipt binds tool/lock and log hashes and records save-to-observed-result timings. It verifies each published binary/dSYM UUID pair and resolves source lines and variables from two relocated generations after staging and the editor have gone away. That symbol-delivery check is distinct from attached debugging.

The 2026-09-06 receipt (repository: fixtures/native_components/reload-evidence.json) records 31 successful reloads in each of two editor processes: one Node/Resource pair and 64 pairs. Every settled observation contained one current Host and one current image. Maximum settled RSS growth was 13,312 KiB and 13,680 KiB, within the bounds fixed before launch. The receipt links compressed archives containing all 2,603 full vmmap snapshots, samples, per-generation publication manifests, and editor/symbol logs. The runs overlapped and shared compilation resources; their recorded timings do not establish iteration performance or the installed-workload parity gate. The supporting trace retains the CLI/Host, addon, threaded-runtime and repository check logs.

The same harness ran as an installed consumer of the sealed gododin-phase10-2026-09-06.7 SDK (--sdk, authority_kind = "installed", lock equality and sdk validate checked before the first build): reload-installed-small-evidence.json (repository: fixtures/native_components/reload-installed-small-evidence.json) and reload-installed-scale-evidence.json (repository: fixtures/native_components/reload-installed-scale-evidence.json) each record 31 successful reloads (1 and 64 pairs) in one editor process on exact 4.8-dev4 with settled one-Host/one-image observations, bounded RSS, and save-to-observed timings. The small run also carries the debugger qualification: the engine's hardened runtime refuses attaching to the official signed binary (the denial is kept in the receipt), and a copy re-signed ad hoc with get-task-allow ran under lldb, whose breakpoint stopped inside the loaded image's authored reload_node_init at its source line with argument variables resolved from the published DWARF; the engine version banner inside that session names the pinned 4.8-dev4 commit.

Phase 15 iteration baselines with the new consumers

Phase 15's first step re-ran the same small/scale baselines with the Phase 10/12/13/14 consumer kinds added to the workload: borrowed packed-byte inputs (a baked-asset-shaped immutable grid plus a revision-derived bulk grid, both verified against a GDScript FNV oracle), a staged bulk result copied from the instance's own retained buffer, borrowed typed-vector and radius arrays, and an instance-owned pure background worker (its own pthread, bounded handoff, per-image compiled revision marker) that is submitted, completed, and left with one unconsumed record across every reload — exercising cancel/join/drain teardown and fresh reconstruction each cycle (reload.gd, reload.odin.in (repository: fixtures/native_components/reload.odin.in), reload-subsystem.odin.in (repository: fixtures/native_components/reload-subsystem.odin.in)).

The worker template is the configured fixture's subsystem/reload_worker.odin plus the revision marker the harnesses rewrite on each save. Both repository tiers require the two to be identical apart from that marker, and run the package's own cases (reload_worker_test.odin):

  • A request submitted while another runs is promoted when that one finishes, and the displaced unconsumed record is released.
  • A cancel stops the active request, and the token resets for the next one.
  • A cancel admitted before the worker picks a request up is reported as a cancellation, not published as a result.

The 2026-09-09 review's worker fix had landed only in the checked-in package, so until the C5 correction (2026-09-10) the reload and restart harnesses were still materializing the unfixed worker.

Results on the recording host against the installed gododin-phase10-2026-09-07.2 CLI, sequential runs (shared compilation resources would taint the timings):

run pairs reloads p50 p95 archive
small (repository: fixtures/native_components/reload-baseline-small-evidence.json) 1 31 24.34 s 38.08 s reload-baseline-small-2026-09-08.tar.xz (repository: fixtures/native_components/reload-baseline-small-2026-09-08.tar.xz)
scale (repository: fixtures/native_components/reload-baseline-scale-evidence.json) 64 31 24.52 s 39.31 s reload-baseline-scale-2026-09-08.tar.xz (repository: fixtures/native_components/reload-baseline-scale-2026-09-08.tar.xz)

Against the Phase 9 installed baselines (24.37/36.03 s small, 24.67/34.45 s scale), the p50 is unchanged within run-to-run noise: the new consumers cost nothing measurable in the loop, and 64 instances cost no more than one. The dominant measured delay is the six-stage build's compile stage: a focused decomposition on the small consumer measured the debug build at ~21 s and sync at ~3 s of the ~24.4 s p50 loop, leaving watch/debounce, reload activation, and full consumer verification at roughly half a second combined. Compile is the optimization target Phase 15's next steps attack; timings remain automated saves with the recorded sampling rule, not a developer-journey comparison.

Phase 15 optimized loop after the repeated-work removals

The same sample matrix re-ran on 2026-09-08 after the stage caches, the per-invocation managed-Odin skip, and the parallelized compile stage landed, sequentially, against one CLI publication and the same locked Godot executable and Odin lock as the baseline:

run pairs reloads p50 p95 archive
small (repository: fixtures/native_components/reload-optimized-small-evidence.json) 1 31 21.40 s 22.22 s reload-optimized-small-2026-09-08.tar.xz (repository: fixtures/native_components/reload-optimized-small-2026-09-08.tar.xz)
scale (repository: fixtures/native_components/reload-optimized-scale-evidence.json) 64 31 20.79 s 22.40 s reload-optimized-scale-2026-09-08.tar.xz (repository: fixtures/native_components/reload-optimized-scale-2026-09-08.tar.xz)

Against the frozen baseline above that is a ~12–15 % p50 improvement and a collapsed tail (the removed repeated work was what the slow reloads used to pay), with Phase 9's frozen budgets (24.37/24.67 s p50, 36.03/34.45 s p95) met on both runs. The workload's regression scenarios still ran inside the measured matrix: the compiler error repaired, the in-flight save superseded, and the incompatible stored-property type was rejected and repaired, with one settled Host and one image in every settled observation and bounded RSS.

The game-restart path is measured separately by restart.py (repository: fixtures/native_components/restart.py): each sample edits the authored native sources, then gododin run --scene res://scenes/scenario.tscn materializes a private runtime, rebuilds through the coordinator (stage caches active), imports, and launches the captured project into an explicit scenario scene whose native node proves the reloaded revision through its background worker's compiled-in marker; the sample ends at the scene sentinel's first confirmed result. The 30-sample receipt (repository: fixtures/native_components/restart-optimized-evidence.json) (archive (repository: fixtures/native_components/restart-optimized-2026-09-08.tar.xz)) binds the CLI, Godot, and lock digests and reports p50/p95/median; it has no frozen Phase 9 budget, and its only earlier reference is the three-sample harness smoke (p50 23.46 s).

The isolated threaded runtime regression uses the same Odin/LLVM profile:

sh
python3 runtime/thread_storage/tests/run.py \
  --odin /absolute/path/to/locked/odin \
  --library-path /absolute/path/to/managed-odin/lib

It verifies TLS address/value isolation and 128-byte alignment with three concurrent callers, keeps two worker threads alive through 30 actual library unmaps, and exercises a thread exiting during each generation. Calls exercise the runtime RNG and the allocator-backed core:os TLS arenas.

editor/tests/run.py --godot /absolute/path/to/Godot separately checks real child exit statuses (including termination by signal), failed/mismatched/superseded sync, rapid equal-size saves, a save queued during compilation, public-name alias refusal, and unchanged loaded identity on pre-activation failure. Host tests inject native teardown and loader-release failure and reject stale native entry.

These are focused implementation regressions. The lifetime harness launches the CLI-authenticated editor directly so it can observe its PID and mappings; it does not qualify the installed-SDK public editor launcher, the Cavern Lab journey, rendering, attached debugging, or exports. The integrated C1 workloads in the plan remain the parity gate. Windows symbol pairing and desktop qualification remain C2 work.

The same native build profile separately passed the compositor's focused Forward+/Metal run, recovery and accepted editors, current game run, and both executed exports; its receipt (repository: fixtures/compositor/phase4-evidence.json) binds the unchanged inspected PNG and zero-owner shutdown. That matrix does not exercise compositor replacement during an active render callback.

Full debugger qualification scope

The simple --attach-debugger session proves launch, one breakpoint stop in the loaded image, and DWARF-resolved source/variable presentation. The --debugger-scope mode runs the complete Phase 9 debugger workflow in driven lldb sessions (the driver interleaves harness actions with the conversation, so the timed measurement loop is skipped and its receipts are untouched): launch and stop in authored Odin; the recorded frame chain from the engine's ClassDB construction path through the stable Host and the generated bridge's construct_Reload_Node/dispatch_body into the authored frame; finish stepping out across the generated boundary (the return value visible, the bridge frame resolved at its generated source line); scalar, string, and slice field inspection at the init body's return line in authored frames; the workload's real borrowed-mask boundary read from the mask body's own frame; survival of a live edit's rebuild + reload with the same line breakpoint re-resolving into the edit's publication at revision 2's values (verified against the loaded image's publication identity, so bookkeeping rebuilds cannot pass for the edit); diagnosis of a deliberately unmatched symbol (pending, no locations); and a fresh relaunch whose breakpoint resolves to the newly loaded artifact at revision 2's values.

Receipt: debugger-scope-evidence.json (repository: fixtures/native_components/debugger-scope-evidence.json) (13 assertions, all true) with the session transcripts in debugger-scope-2026-09-09.tar.xz (repository: fixtures/native_components/debugger-scope-2026-09-09.tar.xz). The session runs on a re-signed engine copy because the official pinned binary's hardened runtime refuses live attach; the denial stays recorded in the reload workloads' receipts.