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
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ breaking changes to the contract were allowed where they bought correctness (D5
`docs/PRODUCTION-PLAN.md`); each is recorded here. From 1.0.0, a change to the class contract is
a major version.

## 1.0.1 — unreleased
## 1.0.1 — 2026-10-07

### Fixed

Expand All @@ -17,6 +17,12 @@ a major version.
still worked. The mock kernel the unit tests run against answers null, which is why they passed.
The guard now recognizes `method_false()`, the unit test's kernel answers as Max does, and the
Mac session (8.8) is what would have caught it before the tag.
- **A reload lost every attribute.** With that fixed, the guard still took the attributes the
object had added for the class's fields — which answer their names in Max, as messages do — for
Max's own, so a save left the object with no attributes ("the field … is reserved by the host",
then "doesn't understand …"). It now leaves out the object's own attributes as it did its own
messages. Found in the Mac session; the unit test's kernel now answers an added attribute's name
as Max does, and fails without the fix.

### Changed

Expand Down
16 changes: 16 additions & 0 deletions docs/PRODUCTION-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,22 @@ down rather than discovered again. *This plan was itself audited before being ad
- [ ] **8.8 The Mac session for this phase.** Runtime tests for 8.2 (`faults`) and 8.3 (`worker`);
8.3's stack measurement; the macOS half of 8.7 is not needed (POSIX paths are UTF-8). Then
repeat the runbook's step 4 on the release packages, and tag `v0.11.0`.
*Run 2026-10-07 (Intel Mac, Max 9, on main with 1.0.1's fix):* the suite found **a second bug in
8.2's guard** — `reload` failed: the attributes the object adds for a class's fields answer their
names in Max, so on a reload the guard took them for Max's own and reserved every field ("the
field gain is reserved by the host", then "doesn't understand gain"); `worker`'s
`hang` failed the same way after its reload, silently (an unchanged file announces nothing).
Fixed for 1.0.1 (`answered_by_max()` leaves out `m_python_attributes` as it did
`m_python_messages`), against a glue scenario that fails without it — the test's kernel now
answers an added attribute's name, as Max does, while the scenario asks. `faults` passes (DSP
toggled and a cord connected with the C-argument names in the class). `worker`'s console count
was wrong, not the worker: the reload before `hang 1` begins a new load, so the 500 ms hang is
reported late again, and it outlasts the ring (latency + 0.25 s), so input is dropped too — four
lines, not two; the test now expects each. Whole suite: 12 patchers pass. *Stack measured:* a
fresh `pthread_attr_t` (`std::thread`'s) has 512 KiB here, and recursion through `sorted`'s key
crashes there after about 98 levels — short of `deep_recursion.py`'s 450; at the worker's
16 MiB it reaches CPython's own recursion limit (about 2,500 levels), as `threading.Thread`
does. *Still by hand:* step 4 on the release packages, and Build Collective for `fileusage`.

*The 1.0 gate (revised):* 8.1–8.6 change what the ReadMe promises or what a class can rely on, so
they land before 1.0, where D5 still allows them; 8.7 may follow it. *Decided 2026-10-01 (the maintainer):*
Expand Down
7 changes: 6 additions & 1 deletion runtime-tests/make_patchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,7 @@ def worker(latency_ms: float | None = None, quiet_windows: int = 0) -> Test:
t.patcher.connect(delay_by_latency, 1, py)
t.patcher.connect(delay_by_latency, 0, delay, 1)
t.count_errors("late.for")
t.count_errors("dropped")
t.count_errors("interrupted")

watch = t.no_change("delayed-by-latencysamples", late, 0.0)
Expand Down Expand Up @@ -656,7 +657,11 @@ def worker(latency_ms: float | None = None, quiet_windows: int = 0) -> Test:
t.step(t.attribute_equals("direct-reports-no-latency", py, "latencysamples", 0),
t.sample_equals("direct-has-no-delay", now, 0.0),
t.errors_are("hang-interrupted-once", "== 1", "interrupted"),
t.errors_are("console-only-the-stall-and-the-interruption", "== 2"), wait=1000)
# the reload began a new load, so the hang's lateness is reported again, and at 500 ms it
# outlasts the ring (latency + 0.25 s) and drops input too (found in Max, plan 8.8)
t.errors_are("hang-late-again-after-the-reload", "== 2", "late.for"),
t.errors_are("hang-outlasts-the-ring", "== 1", "dropped"),
t.errors_are("console-only-the-stall-and-the-hang", "== 4"), wait=1000)
return t


Expand Down
Loading
Loading