clone

a browser grid puzzle game where you push words around to rewrite the rules of each level

Specify mod feature and rule-word diagnostics

Dami committed 0d18c4a
commit
0d18c4ad62fe99e821eda64cadc715c45cf43d7c
parent
323b79a3ad90a40d6734b0e522a66b5d5b482d1e
author
Dami <dami.etoile@gmail.com>
7 files changed, +155 -0
Aopenspec/changes/validate-mod-feature-contract/.openspec.yaml4++
Aopenspec/changes/validate-mod-feature-contract/README.md3++
Aopenspec/changes/validate-mod-feature-contract/design.md60++++++++++++++++++++++++++++++
Aopenspec/changes/validate-mod-feature-contract/proposal.md28++++++++++++++
Aopenspec/changes/validate-mod-feature-contract/specs/commonjs-mods/spec.md28++++++++++++++
Aopenspec/changes/validate-mod-feature-contract/specs/level-sharing/spec.md16++++++++
Aopenspec/changes/validate-mod-feature-contract/tasks.md16++++++++
A openspec/changes/validate-mod-feature-contract/.openspec.yaml +4 -0
A openspec/changes/validate-mod-feature-contract/README.md +3 -0
A openspec/changes/validate-mod-feature-contract/design.md +60 -0
@@ -0,0 +1,60 @@
1+## Context2+3+The engine treats core properties and each level's `automata[].property` as semantic predicates. Every other predicate becomes a noun transform. The loader validates token syntax but has no author warning for a property typo, allows an object noun to equal a custom property, and preserves unknown `manifest.features` silently. Local CommonJS and data-only shared packs both use `validateMod`; pack-service previews currently expose only errors.4+5+Clockwork needs diagnostics without changing turn semantics. Existing `#pack` v1 links and the shipped automata example omit feature declarations, so the new host must continue accepting them.6+7+## Goals / Non-Goals8+9+**Goals:**10+11+- Distinguish unsupported property intent from a legitimate noun transform during authoring.12+- Reject known semantic property/noun collisions and unknown required features before a pack is installed.13+- Return bounded nonfatal warnings in the same preview envelope for local and shared packs.14+- Preserve existing v1 links, authored automata data, and the `manifest.engine: 1` contract.15+16+**Non-Goals:**17+18+- Executing custom property callbacks, `pairs`, or any new engine effect.19+- Inferring every sentence players might form by moving words; this pass inspects authored sentence positions.20+- Treating a presentation glossary alone as proof that the engine implements a property.21+- A visual layout decision for Gift/Workshop; that remains with design.22+23+## Decisions24+25+### Use one normalized diagnostic pass after validation26+27+Keep `validateMod()` as the existing error-array API. Add a pure diagnostic function that takes validated, normalized pack data and returns a bounded list of warning records with rule/word source context. It uses the engine's rule parser for authored horizontal, vertical, stacked, and `AND` sentences so the warning logic does not invent a second grammar. Warnings are nonfatal because a new noun transform is legal.28+29+The known noun set is pack-wide: placed object nouns, palette keys, and declared vocabulary. The semantic property set is core properties plus implemented level declarations such as `automata[].property`. A glossary entry without a matching implemented declaration does not suppress a warning. A warning is emitted when a parsed predicate belongs to neither set. Diagnostics are stable and de-duplicated by level and predicate source word, with a cap for hostile or oversized packs.30+31+### Enforce custom property/object collisions at the level boundary32+33+Before validating level objects, collect that level's implemented custom property tokens. An object whose noun equals one of them is invalid, just as a core-property noun is invalid today. The engine's direct `createGame()` validator receives the same check so local modules and direct engine callers agree. This does not reserve an automaton property in unrelated levels where it has not been declared.34+35+### Treat feature flags as explicit minimum requirements36+37+`manifest.features` is an optional array of unique lowercase feature IDs. The current host recognizes `automata`; an unknown ID is a blocking validation error with player-readable upgrade guidance. A valid pack with `automata` data and no feature array remains supported so old v1 links and current examples keep working. New authored packs should declare `automata` explicitly. A future feature that needs old hosts to reject it must also change the engine schema version, because old hosts ignore unknown manifest fields; flags alone cannot retroactively protect them.38+39+### Keep warnings out of authored and shared data40+41+Local import and shared decode both run the diagnostic pass and return `warnings: [{ message, detail, levelId? }]` in their pack-service preview envelopes. Successful results retain `errors: []`. Warnings are not serialized into CommonJS exports, share URLs, installed pack records, or engine snapshots. The application passes the warning records to Gift/Workshop for design-owned presentation; a warning never silently becomes an error.42+43+## Risks / Trade-offs44+45+- [Risk] A property typo in a sentence formed only during play is absent from authored positions. → Warn on initially formed predicates now; the future editor can run the same diagnostic after each edit.46+- [Risk] Pack-wide noun recognition can mask a typo that matches a noun in another proof. → Preserve the pack-wide vocabulary contract and report source location for each warning; consider level-local vocabulary later.47+- [Risk] Feature flags do not protect old deployed code. → Preserve v1 compatibility here and require a future engine version bump for a new unsupported mechanic.48+- [Risk] Warning rendering can crowd the Gift card. → Keep service records independent of layout and request a design placement before shipping player presentation.49+50+## Migration Plan51+52+1. Add diagnostics and validation fixtures at the loader and direct-engine boundaries.53+2. Expose warnings in pack-service local/share previews and add a minimal readable status path.54+3. Verify old v1 automata links still import; confirm unknown features fail before installation.55+4. Hand warning placement to design for review, then run browser and sharing acceptance.56+57+## Open Questions58+59+- Design's placement of warning summary and proof/word details in Gift and Workshop.60+- Whether the world team wants new Clockwork exports to declare `features: ['automata']` immediately; the host accepts both forms.
A openspec/changes/validate-mod-feature-contract/proposal.md +28 -0
@@ -0,0 +1,28 @@
1+## Why2+3+A misspelled or unsupported property word can silently become a noun transformation. Clockwork's `DOOR IS SHUT` example turns doors into unglyphed SHUT objects when the host does not implement SHUT. Authors need a clear diagnostic, and packs need a way to declare mechanics that a host must recognize before play.4+5+## What Changes6+7+- Report nonfatal author warnings when an authored sentence uses a predicate that is neither a supported property nor a known noun in the pack.8+- Reject object nouns that collide with a level's declared property words.9+- Validate optional `manifest.features` identifiers and reject unknown required features with player-readable feedback.10+- Carry warnings separately from errors through local and shared pack previews without serializing transient diagnostics into exported pack data.11+- Preserve existing supported `#pack` v1 links and CommonJS packs while introducing the feature contract.12+13+## Capabilities14+15+### New Capabilities16+17+None.18+19+### Modified Capabilities20+21+- `commonjs-mods`: validate declared mechanic features and property/noun collisions; diagnose unknown predicates.22+- `level-sharing`: apply the same feature gate to decoded data and return nonfatal warnings in preview envelopes.23+24+## Impact25+26+- `src/mods.js`, the CommonJS and data-only validators, and pack-service result envelopes.27+- Gift/Workshop warning presentation, coordinated through the design inbox.28+- Clockwork authoring and compatibility fixtures; no change to engine turn semantics.
A openspec/changes/validate-mod-feature-contract/specs/commonjs-mods/spec.md +28 -0
@@ -0,0 +1,28 @@
1+## ADDED Requirements2+3+### Requirement: Pack validation SHALL distinguish unknown rule words from unsupported mechanics4+The loader SHALL accept optional `manifest.features` as a list of unique supported feature identifiers and SHALL reject unknown required features before registration with an upgrade message. A level SHALL reject an object noun equal to a custom property implemented by that level. For each initially formed sentence whose predicate is neither an implemented property nor a noun known from the pack's objects, palette, or vocabulary, the loader SHALL report a nonfatal warning with proof and word context. Existing packs with automata and no feature list SHALL remain valid.5+6+#### Scenario: Unknown predicate receives a warning7+- **WHEN** a pack contains `DOOR IS SHUT` with `SHUT` absent from its implemented properties and known nouns8+- **THEN** local preview succeeds, reports a warning identifying `SHUT` and the proof, and does not transform the warning into a blocking validation error9+10+#### Scenario: Known noun transformation remains valid11+- **WHEN** a pack contains `ROCK IS MIAU` and `MIAU` is declared by an object, palette entry, or vocabulary entry12+- **THEN** no unknown-predicate warning is returned for `MIAU`13+14+#### Scenario: Declared automaton property remains semantic15+- **WHEN** a level declares `TICK` through `automata[].property` and contains `MOUSE IS TICK`16+- **THEN** no unknown-predicate warning is returned for `TICK`17+18+#### Scenario: Property noun collision is rejected19+- **WHEN** a level declares `TICK` as an automaton property and places an object whose noun is `TICK`20+- **THEN** the loader rejects the level with the object's path and does not register part of the pack21+22+#### Scenario: Unsupported feature is rejected23+- **WHEN** a pack declares `manifest.features: ['pairs']` in a host that supports only `automata`24+- **THEN** the loader rejects the pack with a readable newer-version message before any level is installed25+26+#### Scenario: Legacy automata pack remains usable27+- **WHEN** a valid existing pack contains automata but omits `manifest.features`28+- **THEN** the current loader accepts it and preserves its automata behavior
A openspec/changes/validate-mod-feature-contract/specs/level-sharing/spec.md +16 -0
A openspec/changes/validate-mod-feature-contract/tasks.md +16 -0