Context
The current loader validates manifest.engine as the data/schema version and
defaults an omitted value to MOD_SCHEMA_VERSION. It has no equivalent field
for behavioral semantics. W6 changes precedence, transform reading order, and
contact order while preserving the existing manifest shape, so an old pack
needs an explicit compatibility signal.
Decisions
A monotonic integer revision
The host exports one current rules revision, CURRENT_RULES_VERSION = 2.
Revision 2 means the post-W6 contract: PUSH precedence over STOP, defeat
before win, X IS X self-blocking, reading-order transform conflicts, one-link
transform chains per turn, and complete AND noun reading. Future semantic
changes bump the integer. This field is deliberately separate from
manifest.engine, which continues to describe the validated data API.
manifest.rules is a positive integer no greater than the current revision.
Non-integers, zero, negative values, and other types are validation errors.
A value greater than 2 is rejected as an unsupported future rules revision;
the loader must fail before it registers any part of the pack.
Missing means legacy revision one
An omitted manifest.rules is interpreted as revision 1, the pre-W6 rules
contract. Normalization materializes the effective revision so local imports,
shared decoding, CommonJS export, catalog records, pack-service preview views,
and cloned or persisted views agree. Every projection boundary, including
manifestFromView() and createPackView(), must carry rules forward instead
of rebuilding a manifest that drops it. The engine still runs the current rules
implementation; this revision is a compatibility declaration and does not
select an alternate simulator.
First-party packs that are authored for the current runtime declare
rules: 2 explicitly. Imported packs without the field stay usable and receive
the legacy note below.
One transient pack-level warning
For an effective revision below the host revision, diagnostics returns one bounded warning with the pack identity and source detail. Its player message is exactly:
This pack was made for older rules; some proofs may play differently.
The warning is nonfatal and appears in both local CommonJS and data-only shared
previews. It uses the existing warning envelope, with word: 'RULES',
levelId: null, levelTitle: null, and sentence: null; detail identifies
manifest.rules, the effective revision, and the current revision. It is
emitted once per preview, not once per proof, and does not count authored
levels as separate warnings. The warning is recomputed on preview and is never
copied into normalized authored data, catalog records, snapshots, exports, or
shared payloads.
Keep preview and installation atomic
Validation happens before catalog registration. An unsupported future revision
returns the normal structured pack error and leaves the catalog unchanged. A
known older revision returns errors: [], installs normally when selected, and
retains the effective revision in the pack's normalized manifest.
Risks and mitigations
- Existing packs omit the field. Treating absence as revision one preserves loading while making the semantic mismatch visible.
- A warning can be mistaken for a compatibility mode. The message and docs say the current rules run; no legacy simulator is introduced.
- Local and shared paths can drift. Run the same fixtures through
loadMod,decodeSharedPack, and pack-service previews, then compare warning envelopes and normalized manifests. - First-party world packs may be edited by their owners. Ask each owner to add
rules: 2rather than mutating team-owned pack files in this change.
Non-goals
- Reimplementing pre-W6 rules or adding a rules-version switch to the engine.
- Changing
manifest.engine, share envelope version, feature negotiation, or warning thresholds for long proof titles. - Changing puzzle content, solver search semantics, or presentation copy beyond the one compatibility note.