Source/Core/AkronFeatureRegistry.cs.
Status categories
Do not introduce new policy words. Akron does not use Yellow, Practice, or a separate room-lab clear category.
AkronStatus order matters because attempt status escalates by enum value:
Unclassified is the new or reset attempt state before Akron records feature use. Feature definitions use the other three values.
Classification flow
Suboptions
Classify the smallest meaningful behavior. A parent row can be clean while a tooltip suboption is Cheat. Examples:- Safe Mode can be clean while stat-freeze suboptions are Cheat.
- Room or map capture can be clean while a timer-freeze suboption is Cheat.
- Death Stats can be Goldberry/Hardlist clear while the PB loss prompt is Normal clear.
- If the row maps to an
AkronFeatureKind, classify it inDefinitions. - If the row has no
AkronFeatureKind, add it toUiLabelClassifications. - If a tooltip suboption differs from the parent, add it to
UiSuboptionClassifications. - Add or update tests in
tests/feature-registry-tests.cs.
Feature shape
Pick the row type based on how the user interacts with it.
Configuration values should not activate a feature by themselves. For example, if a sound override volume is set to
150 but the per-sound modifier is off, the live game should use vanilla sound volume.
Classification examples
Enforcement
Policy is enforced through:Source/Core/AkronFeatureRegistry.csfor classifications and reasons.- UI label and suboption classification maps.
- Runtime calls that use
TryUseorCanUse. - Feature registry tests.
- Clean vs cheat classifications.
Map-authored feature restrictions
A map can suppress selected Akron features without changing the player’s saved preferences. This is independent of the clean/cheat classification. A restricted feature is unavailable even if its saved toggle is on, a setup enables it, or restored session state requests it. In Lönn, place Akron Feature Restrictions (Akron/featureRestrictions) in any room. Set its features string to comma-separated, case-sensitive AkronFeatureKind identities, for example:
FrameAdvance. Whitespace around identities and empty entries are ignored. Repeated identities have no additional effect. Unknown identities produce an Everest log warning and are ignored; other valid entries still apply. Check the log before shipping a map.
The declaration has no region, nodes, flag conditions, or executable commands. Akron reads declarations from every room and takes their union before loading gameplay entities. A marker in an unvisited room applies from the start. Marker position and whether the current room spawns it do not change policy. No marker, or an empty features value, leaves all features available.
Restrictions cover the current map side. They persist through room transitions, death, room reload, chapter restart and save-and-quit/reentry. Leaving for a menu, entering another map, or unloading Akron removes the old map’s restrictions. Pending frame steps, StartPos actions and captures do not transfer into the next map. Restrictions are compiled from map data rather than stored in Akron’s savestate/session payload. Restart the map after editing its declaration.
Direct chapter reloads retain policy through the outgoing level’s end and every loader frame, including reloads that reuse the same session. Constructing a detached loader does not change the live map’s policy. A cancelled StartPos death restore falls back to ordinary respawn only while its original dead body still belongs to the current room; stale callbacks cannot reload a replacement room or player.
StartPos snapshots record whether Akron owned the saved global timescale. Loading a snapshot cannot restore a denied Akron timescale or replace the active map’s clock with it. Independently owned saved timing remains part of the restored simulation. This changes the snapshot format to v12; capture older StartPos snapshots again.
The overlay retains the configured value and marks restricted rows as map-suppressed. akron_status lists the policy version and restricted identities separately from configured values. Commands and hotkeys cannot override map policy. Ongoing effects use the same policy, so a pre-enabled feature does not bypass the declaration. Suppression never saves replacement preferences. Akron releases timing and other reversible state it owns; it does not repeatedly reset another mod’s clock.
The Logging identity suppresses Akron’s own log-file writes. Warnings and errors still reach the Everest log when warning/error mirroring is enabled, so map restrictions cannot hide diagnostics needed to report a failure.
Each identity is independent. To suppress a preset’s individual effects, list those effects as well as any bundle action you want to deny. For example, use MapVersionStamp, PauseTracker, ProofRecorderGuard and EndScreenHelper when those independently configured features must be off, rather than assuming SubmissionMode recursively suppresses them. For independent freeze and stepping control, Freeze suppresses the freeze itself and FrameAdvance suppresses stepping while leaving an allowed freeze intact. CaptureCheatOptions denies room and chapter scanners because they move the player and restore simulation clocks, even with their optional freeze/hide settings off. Ordinary screenshots remain available; use ScreenshotTool to deny those too.
This is an Akron contract, not a sandbox for other mods. It governs Akron’s effect and integration entry points, not independent commands or features in Speedrun Tool, CelesteTAS or other helpers. It does not undo irreversible actions performed before entering the map, such as save unlocks or previously spawned entities.
Akron’s EVM integration requires the randomizer mutation observer so it can track and release randomized changes. If the installed EVM assembly lacks that capability, Akron logs a warning and leaves EVM state untouched rather than making untracked writes.
Integration API
Code integrations can qualify support without private-field access:AkronPolicy.MapRestrictionVersionis1for this contract.AkronPolicy.HasMapRestrictionsreports whether any valid identities are restricted.AkronPolicy.RestrictedFeaturesexposes the read-only current identity list.AkronPolicy.IsMapRestricted(feature)reports the map declaration’s decision.AkronPolicy.CanUse(feature)returns the effective decision and a user-facing reason.AkronFeatureRegistry.TryResolveIdentity(identity, out feature)resolves the same exact names used by map declarations.
TryUse for an explicit user action and TryUseRuntime for recurring effects so denied features do not create a toast every frame. Do not implement restrictions by mutating saved settings or by detouring setting getters.
Permission checks are separate from use classification. Use CanUse when an action only disables or resets an effect, such as resetting Timescale to normal speed. Resetting an inactive effect must not mark an otherwise clean attempt as Cheat.
Verification
FeatureRegistryTests covers all registered identities, map-wide union, identity validation, policy reentry, retained preferences, independent freeze/step decisions and timing ownership release. Live verification must also exercise enabled-on-entry and mid-map changes, deferred StartPos/capture cancellation, ordinary death and room transitions, menu return, reentry and unload. External integrations, rendered HUD/media and audio require their own live cases; parser tests do not prove those effects.
Writing policy reasons
Good reasons say what behavior affects policy:Displays local inputs without changing gameplay.Changes simulation cadence and gameplay timing.Draws hidden trigger volumes for map inspection.
Tooltip writing
Tooltips describe observable behavior. Good tooltip style:- Say what changes when the option is enabled.
- Mention the affected state: gameplay, camera, proof, timer, save, audio, overlay, recording, or import/export.
- Keep it concrete and short.
- For dangerous options, name the risk plainly.
- Do not repeat classification text manually. The UI owns policy display.
- Do not say a feature is “for practice”, “for cheating”, or “safe” as the main explanation.
- Do not imply configuration is active when the parent toggle is off.
Prefer concrete verbs such as show, hide, dim, mute, route, export, import, record, set, clear, save, load, reset, apply, reduce, skip, clamp, cache, and filter.
Feature registry checklist
When adding or changing a policy-visible feature:- Decide the smallest behavior unit that needs policy tracking.
- Add an
AkronFeatureKindonly if the behavior is a real policy unit reused by code or attempt tracking. - Add a
FeatureDefinitionwithGoldberryHardlistClean,RegularClean, orCheat. - Write the reason as a policy explanation, not a tooltip.
- If the UI row lacks
FeatureKind, addUiLabelClassificationscoverage. - If a popup suboption differs from the parent, add
UiSuboptionClassificationscoverage. - Add or update rows in
tests/feature-registry-tests.cs. - Run
dotnet test tests/akron-tests.csproj --nologo --filter FeatureRegistryTests.
- No
AkronStatus.Practice,QolSafe,CleanCaution, or Yellow category text was reintroduced. - Every
AkronFeatureKindhas aFeatureDefinition. - Every new row or suboption displays an accurate classification.
- Tooltip copy describes behavior and leaves classification formatting to the UI.
- The registry, tests, feature guide, and classification docs agree on the policy rule.