Skip to content

Correct the spec's effect section, and two dead/flaky things beside it - #5

Merged
admercs merged 2 commits into
masterfrom
spec-effects
Aug 12, 2026
Merged

Correct the spec's effect section, and two dead/flaky things beside it#5
admercs merged 2 commits into
masterfrom
spec-effects

Conversation

@admercs

@admercs admercs commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Three cleanups found by reading and running around the effect handlers merged in #4.

The spec described an effect system this compiler does not implement

MAGE_SPEC.md §11.4, in full:

Effects are inferred bottom-up. Explicit annotations are optional documentation.

The second sentence has been false for as long as the boundary check has existed. A pub function that performs an undeclared effect is an error, and any function that annotates at all is held to inferred ⊆ declared. Calling annotations "optional documentation" describes a language where the capability gate does not exist.

§11.4 now states the rule the compiler actually enforces — private infers, published declares, over-declaration is an upper bound — and a new §11.5 gives the concrete syntax for performing and handling, which the spec previously had only as the [E-Handle] inference rule in §11.3.

Worth recording: the implementation from #4 turns out to match [E-Handle] exactly — body type preserved, handled effect removed from the set. That was not by reference. §11.3 was read afterwards, while checking whether this PR contradicted the spec.

Every claim in the new text was checked by running it. The §11.5 example is a real file that reports f transcribe: { audit } / f summarize: pure and evaluates to "recorded 11 chars".

Dead code that looked live

The collector had an effect.perform branch. It could not fire for the syntax it named — X.perform(args) parses as a MethodCall, never as a Call on a FieldAccess — and it called Effect::from_name on the raw capitalised receiver, which can never match the lowercased names declarations are keyed by. Removed; nothing in the suite or the examples noticed, which is consistent with it never having run.

A flaky test that reddened a full-suite run

a_dead_worker_reports_a_transient_error_not_a_build_failure connects to a freshly spawned worker with a 300 ms timeout before killing it. spawn_worker returns as soon as the thread is spawned, so the listener may not be accepting yet — and under the load of a full scripts/test-all.sh run the budget lapsed, giving a Windows os error 10060 and a red suite. It passed on its own immediately afterwards, which is the signature of a timing assumption rather than a defect.

The connect is setup; the test is about what happens once the worker is dead. It now uses the same timeout() as the rest of the file. Nothing is weakened — the fail-fast-rather-than-hang behaviour a short timeout looks like it covers is really covered by connecting_to_nothing_fails_transiently_rather_than_hanging, which points at a port where nothing listens and keeps its 300 ms deliberately.

This is the second flaky test this week found by running the whole suite rather than one crate; the other was forge's registry temp directory. Both were invisible to CI and visible locally.

Verification

  • scripts/test-all.sh --check-docs green; 45 documented counts match the run.
  • scripts/check-examples.sh green — all 12 typecheck, run, print their recorded answers.
  • ribosome 145 tests green; 0 clippy warnings across the four owned crates.
  • No test counts change: this PR adds no tests and removes none.

🤖 Generated with Claude Code

NERVOSYS and others added 2 commits August 12, 2026 10:27
"Effects are inferred bottom-up. Explicit annotations are optional
documentation." The second sentence has been false for as long as the boundary
check has existed: a `pub` function that performs an undeclared effect is an
error, and any function that annotates at all is held to `inferred ⊆ declared`.
Calling the annotations optional documentation describes a language where the
capability gate does not exist.

§11.4 now states the rule the compiler enforces, and a new §11.5 gives the
concrete syntax for performing and handling, which the spec had only as the
[E-Handle] inference rule in §11.3. The implementation turns out to match that
rule exactly — body type preserved, the handled effect removed from the set —
which was not by reference; §11.3 was read afterwards.

Every claim in the new text was checked by running it. The §11.5 example is a
real file that reports `f transcribe: { audit }`, `f summarize: pure`, and
evaluates to "recorded 11 chars".

Also removes the `effect.perform` branch from the collector. `X.perform(args)`
parses as a `MethodCall`, never as a `Call` on a `FieldAccess`, so the branch
could not fire for the syntax it named; and it used `Effect::from_name` on the
raw capitalised receiver, which could never match the lowercased effect names
the declarations are keyed by. Dead code that looked live, and would have
misled the next person to touch effect inference.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`a_dead_worker_reports_a_transient_error_not_a_build_failure` connects to a
freshly spawned worker before killing it. `spawn_worker` returns as soon as the
thread is spawned, so the listener may not be accepting yet — and under the load
of a full `scripts/test-all.sh` run the 300ms budget lapsed, giving a Windows
`os error 10060` and a red suite. It passed on its own immediately afterwards,
which is the signature of a timing assumption rather than a defect.

The connect is setup; the test is about what happens once the worker is dead.
It now uses the same `timeout()` as the rest of the file. Nothing is weakened:
the fail-fast-rather-than-hang behaviour a short timeout looks like it covers is
actually covered by `connecting_to_nothing_fails_transiently_rather_than_hanging`,
which points at a port where nothing listens and keeps its 300ms deliberately.

Second flaky test this week found by running the full suite rather than one
crate — the other was `forge`'s registry temp directory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@admercs
admercs merged commit 636ce2c into master Aug 12, 2026
10 checks passed
@admercs
admercs deleted the spec-effects branch August 12, 2026 17:34
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