7.6 KiB
NEO-133 — E7M3-01: FactionDef catalog + quest gate/rep schemas + CI
Linear: NEO-133
Branch: NEO-133-e7m3-factiondef-catalog-quest-gate-rep-schemas-ci
Backlog: E7M3-pre-production-backlog.md — E7M3-01
Module: E7_M3_FactionReputationLedger.md
Pattern: NEO-124-implementation-plan.md (schemas + catalog + validate_content.py gates)
Goal
Land faction and quest content contracts for Epic 7 Slice 3 so CI validates faction JSON, extended quest fields (factionGateRules, completionRewardBundle.reputationGrants), and the frozen five-quest prototype roster before NEO-134 server faction catalog load.
Kickoff clarifications
Decisions are already frozen in E7M3-pre-production-backlog.md — Kickoff decisions and E7_M3 — Prototype Slice 3 freeze. No blocking AskQuestion items.
| Topic | Decision | Evidence |
|---|---|---|
| JSON naming | camelCase: reputationGrants, factionGateRules, factionId, minStanding |
E7M2 bundle pattern; backlog kickoff table |
| Rep delivery | Via completionRewardBundle.reputationGrants (E7.M2 bundle idempotency path) |
E7_M3 module doc; NEO-138 applies rep |
| Gate site | Quest accept only (Slice 3) | Backlog E7M3-06; zone gates deferred to E4.M1 |
| Faction roster | Exactly two ids: prototype_faction_grid_operators, prototype_faction_rust_collective |
E7_M3 freeze table |
| Quest roster | Five quests: four E7.M1 onboarding + prototype_quest_grid_contract |
E7_M3 freeze; expands PROTOTYPE_E7M1_QUEST_IDS → PROTOTYPE_E7M3_QUEST_IDS |
| Operator chain | Add Grid Operators +15 reputationGrants; keep existing item + skill XP |
E7_M3 freeze |
| Grid contract | Prereq operator chain; Grid Operators minStanding 15; objective inventory_has_item survey_drone_kit ×1; completion Rust Collective +10 |
E7_M3 freeze |
| Server loader / stores / HTTP / Godot | Out of scope (NEO-134+) | Backlog E7M3-01 scope |
dotnet test green |
Minimal roster parity on existing E7.M1/E7.M2 catalog rules only (see below) | Content JSON change breaks four-quest server freeze without constant sync |
Minimal server parity (CI / test green only)
Adding the fifth quest and updated prototype_quests.json will fail server startup tests until PrototypeE7M1QuestCatalogRules.ExpectedQuestIds includes five ids. This is not NEO-134 loader work — only constant + freeze-table sync mirroring NEO-124 → NEO-125:
- Extend
ExpectedQuestIds+ addGridContractQuestId/ objective constants. - Add
prototype_quest_grid_contracttoPrototypeE7M2QuestCatalogRules.ExpectedCompletionBundleswith emptyitemGrants/skillXpGrants(rep-only bundle; serverParseCompletionRewardBundleignoresreputationGrantsuntil NEO-138). - Operator chain JSON may include
reputationGrants; server E7M2 freeze still compares item + skill XP only (unchanged normalized bundle).
No new faction loader, no IFactionDefinitionRegistry, no gate evaluation.
Files to add
| Path | Purpose |
|---|---|
content/schemas/faction-def.schema.json |
FactionDef: id, displayName, minStanding, maxStanding, neutralStanding |
content/schemas/faction-gate-rule.schema.json |
factionId, minStanding |
content/schemas/reputation-grant-row.schema.json |
factionId, amount (signed int) |
content/factions/prototype_factions.json |
Two frozen factions per E7_M3 freeze |
Files to modify
| Path | Change |
|---|---|
content/schemas/quest-def.schema.json |
Optional factionGateRules array ($ref faction-gate-rule) |
content/schemas/quest-reward-bundle.schema.json |
Optional reputationGrants array ($ref reputation-grant-row) |
content/quests/prototype_quests.json |
Operator chain reputationGrants; new prototype_quest_grid_contract |
scripts/validate_content.py |
Faction load/validate; PROTOTYPE_E7M3_QUEST_IDS (5); faction cross-ref gates; PROTOTYPE_E7M3_COMPLETION_BUNDLES freeze (item + skill + rep); rename/supersede E7M1 four-quest id gate |
content/README.md |
Document content/factions/, new schemas, five-quest roster |
docs/decomposition/modules/E7_M3_FactionReputationLedger.md |
Mark catalog landed when done |
server/.../PrototypeE7M1QuestCatalogRules.cs |
Five-quest roster + grid-contract constants |
server/.../PrototypeE7M2QuestCatalogRules.cs |
Grid-contract empty item/skill bundle in freeze |
CI gates (validate_content.py)
- Faction catalog — schema validate
content/factions/*_factions.json; freeze exactly two faction ids + display names + standing band (-100…100, neutral 0). - Quest roster —
PROTOTYPE_E7M3_QUEST_IDSfrozen set of five ids (includes all four E7M1 ids +prototype_quest_grid_contract). - E7M1 gates retained — acyclic prerequisites, objective cross-refs, chain terminal token (updated for fifth quest).
- Faction cross-refs — every
factionGateRules[].factionIdandreputationGrants[].factionIdmust exist in faction catalog. - E7M3 completion bundle freeze — per-quest expected
itemGrants,skillXpGrants,reputationGrantsfor all five quests (supersedes E7M2 freeze for operator chain + grid contract rep rows; first three gather/craft quests unchanged). - Grid contract shape — prereq operator chain;
factionGateRulesGrid Operators minStanding 15; objectiveinventory_has_item/survey_drone_kit.
Tests
| Layer | What |
|---|---|
| CI | python3 scripts/validate_content.py (existing PR gate) |
| Server | Existing quest catalog loader/registry tests — should pass after roster parity constants; no new [Fact] unless a regression needs explicit grid-contract coverage |
Manual QA: none (content-only; Godot capstone is NEO-143).
Implementation order
- Schemas (
faction-def,faction-gate-rule,reputation-grant-row; extend quest-def + quest-reward-bundle). prototype_factions.json+ quest JSON edits.validate_content.pygates + bundle freeze tables.- Minimal server roster/freeze constant sync.
content/README.md+ E7_M3 module doc status line.- Run
python3 scripts/validate_content.pyanddotnet teston server project.
Out of scope (follow-on tickets)
| Ticket | Work |
|---|---|
| NEO-134 | Server faction catalog fail-fast load |
| NEO-135–137 | Faction standing store, delta audit, gate evaluation on accept |
| NEO-138 | RewardRouterOperations reputation apply + idempotent delivery |
| NEO-139–143 | HTTP read, telemetry, Godot HUD, capstone |
Client counterpart
None for E7M3-01. Client faction HUD and capstone: NEO-142 / NEO-143.
Blocks
NEO-134 (server faction catalog load) is blocked until this catalog + CI lands.
Implementation reconciliation (shipped)
- Schemas:
faction-def,faction-gate-rule,reputation-grant-row; extendedquest-def+quest-reward-bundle. - Catalog:
prototype_factions.json(two factions);prototype_quests.jsonoperator-chain rep +prototype_quest_grid_contract. - CI:
PROTOTYPE_E7M3_*gates invalidate_content.py(roster, faction freeze, bundle freeze, cross-refs, grid-contract shape). - Server parity: five-quest
ExpectedQuestIds, grid-contract E7M2 bundle freeze, schema registry for new$refs. - Docs:
content/README.mdE7 Slice 3 paragraph; E7_M3 module CI line.