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.namenow, 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 .name following .state) → the current-state name accessor (segment kind ContextSystemState, unchanged downstream).
  • @@:system.state not followed by .name → a new segment kind, ContextSystemStateReservedE608.
  • @@:system followed by any other member, or nothing → ContextSystemBareE604 (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

  • E608bare @@:system.state is 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) in frame_validator.rs. The text scan treats @@:system.state as reserved unless it is a complete token continued by a word-bounded .name.
  • E421no state access in static operations. Retargeted from @@:system.state to @@:system.state.name (the new valid spelling). A static operation that writes bare @@:system.state gets 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 the frc-future.md “System Context” essay.
  • Test environment (framec-test-env): the gen_const_sys.py fuzz generator and the context_parser positive fixtures across all 17 backends.
  • Showcase (frame-games): .fjs chapters — 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).