112 lines
7.6 KiB
Markdown
112 lines
7.6 KiB
Markdown
# NEO-133 — E7M3-01: FactionDef catalog + quest gate/rep schemas + CI
|
||
|
||
**Linear:** [NEO-133](https://linear.app/neon-sprawl/issue/NEO-133)
|
||
**Branch:** `NEO-133-e7m3-factiondef-catalog-quest-gate-rep-schemas-ci`
|
||
**Backlog:** [E7M3-pre-production-backlog.md](E7M3-pre-production-backlog.md) — **E7M3-01**
|
||
**Module:** [E7_M3_FactionReputationLedger.md](../decomposition/modules/E7_M3_FactionReputationLedger.md)
|
||
**Pattern:** [NEO-124-implementation-plan.md](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](E7M3-pre-production-backlog.md#kickoff-decisions) and [E7_M3 — Prototype Slice 3 freeze](../decomposition/modules/E7_M3_FactionReputationLedger.md#prototype-slice-3-freeze-e7m3-01). 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` + add `GridContractQuestId` / objective constants.
|
||
- Add `prototype_quest_grid_contract` to `PrototypeE7M2QuestCatalogRules.ExpectedCompletionBundles` with **empty** `itemGrants` / `skillXpGrants` (rep-only bundle; server `ParseCompletionRewardBundle` ignores `reputationGrants` until 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`)
|
||
|
||
1. **Faction catalog** — schema validate `content/factions/*_factions.json`; freeze exactly two faction ids + display names + standing band (-100…100, neutral 0).
|
||
2. **Quest roster** — `PROTOTYPE_E7M3_QUEST_IDS` frozen set of five ids (includes all four E7M1 ids + `prototype_quest_grid_contract`).
|
||
3. **E7M1 gates retained** — acyclic prerequisites, objective cross-refs, chain terminal token (updated for fifth quest).
|
||
4. **Faction cross-refs** — every `factionGateRules[].factionId` and `reputationGrants[].factionId` must exist in faction catalog.
|
||
5. **E7M3 completion bundle freeze** — per-quest expected `itemGrants`, `skillXpGrants`, `reputationGrants` for all five quests (supersedes E7M2 freeze for operator chain + grid contract rep rows; first three gather/craft quests unchanged).
|
||
6. **Grid contract shape** — prereq operator chain; `factionGateRules` Grid Operators minStanding 15; objective `inventory_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
|
||
|
||
1. Schemas (`faction-def`, `faction-gate-rule`, `reputation-grant-row`; extend quest-def + quest-reward-bundle).
|
||
2. `prototype_factions.json` + quest JSON edits.
|
||
3. `validate_content.py` gates + bundle freeze tables.
|
||
4. Minimal server roster/freeze constant sync.
|
||
5. `content/README.md` + E7_M3 module doc status line.
|
||
6. Run `python3 scripts/validate_content.py` and `dotnet test` on 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`; extended `quest-def` + `quest-reward-bundle`.
|
||
- **Catalog:** `prototype_factions.json` (two factions); `prototype_quests.json` operator-chain rep + `prototype_quest_grid_contract`.
|
||
- **CI:** `PROTOTYPE_E7M3_*` gates in `validate_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 `$ref`s.
|
||
- **Docs:** `content/README.md` E7 Slice 3 paragraph; E7_M3 module CI line.
|