9.5 KiB
NEO-76 — Implementation plan
Story reference
| Field | Value |
|---|---|
| Key | NEO-76 |
| Title | E5M1-01: Prototype AbilityDef catalog + schemas + CI |
| Linear | https://linear.app/neon-sprawl/issue/NEO-76/e5m1-01-prototype-abilitydef-catalog-schemas-ci |
| Module | E5.M1 — CombatRulesEngine · Epic 5 Slice 1 · backlog E5M1-01 |
| Branch | NEO-76-e5m1-prototype-backlog |
| Precursor | E1.M4 — hardcoded allowlist in PrototypeAbilityRegistry (unchanged this story) |
| Pattern | NEO-50 / NEO-65 — row JSON Schema + catalog envelope + scripts/validate_content.py gate |
| Blocks | NEO-77 — server ability catalog load |
Kickoff clarifications
| Topic | Question | Agent recommendation | Answer |
|---|---|---|---|
| Prototype stat freeze | baseDamage / cooldownSeconds / abilityKind per row? |
Recommended table — pulse 25 dmg / 3.0s; burst 40 / 5.0; guard 0 / 6.0 utility; dash 0 / 4.0 movement; four pulse casts = 100 HP dummy defeat; pulse cooldown matches NEO-32 global 3s. | User: recommended table (adopted below). |
| abilityKind on rows | Required on all four prototype rows? | Include on all four — optional in schema but aids E5M1-04 GET / E5M1-11 HUD without follow-up schema churn. | Adopted (bundled with stat freeze). |
| Catalog envelope | File shape? | { "schemaVersion": 1, "abilities": [ … ] } in content/abilities/prototype_abilities.json (mirror items / recipes). |
Adopted (NEO-50 / NEO-65 precedent). |
| CI constant name | Slice gate identifier? | PROTOTYPE_E5M1_ABILITY_IDS — Epic 5 Slice 1; avoids collision with economy Slice 1–4 numbering in validate_content.py. |
Adopted. |
Goal, scope, and out-of-scope
Goal: Lock content shape and CI validation for four frozen AbilityDef rows before server load (NEO-77+).
In scope (from Linear + E5M1-01):
content/schemas/ability-def.schema.json.content/abilities/prototype_abilities.json— four frozen ids (freeze table below).scripts/validate_content.py— schema validation, duplicateid, exact four-id allowlist, non-emptydisplayName.- Designer note + Prototype Slice 1 freeze table in E5_M1 +
content/README.md. - CT.M1 PR gate paragraph for ability catalogs.
documentation_and_implementation_alignment.mdE5.M1 row → In Progress / NEO-76.
Out of scope (from Linear):
- Server loader, combat engine, HTTP, Godot (NEO-77+).
- Changing
PrototypeAbilityRegistryor global cooldown behavior (NEO-77 / E5M1-07).
Acceptance criteria checklist
- PR gate validates ability JSON against schema.
- Exactly four prototype ability ids; duplicate
idfails CI. - Stable id list documented in module doc freeze box.
Implementation reconciliation (shipped)
- Schema:
ability-def.schema.json— requiredid,displayName,baseDamage,cooldownSeconds; optionalabilityKind. - Catalog:
prototype_abilities.json— four frozen rows per kickoff stat table. - CI:
PROTOTYPE_E5M1_ABILITY_IDSgate invalidate_content.py. - Docs: E5.M1 freeze table,
content/README.md, CT.M1, alignment register, E5M1-01 backlog checkboxes.
Technical approach
-
Row schema (
ability-def.schema.json): Draft 2020-12 object;additionalProperties: false. Required:id,displayName,baseDamage,cooldownSeconds.idpattern^[a-z][a-z0-9_]*$.displayNameminLength: 1.baseDamageinteger ≥ 0.cooldownSecondsnumber exclusiveMinimum 0 (positive). Optional:abilityKindenumattack|utility|movement(present on all four prototype rows). -
Catalog file:
content/abilities/prototype_abilities.jsonwithschemaVersion: 1and four rows from the freeze table. -
Frozen content values (E5M1-01 — kickoff adopted):
AbilityDef.iddisplayNamebaseDamagecooldownSecondsabilityKindprototype_pulsePrototype Pulse 25 3.0 attackprototype_burstPrototype Burst 40 5.0 attackprototype_guardPrototype Guard 0 6.0 utilityprototype_dashPrototype Dash 0 4.0 movementRules: Do not rename
idvalues after ship — changedisplayNameonly, or add a newidin a follow-up issue. Relaxing the four-id CI gate requires a new Linear issue.prototype_pulseat 25 dmg × 4 casts = 100 HP dummy defeat (E5M1 backlog max HP default).prototype_pulsecooldown 3.0s matches currentAbilityPrototypeCooldown.GlobalDurationuntil E5M1-07 switches to per-ability catalog cooldowns. -
validate_content.py:- Add
ABILITY_SCHEMA,ABILITIES_DIR; discovercontent/abilities/*_abilities.json. _validate_ability_catalogs: envelopeschemaVersion: 1, top-levelabilitiesarray; validate each row againstability-def.schema.json; trackseen_ids; reject duplicates across files._prototype_e5m1_ability_gate: ids must equalPROTOTYPE_E5M1_ABILITY_IDS(frozenset of four ids above).- Run ability validation after existing catalogs succeed (no cross-catalog refs in Slice 1).
- Require ability schema file in
main()startup checks; extend module docstring + success summary with ability file + id counts. - Keep
PROTOTYPE_E5M1_ABILITY_IDSin sync with E5.M1 freeze table and future NEO-77 server loader.
- Add
-
Docs:
content/README.md— addabilities/table row + E5 Slice 1 freeze paragraph.E5_M1_CombatRulesEngine.md— add Prototype Slice 1 freeze (E5M1-01) subsection with id + stat table + CI rules line.CT_M1_ContentValidationPipeline.md— ability catalog PR gate paragraph.documentation_and_implementation_alignment.md— E5.M1 row → In Progress / NEO-76 catalog landed.E5M1-prototype-backlog.md— E5M1-01 checkboxes when implementation completes.
-
No server/C#/client changes in this story.
Files to add
| Path | Purpose |
|---|---|
content/schemas/ability-def.schema.json |
JSON Schema for a single AbilityDef row. |
content/abilities/prototype_abilities.json |
Prototype four-ability catalog (schemaVersion + abilities array). |
docs/plans/NEO-76-implementation-plan.md |
This plan. |
Files to modify
| Path | Rationale |
|---|---|
scripts/validate_content.py |
Load/validate ability catalogs; duplicate id; E5M1 four-id gate; success summary. |
content/README.md |
Document content/abilities/, schema path, E5 Slice 1 freeze (active catalog). |
docs/decomposition/modules/E5_M1_CombatRulesEngine.md |
Freeze table + CI enforcement note; link to plan when complete. |
docs/decomposition/modules/CT_M1_ContentValidationPipeline.md |
PR gate paragraph for ability catalog validation. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
E5.M1 / NEO-76 status after catalog lands. |
docs/plans/E5M1-prototype-backlog.md |
E5M1-01 checkboxes + landed note when complete. |
Tests
| Item | Coverage |
|---|---|
| CI / PR gate | .github/workflows/pr-gate.yml already runs python scripts/validate_content.py — extended script is the regression signal. |
| Manual negative cases | From repo root after implementation: (1) duplicate ability id → non-zero exit; (2) remove one frozen id → gate error; (3) break schema (negative baseDamage or zero cooldownSeconds) → schema error; (4) empty displayName → schema error. |
| Manual happy path | pip install -r scripts/requirements-content.txt && python3 scripts/validate_content.py — reports ability catalog + existing catalogs OK. |
No dedicated pytest module in-repo today (same as NEO-50 / NEO-65).
Open questions / risks
| Question / risk | Agent recommendation | Status |
|---|---|---|
| Catalog growth | E5M1 gate requires exactly the four frozen ids; expanding the roster needs a follow-up issue to change PROTOTYPE_E5M1_ABILITY_IDS. |
adopted |
| C# vs Python drift | None until NEO-77; document “keep in sync” constant names in both places when server load lands (mirror NEO-66 / PrototypeSlice3RecipeCatalogRules). |
adopted |
| Runtime still uses global 3s cooldown | Expected — content lands before E5M1-07 wires per-ability cooldownSeconds; pulse 3.0s pre-aligns catalog with NEO-32 behavior. |
adopted |
| Zero-damage guard/dash before E5M1-06 | Catalog values are forward-looking; cast accept path still has no damage until E5M1-07. Zero-damage rows support E5M1-06 unit tests. | adopted |
None blocking.