neon-sprawl/docs/plans/NEO-124-implementation-plan.md

12 KiB
Raw Permalink Blame History

NEO-124 — Implementation plan

Story reference

Field Value
Key NEO-124
Title E7M2-01: QuestRewardBundle schema + quest catalog extension + CI
Linear https://linear.app/neon-sprawl/issue/NEO-124/e7m2-01-questrewardbundle-schema-quest-catalog-extension-ci
Module E7.M2 — RewardAndUnlockRouter · Epic 7 Slice 2 · backlog E7M2-01
Branch NEO-124-e7m2-questrewardbundle-schema-quest-catalog-extension-ci
Precursor E7.M1 Ready (NEO-112NEO-123) · NEO-43 MissionRewardSkillXpGrant prep · frozen item + skill catalogs
Pattern NEO-112 — extend row JSON Schema + catalog + scripts/validate_content.py gate
Blocks NEO-125 (server catalog load), NEO-126 (delivery store)
Client counterpart none — infrastructure-only (E7M2-prototype-backlog)

Kickoff clarifications

Topic Question Agent recommendation Answer
Bundle array keys item_grants / skill_xp_grants (backlog prose) vs camelCase? itemGrants + skillXpGrants — matches repo JSON (fixedGrants, prerequisiteQuestIds, completionRewardBundle). Adopted — camelCase
Skill XP sourceKind in content Explicit sourceKind: mission_reward per row vs omit? Omit — v1 Slice 2 is mission_reward only; CI checks target skill allowedXpSourceKinds includes mission_reward; apply path hardcodes via MissionRewardSkillXpGrant (NEO-43). Adopted — omit from content

Additional defaults (no kickoff question — settled by backlog / precedent):

  • Item grant rows: $ref existing reward-grant-row.schema.json (itemId + quantity) — same shape as E5.M3 fixedGrants.
  • Bundle required on all four quests: CI gate enforces presence + exact freeze-table contents (not optional omission on prototype ids).
  • Empty itemGrants: omit property when no item rows (XP-only quests); schema allows optional itemGrants with minItems: 0 when present.
  • At least one grant: bundle must contain ≥1 item or skill XP row (skillXpGrants non-empty for all four frozen quests per freeze table).

Goal, scope, and out-of-scope

Goal: Lock completionRewardBundle content shape and CI validation on the four frozen E7.M1 quests before server load (NEO-125+).

In scope (from Linear + E7M2-01):

  • content/schemas/quest-reward-bundle.schema.json, content/schemas/quest-skill-xp-grant.schema.json.
  • Extend quest-def.schema.json with optional completionRewardBundle ($ref bundle schema).
  • Update prototype_quests.json — bundles per freeze table (below).
  • scripts/validate_content.py: bundle schema validation via extended quest validator registry; item id cross-refs; skill id cross-refs; mission_reward allowlist alignment (skill row must list mission_reward in allowedXpSourceKinds); E7M2 prototype bundle freeze gate.
  • Designer note in E7_M2 (camelCase array keys + CI line) + content/README.md E7 Slice 2 paragraph + CT.M1 quest-bundle PR gate paragraph.

Out of scope (from Linear):

  • Server loader, delivery store, router, HTTP, Godot (NEO-125+).
  • sourceKind field in content rows; unlock/flag grant kinds.

Acceptance criteria checklist

  • PR gate validates quest JSON including optional completion bundles.
  • All four prototype quests include completionRewardBundle matching freeze table.
  • Invalid item/skill refs fail CI.

Implementation reconciliation (shipped)

Technical approach

1. quest-skill-xp-grant.schema.json

Draft 2020-12 object; additionalProperties: false. Required: skillId (pattern ^[a-z][a-z0-9_]*$), amount (integer ≥ 1). No sourceKind — apply path uses MissionRewardSkillXpGrant (NEO-43).

2. quest-reward-bundle.schema.json

Draft 2020-12 object; additionalProperties: false. Optional: itemGrants (array of $ref reward-grant-row), skillXpGrants (array of $ref quest-skill-xp-grant). At least one array must be present with ≥1 row (enforce in schema via anyOf / minItems or post-schema CI helper). Omit itemGrants when empty (XP-only quests).

3. Extend quest-def.schema.json

Add optional property completionRewardBundle$ref quest-reward-bundle. Remaining required fields unchanged.

4. Extend _quest_def_validator() registry

Register quest-reward-bundle.schema.json and quest-skill-xp-grant.schema.json (and existing reward-grant-row.schema.json) in the Draft 2020-12 $ref registry alongside step/objective schemas.

5. Catalog — content/quests/prototype_quests.json

Add completionRewardBundle to each of the four rows:

Quest id itemGrants skillXpGrants
prototype_quest_gather_intro (omit) salvage 25
prototype_quest_refine_intro (omit) refine 25
prototype_quest_combat_intro (omit) salvage 25
prototype_quest_operator_chain survey_drone_kit ×1 salvage 50

Example (gather intro):

"completionRewardBundle": {
  "skillXpGrants": [{ "skillId": "salvage", "amount": 25 }]
}

Example (operator chain):

"completionRewardBundle": {
  "itemGrants": [{ "itemId": "survey_drone_kit", "quantity": 1 }],
  "skillXpGrants": [{ "skillId": "salvage", "amount": 50 }]
}

6. validate_content.py constants + gates

Constant Purpose
PROTOTYPE_E7M2_COMPLETION_BUNDLES Map quest id → normalized { "itemGrants": [...], "skillXpGrants": [...] } matching freeze table (keep in sync with E7.M2 module doc + future NEO-125 loader)
PROTOTYPE_E7M2_MISSION_REWARD_SOURCE_KIND "mission_reward" — used in cross-ref gate only

New validators (run after quest schema pass, alongside existing E7M1 gates):

  • _prototype_e7m2_completion_bundle_presence_gate: every id in PROTOTYPE_E7M1_QUEST_IDS must have completionRewardBundle object.
  • _prototype_e7m2_completion_bundle_freeze_gate: bundle contents must exactly match PROTOTYPE_E7M2_COMPLETION_BUNDLES (sorted grant rows for stable compare).
  • _prototype_e7m2_completion_bundle_cross_ref_gate: each itemGrants[].itemId ∈ frozen item ids; each skillXpGrants[].skillId ∈ frozen skill ids; each target skills allowedXpSourceKinds must include mission_reward (load skill rows from validated skill catalog or reuse existing skill id map if available in main() scope).

Extend module docstring (quest catalogs bullet) and startup schema file existence checks for new schema paths.

7. Docs

  • E7_M2_RewardAndUnlockRouter.md — update freeze prose to itemGrants / skillXpGrants (camelCase); add CI enforcement line under prototype freeze.
  • content/README.mdPrototype E7 Slice 2 — quest completion bundles (NEO-124) paragraph: four quests require bundles; cross-ref rules; link E7M2 backlog + this plan.
  • CT_M1_ContentValidationPipeline.md — extend quest catalog bullet with completion bundle validation.
  • E7M2-prototype-backlog.md — E7M2-01 checkboxes when implementation completes.
  • documentation_and_implementation_alignment.md — E7.M2 row note NEO-124 catalog in progress / landed.

8. No server/C#/client changes in this story.

Files to add

Path Purpose
content/schemas/quest-skill-xp-grant.schema.json Single skill XP row on a quest completion bundle (skillId, amount).
content/schemas/quest-reward-bundle.schema.json Composite bundle (itemGrants, skillXpGrants) attached to QuestDef.
docs/plans/NEO-124-implementation-plan.md This plan.

Files to modify

Path Rationale
content/schemas/quest-def.schema.json Add optional completionRewardBundle property referencing bundle schema.
content/quests/prototype_quests.json Add completionRewardBundle to all four frozen quest rows per freeze table.
scripts/validate_content.py New schema paths; extended quest $ref registry; E7M2 bundle presence/freeze/cross-ref gates; docstring + schema existence checks.
docs/decomposition/modules/E7_M2_RewardAndUnlockRouter.md camelCase grant keys + CI enforcement note under prototype freeze.
content/README.md E7 Slice 2 quest completion bundle paragraph (NEO-124).
docs/decomposition/modules/CT_M1_ContentValidationPipeline.md PR gate paragraph for quest completion bundles.
docs/plans/E7M2-prototype-backlog.md Mark E7M2-01 AC complete when shipped.
docs/decomposition/modules/documentation_and_implementation_alignment.md E7.M2 alignment row — NEO-124 catalog landed.

Tests

Target Coverage
No new automated test files Content-only slice; regression surface is scripts/validate_content.py invoked by PR gate (NEO-112 / NEO-100 precedent).
Manual verification pip install -r scripts/requirements-content.txt && python3 scripts/validate_content.py — must pass with four bundles. Spot-check failures: typo itemId, unknown skillId, skill without mission_reward in allowedXpSourceKinds, missing completionRewardBundle on a quest row, wrong XP amount vs freeze table.

Open questions / risks

Question or risk Agent recommendation Status
Constants drift vs future NEO-125 C# loader Sync comment on PROTOTYPE_E7M2_COMPLETION_BUNDLES pointing at E7.M2 module freeze + future server rules class. adopted
Backlog prose still says item_grants / skill_xp_grants Update E7M2 backlog + E7.M2 module to camelCase when implementing (kickoff decision). adopted
Bundle compare stability (grant row order) Normalize/sort grant rows in freeze gate before compare. adopted

None beyond the above.