Skip to content

release: 5.9.0 — three ways a program could be silently wrong - #708

Merged
Masterplanner25 merged 1 commit into
mainfrom
release/5.9.0
Sep 1, 2026
Merged

Masterplanner25 merged 1 commit into
mainfrom
release/5.9.0

Conversation

@Masterplanner25

Copy link
Copy Markdown
Owner

Release PR for 5.9.0. Steps 1–4c of the sequence are done; this is step 5.

Why now

Two severity:high defects in this release share a failure mode, and it is the one this project treats most seriously: the program did the wrong thing and reported success. Both are fixed on main and neither is on PyPI.

#691 a callback handed to an imported module's function. Inside a workflow step the step truncated at the call, nothing was raised, and the run reported failed: [] with steps: {}. Five symptoms from one construct, because a closure's address indexes the chunk it was compiled from and the symptom is whatever sat at that address.
#696 the same question in the other direction — a closure a module returns. No workflow needed; let f = m.make_adder(3i) at top level was enough, which made a factory function unusable. It could not share #691's fix: every context source that one uses records something a call is still inside of, and a returned closure is called after all of them are gone.
#704 the bytecode cache keyed on path and mtime and never on content, so an edit landing inside the platform's timestamp resolution ran the previous program. Not a PyPy bug — five rapid rewrites with different content produced 2 distinct cache keys out of 5 on CPython too.

Added: #170 binary file I/O (fs.read_bytes / fs.write_bytes) — what a Nodus program needs to write a compiled artifact, a Stage 3 bootstrap gap the Runtime Readiness audit recorded. A feature, so a minor bump is the right shape.

Performance: #702, ~9.6x recovered on PyPy. VM.__init__ sat at 79 instance attributes; PyPy loses map-based storage above 80. A goal-budget feature made it 80 and cost ~9x for three releases with every gate green — because CPython is flat across that boundary and every gate here is CPython.

Tooling: #412 phase 4 finishes the opcode audit — all 49 carry a semantic spec, verified by 52 mutations, 52 killed. Plus guards over the filesystem builtin surface, BUILTIN_NAMES checked against the live registry, and a test counting the VM's instance attributes so the next crossing of the PyPy cliff fails on the PR that causes it.

Release steps completed

step result
1 · suite 3,353 passed, 8 skipped (3,132 at the 5.8.0 cut)
1 · ruff / mypy / fmt --check clean
1 · keyword coverage 13 passed
1 · nodus_gate --all 9 phases green, closed-issues 6/6
2 · version files version.py and pyproject.toml both 5.9.0
2 · README "Recent" rewritten now, before the tag
3 · CHANGELOG [Unreleased] → ## [5.9.0] - 2026-08-31
4 · --closed-issues --section 5.9.0 6/6 — re-run after the cut, not before
4b · --versions 10 of 13 claims were stale; fixed, now 13/13
4c · llms.txt synced into the packaged copy

Housekeeping done first: [Unreleased] had 2× ### Fixes and 3× ### Tooling. Consolidated to one of each in Added → Fixes → Performance → Tooling order, with issue references verified identical before and after.

Gate 10b probes, written before the tag

Four behaviour probes and one prose probe added for 5.9.0 — 88/88 passing. They are the only check that reads the release's claims rather than its code, and running them after the tag would make any correction impossible.

Each 5.9.0 probe runs in the position its documentation points at. That is #691's lesson rather than a preference: retry.until had a full suite, nine gate phases and 83 probes behind it and did not work inside a step body, because every one of them ran in fn main().

Two of the new probes failed on first run — my own bug, absolute paths in an import, then relative imports resolving against CWD rather than the filename label (#521's territory). Fixed by writing a real main.nd and using run_file, which is the route the regression tests take for the same reason.

Remaining after merge

Tag → wheel → Gate 10a dependent suites (before any upload) → Gate 10b against the built wheel from outside the repo with --require-installed → PyPI → Stage 5 → GitHub release → Stage 6 sweep, with the three eval documents.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EdYjdCRLuedehwRCdi2hXb

Two `severity:high` defects in this release share a failure mode, and it is the
one this project treats most seriously: the program did the wrong thing and
reported success.

  #691  a callback handed to an imported module's function. Inside a workflow
        step the step truncated at the call, nothing was raised, and the run
        reported `failed: []` with `steps: {}`. Five symptoms came out of one
        construct because a closure's address indexes the chunk it was compiled
        from, and the symptom is whatever sat at that address.

  #696  the same question in the other direction -- a closure a module RETURNS.
        No workflow needed; `let f = m.make_adder(3i)` at top level was enough,
        which made a factory function unusable. It could not share #691's fix:
        every context source that one uses records something a call is still
        INSIDE of, and a returned closure is called after all of them are gone.

  #704  the bytecode cache keyed on path and mtime and never on content, so an
        edit landing inside the platform's timestamp resolution ran the PREVIOUS
        program. Not a PyPy bug -- five rapid rewrites with different content
        produced 2 distinct cache keys out of 5 on CPython too.

Added: #170 binary file I/O (`fs.read_bytes`, `fs.write_bytes`), which is what a
Nodus program needs to write a compiled artifact -- a Stage 3 bootstrap gap the
Runtime Readiness audit recorded.

Performance: #702, ~9.6x recovered on PyPy. `VM.__init__` sat at 79 instance
attributes and PyPy loses map-based storage above 80; a goal-budget feature made
it 80 and cost ~9x for three releases with every gate green, because CPython is
flat across that boundary and every gate here is CPython.

Tooling: #412 phase 4 finishes the opcode audit -- all 49 now carry a semantic
spec, verified by 52 mutations with 52 killed. New guards over the filesystem
builtin surface, `BUILTIN_NAMES` checked against the live registry, and a test
that counts the VM's instance attributes so the next crossing of the PyPy cliff
fails on the PR that causes it.

Release steps 1-4c:

  suite            3,353 passed, 8 skipped (was 3,132 at the 5.8.0 cut)
  ruff / mypy      clean
  fmt --check      clean
  keyword coverage 13 passed
  nodus_gate --all 9 phases green; closed-issues 6/6
  --closed-issues --section 5.9.0   6/6 (re-run after the cut, not before)
  --versions       10 of 13 claims were stale and are fixed; now 13/13
  llms.txt         synced into the wheel copy

Gate 10b probes written BEFORE the tag, per the rule that they are the only
check reading the release's *claims* rather than its code: four behaviour probes
and one prose probe added for 5.9.0, 88/88 passing. Each 5.9.0 probe runs in the
position its documentation points at -- the #691 lesson, since that defect
survived a full suite, nine gate phases and 83 probes that all ran in `fn main()`.

The README's "Recent" paragraph is rewritten now rather than after the tag:
`readme = "README.md"` makes it the permanent PyPI page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdYjdCRLuedehwRCdi2hXb
@Masterplanner25
Masterplanner25 merged commit ea9af85 into main Sep 1, 2026
4 checks passed
@Masterplanner25
Masterplanner25 deleted the release/5.9.0 branch September 1, 2026 04:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant