RFC-0045 — Reserve @@:system; relocate the state-name accessor to @@:system.state.name
- Status: Accepted — implemented in framec 4.4.0 (pre-public-beta breaking change)
- Author: Mark Truluck mark.truluck@cogiton.com
- Created: 2026-06-04
- Builds on: RFC-0006 (self interface call
@@:self.iface()), RFC-0013 (@@/@@:syntax)
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119.
Summary
The @@:system.state accessor — which reads the current state’s name as a
string — is renamed to @@:system.state.name. The bare path
@@:system.state is reserved: framec rejects it with a new diagnostic,
E608, pointing the author at @@:system.state.name.
The reservation keeps @@:system.state open for a future, richer meaning — a
direct reference to the current compartment (the runtime object that carries
the state’s identity, its state arguments, and its state variables), of which the
name is one field. Under that future model @@:system.state would denote the
compartment and @@:system.state.name would denote its name field — so today’s
spelling is chosen to be forward-compatible with it.
This is a breaking change, made deliberately before Frame has public users.
No published Frame program breaks; framec’s own demos, fixtures, and docs migrate
mechanically (@@:system.state → @@:system.state.name).
Motivation
@@: is a descent into the dispatch context: @@:params.x, @@:data.k,
@@:self.method(), @@:return. Read that way, @@:system is a path prefix
(“into the context, navigate to system…”), and @@:system.state reads as
“navigate to system, then to state.” Today state resolves directly to a
string. That is a dead end: it pins the only thing @@:system.state can ever
mean to “the name,” with no room to later expose the compartment’s other facets
(state arguments, state variables, identity) through the same natural path.
Frame intends to make compartments first-class, directly referenceable in a
later RFC. When it does, @@:system.state is the obvious spelling for “the
current compartment,” with .name, .args, .vars, etc. hanging off it. To
keep that door open without a second breaking change later, the name accessor
must move to its proper place — @@:system.state.name — now, while the cost
is only our own repos.
Design
Grammar
The context-construct parser FSM
(native_region_scanner/context_parser.frs, state $ParseSystem) changes as
follows:
@@:system.state.name(a word-bounded.namefollowing.state) → the current-state name accessor (segment kindContextSystemState, unchanged downstream).@@:system.statenot followed by.name→ a new segment kind,ContextSystemStateReserved→ E608.@@:systemfollowed by any other member, or nothing →ContextSystemBare→ E604 (unchanged; its hint now reads@@:system.state.name).
The FSM is dogfooded Frame; the generated context_parser.gen.rs is regenerated
from the .frs and MUST NOT be hand-edited.
Validation
- E608 — bare
@@:system.stateis reserved. Emitted for the new segment kind in handler bodies (segment-kind check,frame_validator/system_checks.rs), and by a text scan over operation bodies (which are raw native code and are not scanned into Frame segments) inframe_validator.rs. The text scan treats@@:system.stateas reserved unless it is a complete token continued by a word-bounded.name. - E421 — no state access in static operations. Retargeted from
@@:system.stateto@@:system.state.name(the new valid spelling). A static operation that writes bare@@:system.stategets E608, not E421.
Code generation — unchanged behavior
@@:system.state.name lowers to exactly what @@:system.state lowered to
before: the per-backend compartment state-name accessor produced by
expand_system_state(lang) (codegen/frame_expansion/scanner_dispatch.rs) — e.g.
self.__compartment.state (Python), self->__compartment->state (C),
frame_state_name__(Data#data.frame_current_state) (Erlang). The generated
output for the relocated accessor is byte-identical to the pre-RFC output for
the old spelling, on all 17 backends. No match lang arm changes; only the
operation-body string-replace target moves from @@:system.state to
@@:system.state.name.
Why E608
E601–E607 are already assigned (E603 bare @@:self, E604 bare @@:system,
E605 typed domain fields, E606 typed parameters, E607 transitions). E608 is the
next free code and sits in the @@:/context-accessor neighborhood.
Migration
Mechanical and total: replace @@:system.state with @@:system.state.name
everywhere. There is no transition window — bare @@:system.state is an
immediate hard error (that is the point of the reservation). Affected framec-owned
surfaces:
- Docs/spec:
docs/frame_language.md,docs/frame_cookbook.md,docs/frame_quickstart.md,docs/frame_getting_started.md,docs/frame_runtime.md, and thefrc-future.md“System Context” essay. - Test environment (
framec-test-env): thegen_const_sys.pyfuzz generator and thecontext_parserpositive fixtures across all 17 backends. - Showcase (
frame-games):.fjschapters — handled as a follow-up.
Future work (non-normative)
Make compartments first-class so @@:system.state denotes the current
compartment value, with @@:system.state.name, @@:system.state.args.*, and
@@:system.state.vars.* reading its facets. That work is a separate RFC; this
one only reserves the spelling and relocates the name accessor so the later
change is additive, not breaking.
Reference — diagnostics
| Code | Meaning |
|---|---|
| E608 (new) | @@:system.state is reserved for future use; use @@:system.state.name to read the current state name. |
| E604 (amended hint) | bare @@:system requires a member access (e.g. @@:system.state.name). |
| E421 (retargeted) | @@:system.state.name is not allowed in a static operation (no compartment access). |