Skip to content

[GOLD-RING] fix(rust-emitter): an enum member is a path, not a field access - #2247

Merged
gHashTag merged 1 commit into
masterfrom
feat/rust-emitter-enum-paths-2
Aug 20, 2026
Merged

[GOLD-RING] fix(rust-emitter): an enum member is a path, not a field access#2247
gHashTag merged 1 commit into
masterfrom
feat/rust-emitter-enum-paths-2

Conversation

@gHashTag

@gHashTag gHashTag commented Aug 19, 2026

Copy link
Copy Markdown
Owner

What

Two gaps in the Rust backend, both in how an enum member is spelled. Together they made every enum-returning function ungeneratable.

  • Verdict.escalate emitted as Verdict.escalate. Rust needs ::. ExprFieldAccess formatted {}.{} unconditionally because nothing told it which identifiers name enums.
  • The shorthand .escalate emitted as escalate:: — the variant on the left of the separator, because ExprEnumValue formatted {}::{} with an empty extra_field.

The codegen now records enum names as it declares them, and the shorthand takes its type from the declared return type of the function being emitted — the context the shorthand is written for.

Why it was found

Generating a real module rather than a fixture: the trios supervisor's decision core (retry, review, merge gate, capacity), the first ring being moved out of Swift under trios#1279.

Verification

fn qualified(x: i32) V { if (x > 0) { return V.a; } return V.b; }
fn shorthand(x: i32) V { if (x > 0) { return .a;  } return .b;  }

Both emit V::a / V::b. rustc --crate-type lib produces an rlib from each, and from the trios ring (14912 bytes). Before this change neither compiled.

Ceremony

  • Seal moved: bootstrap/src/compiler.rs changed, new digest in bootstrap/stage0/FROZEN_HASH per FROZEN.md §5.
  • docs/NOW.md synced per the coordination protocol.
  • cargo build --release in bootstrap/ succeeds.

Not addressed

  • switch used as a function body emits an empty match x { }; — the arms are dropped.
  • ; comments inside an enum body are parsed as variants; // works.
  • pub module name; is rejected at the top level; module name; is accepted. test_highlight.t27 uses the pub form, which is presumably why it reads as supported — that file is a highlighting fixture and is never compiled.

Territory

Nothing under fpga/ and nothing on the wave branch was touched. Branched from origin/master rather than the local wave branch, which carries 21 unpushed commits that are not mine to publish.

Closes #2274

@gHashTag
gHashTag enabled auto-merge (squash) August 19, 2026 18:43
@gHashTag
gHashTag force-pushed the feat/rust-emitter-enum-paths-2 branch from 3b9dc75 to 2d49f91 Compare August 20, 2026 00:10
@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-20 00:10:30 UTC

Summary

Status Count
Total Open PRs 24
PRs with Failing Checks 10
PRs with All Checks Green 14
READY 7
FAILING 10
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=311a8eb2f59a != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

…access

Two gaps in the Rust backend, both in how an enum member is spelled. Together
they made every enum-returning function ungeneratable.

t27 writes a member as `Verdict.escalate` - Zig's spelling, and what the parser
produces. Rust spells it `Verdict::escalate`. `ExprFieldAccess` formatted
`{}.{}` unconditionally because nothing told it which identifiers name enums,
so the generated crate did not compile. The codegen records enum names as it
declares them and emits a path when the base is one of them.

The shorthand `.escalate` was worse: `ExprEnumValue` printed
`format!("{}::{}", node.name, node.extra_field)` with an empty `extra_field`,
so the variant landed on the LEFT of the separator - `escalate::`. t27 takes
the enum from context the way Zig does and Rust has no such rule, so the
emitter has to supply it. The context available at that point is the declared
return type of the function being emitted, which is exactly the case the
shorthand is written for.

Found by generating a real module rather than a fixture: the trios supervisor's
decision core - retry, review, merge gate, capacity - the first ring being moved
out of Swift.

Verified both ways:

    fn qualified(x: i32) V { if (x > 0) { return V.a; } return V.b; }
    fn shorthand(x: i32) V { if (x > 0) { return .a;  } return .b;  }

Both now emit `V::a` / `V::b`, and `rustc --crate-type lib` produces an rlib
from each. The trios ring compiles the same way (14912 bytes).

Seal moved because `bootstrap/src/compiler.rs` changed; new digest recorded in
`bootstrap/stage0/FROZEN_HASH` per FROZEN.md §5. `docs/NOW.md` synced per the
coordination protocol.

Three gaps remain and are NOT addressed here:

- `switch` used as a function body emits an empty `match x { };` - the arms are
  dropped.
- `;` comments inside an enum body are parsed as variants. Use `//` in braces.
- `pub module name;` is rejected at the top level ("Unexpected top-level token:
  KwModule"); `module name;` is accepted. `test_highlight.t27` uses the `pub`
  form, which is presumably why it reads as supported - that file is a
  highlighting fixture and is never compiled.

Nothing under `fpga/` and nothing on the wave branch was touched. Branched from
origin/master rather than the local wave branch, which carries 21 unpushed
commits that are not mine to publish.
@gHashTag
gHashTag force-pushed the feat/rust-emitter-enum-paths-2 branch from 2d49f91 to 8efb17b Compare August 20, 2026 00:17
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-20 00:19:56 UTC

Summary

Status Count
Total Open PRs 23
PRs with Failing Checks 9
PRs with All Checks Green 14
READY 7
FAILING 9
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=cbbfac87dff3 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@gHashTag
gHashTag merged commit 64ab4d8 into master Aug 20, 2026
24 of 30 checks passed
gHashTag added a commit to gHashTag/BrowserOS that referenced this pull request Aug 20, 2026
The second layer of the trunk. It states what an agent, a message and a task
are, and the rules that follow from those definitions alone: whether an agent
counts as alive, whether a task state may move to another, whether a message
needs a named recipient. No I/O - Postgres and HTTP are ring 3.

The contract is not invented. It already runs: nine routes in the agent server,
the `agents` table with its `online`/`offline` status and 30-second heartbeat,
and `AgentTaskState` in the Swift client. This restates it where it can be
generated instead of transcribed.

152 lines of Rust from the `.t27`, compiling under bare `rustc --crate-type
lib`. Integer constants rather than enums, for the reason ring 00 gives: the
seed's enum lowering is not generation-safe until gHashTag/t27#2247 lands, and
the local `t27c` does not have that fix.

`make t27-rings` now runs its rules as a table rather than only compiling it -
message kinds, every task transition, liveness at the boundary and either side
of it, and priority validity.

Three rules worth naming because each is a defect that has happened somewhere:

Cancelling is always allowed for unfinished work - the operator's escape hatch,
and the one transition that must never be refused. Three missed beats, not one,
before an agent is offline: marking it dead for a single hiccup makes the
registry flap, and a status field that flaps is one every consumer learns to
ignore. An out-of-range priority is refused rather than clamped, because a task
that says 99 and is read as critical jumps every queue on a typo.

**My own break-test found a hole in my own test.** Removing the terminal guard
from the ring left every row green: the destinations I looped over did not
include `TASK_CANCELLED`, which is exactly the one the "always allowed" rule
would have let through for finished work. A terminal state must refuse EVERY
destination, including the one that is otherwise unconditional. With that row
added the break goes red on the first try.

915 e2e checks, plus the ring gate.
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.

[GOLD-RING] fix(rust-emitter): an enum member is a path, not a field access

1 participant