Skip to main content
Akron feature policy classifies behavior based on its impact on gameplay, proof assumptions, hidden information, and state. The single source of truth is 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.
Implementation rules:
  1. If the row maps to an AkronFeatureKind, classify it in Definitions.
  2. If the row has no AkronFeatureKind, add it to UiLabelClassifications.
  3. If a tooltip suboption differs from the parent, add it to UiSuboptionClassifications.
  4. 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.cs for classifications and reasons.
  • UI label and suboption classification maps.
  • Runtime calls that use TryUse or CanUse.
  • 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:
Use the exact identity names in the feature registry, not translated labels, commands, numeric enum values, or setting names. For example, the Frame Stepper setting belongs to 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.MapRestrictionVersion is 1 for this contract.
  • AkronPolicy.HasMapRestrictions reports whether any valid identities are restricted.
  • AkronPolicy.RestrictedFeatures exposes 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.
Future runtime features must gate actual effects with this policy. Use 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.
Avoid marketing text, vague value claims, or reasons tied to a specific UI layout.

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.
Examples: 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:
  1. Decide the smallest behavior unit that needs policy tracking.
  2. Add an AkronFeatureKind only if the behavior is a real policy unit reused by code or attempt tracking.
  3. Add a FeatureDefinition with GoldberryHardlistClean, RegularClean, or Cheat.
  4. Write the reason as a policy explanation, not a tooltip.
  5. If the UI row lacks FeatureKind, add UiLabelClassifications coverage.
  6. If a popup suboption differs from the parent, add UiSuboptionClassifications coverage.
  7. Add or update rows in tests/feature-registry-tests.cs.
  8. Run dotnet test tests/akron-tests.csproj --nologo --filter FeatureRegistryTests.
Before committing a policy change, check that:
  • No AkronStatus.Practice, QolSafe, CleanCaution, or Yellow category text was reintroduced.
  • Every AkronFeatureKind has a FeatureDefinition.
  • 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.