diff --git a/MFD_CODEX_MARATHON_GOAL_PROMPT.md b/MFD_CODEX_MARATHON_GOAL_PROMPT.md new file mode 100644 index 00000000..ee7f5dd5 --- /dev/null +++ b/MFD_CODEX_MARATHON_GOAL_PROMPT.md @@ -0,0 +1,617 @@ +# MFD Codex Marathon `/goal` Prompt + +Paste everything below into Codex as one `/goal` request. + +```txt +/goal +You are Codex acting as a senior TypeScript game-engine architect, save-system engineer, and React product engineer for Mr. Football Dynasty (MFD). + +MISSION MODE: MARATHON. +Work continuously through the mission board below. Do not stop after one patch. Do not only plan. Implement, test, fix, update MISSION.md, then continue to the next safe checkpoint until every non-blocked task is complete. If the environment has execution limits, complete as much as possible in dependency order and leave a precise restart point in MISSION.md. + +PRIMARY OBJECTIVE: +Evolve MFD from a strong deterministic dynasty engine into a durable “forever dynasty” product with faster time control, surfaced weekly media, safer saves, and stronger narrative memory. + +NON-NEGOTIABLES: +1. Read AGENTS.md first. Then read CODEX_GAME_GUIDE.md and any repo-specific implementation guidance they reference. +2. This is the MFD pnpm workspace, not the old game.js monolith. Do not use or preserve stale game.js / CDN / three-file delivery assumptions in active docs or prompts. +3. Save compatibility is sacred. Old saves must migrate forward. Do not change SaveState schema shapes unless a task explicitly says to and tests prove migration safety. +4. Migrations are immutable once used by real saves. If SAVE_VERSION is still 36 and no 36→37 migration has shipped, implement the v37 wave as described. If SAVE_VERSION is already above 36 or a 36→37 migration already exists, do not rewrite it; create the next migration version and update fixture policy accordingly. +5. Determinism is sacred. No Math.random() in engine sim paths. No Date.now() in engine sim paths. Do not change RNG consumption order unless a task explicitly requires it and tests prove compatibility. +6. Do not compact dynastyTimeline in this mission. It is the emotional memory layer. Create an ADR/spec for a future v38+ memory archive if useful, but do not ship timeline compaction without a separate design. +7. Do not blindly trim eventLog with slice(-200). eventLog is a mixed stream: disposable sim noise plus important dynasty receipts. +8. Do not call eventLog retention from web commitGame(). Retention must run only in engine-safe end-of-advance paths and migrations. +9. Do not create duplicate UI routes when an existing route can be enhanced. MFSN Weekly Show belongs in the existing /newsroom surface unless repo inspection proves otherwise. +10. Do not weaken, delete, or skip tests to pass. Fix code or write honest blockers. +11. Prefer small safe checkpoints, but keep going. After each checkpoint: run focused tests, fix failures, update MISSION.md, then proceed. +12. Use the repo’s required final response shape: Understanding / Plan / Patch / Verification / Risks-Rollback. + +FIRST ACTIONS: +1. Inspect repo root. +2. Read AGENTS.md. +3. Read CODEX_GAME_GUIDE.md. +4. Inspect package scripts, SAVE_VERSION, save migrations, fixtures, and architecture-boundary tests. +5. Create or update MISSION.md at repo root with: + - mission title + - current SAVE_VERSION and fixture policy observed + - dependency-ordered task checklist + - checkpoint log + - test log + - blockers/restart point +6. From here forward, update MISSION.md after every checkpoint. + +MISSION.md MUST INCLUDE THIS MISSION BOARD: + +MISSION BOARD + +Wave 0 — Preflight, architecture truth, repo hygiene +[ ] Confirm current workspace architecture and active docs. +[ ] Remove/repair active stale monolith/three-file/CDN/Babel prompt language. +[ ] Add a short “Current Architecture Truth” section to active handoff docs if missing. +[ ] Inspect repo bloat from generated artifacts. Update .gitignore and remove clearly generated tracked artifacts only if safe. Do not delete user data or unclear historical docs. +[ ] Add/keep a warning for Kevin: manually export longest dynasty before testing any forward save migration. + +Wave 1A — Save Diet media caps +[ ] Cap mediaCycle.weeklyDigests to 34. +[ ] Cap mediaCycle.powerRankingHistory to 34. +[ ] Add migration for current save version bump. +[ ] Follow fixture drift policy exactly. If SAVE_VERSION 36 → 37, likely add v35.json and update checkedInFixtureVersions, not v37.json. +[ ] Do not touch dynastyTimeline. + +Wave 1B — eventLog year repair and retention +[ ] Add event-log-retention helper module. +[ ] Implement readGameEventYear(event, currentYear) with data.year → known ID pattern → clamped timestamp fallback. +[ ] Implement inferEventYearFromKnownIdPattern safely. +[ ] Implement withEventDate(data, year, week). +[ ] Stamp data.year/data.week on new eventLog events. +[ ] Fix deadlineResolvedEvent Date.now timestamp to logical game-time timestamp. +[ ] Update history.ts and coaching-legacy.ts to prefer readGameEventYear. +[ ] Implement trimEventLogForRetention(game) with full taxonomy below. +[ ] Apply retention only in migration and safe end-of-advance engine paths. +[ ] Add full tests below. + +Wave 1C — Loaded-save reference rehydration +[ ] Add public engine helper rehydrateGameStateReferences(game) or normalizeRuntimeGameReferences(game). +[ ] Internally call syncPlayers(game). +[ ] Export from @mfd/engine barrel. +[ ] Call from web persistence after successful schema parse and agent initialization. +[ ] Add reference-identity tests. + +Wave 1D — Cartridge size guard and optional v2 cartridge dedupe +[ ] First add cartridge size/regression tests or diagnostic guard. +[ ] Then, only if safe and fully tested, implement CARTRIDGE_VERSION 2 dedupe: + export rostered players in players map as { $roster: teamId } stubs; + in parseCartridge, rehydrate v2 players map from team rosters before SaveStateSchema validation; + v1 cartridges still parse unchanged; + persisted SaveState stays unchanged. +[ ] If v2 is not provably safe in this run, ship the guard and write docs/spec with exact blocker. Continue mission. + +Wave 2 — Web Worker sim boundary +[ ] Add apps/web/src/app/store/sim.worker.ts. +[ ] Implement request-ID protocol for advanceWeek and previewHalftimeDecision. +[ ] Rewrite sim.ts wrapper to use singleton Worker with fallback. +[ ] Update architecture-boundaries.test.ts allowlist for sim.worker.ts. +[ ] No callbacks through postMessage. +[ ] No new dependency unless repo already has an approved pattern requiring it. + +Wave 2.5 — Extract post-advance finalizer +[ ] Extract shared finalization/routing logic from advanceWeek and resolveHalftimeDecision in game-store.ts. +[ ] Keep helper local to store unless repo architecture strongly suggests otherwise. +[ ] Preserve behavior exactly. +[ ] Do not add Sim Ahead until this is done and tested. + +Wave 3 — Sim Ahead +[ ] Add engine simulateWeeks in packages/engine/src/systems/sim-ahead.ts. +[ ] Add target types and stop reasons below. +[ ] Add Worker protocol support with serializable progress frames. +[ ] Add runSimAhead in sim.ts. +[ ] Add game-store action simAhead(target), committing/autosaving once at final frame. +[ ] Add WeekAdvance UI controls and progress ticker. +[ ] Use two-class product model: “My Next Game” stops before user game; true fast-forward targets may sim through user games but must stop on interrupts/major user injury. + +Wave 4 — MFSN Weekly Show in /newsroom +[ ] Add selectors for weeklyDigests/latest digest/user-team digest segment. +[ ] Enhance existing NewsroomDigest, not a duplicate route. +[ ] Surface latest weekly digest, headlines, hot takes, power rankings, user-team segment, storyline board, CTAs. +[ ] Do not generate media during render; read saved engine output only. + +Wave 5 — Matchup Radar upgrade +[ ] Enhance existing WeekAdvance matchup panel/card. +[ ] Add rivalry/revenge/weather/primetime/opponent power rank/user injury context/why-this-game-matters. +[ ] Use existing engine data where possible; avoid save schema changes. + +Wave 6 — Chip cross-season memory callbacks +[ ] Add read-only Chip callbacks from dynastyTimeline/storylineThreads/rivalry/coaching/team history. +[ ] Add content that makes Chip a dynasty witness, not just a tutorial. +[ ] No save schema change unless clearly necessary and migrated. + +Wave 7 — Save Health Meter +[ ] Add save/version/size/integrity/slot-age/autosave/manual-export trust UI. +[ ] Surface in Settings or save/load area. +[ ] Pair with cartridge sizeBytes if available. + +Wave 8 — Community/share loops +[ ] Shareable Season Recap card PNG/export if existing SeasonRecapCard or browser APIs make it safe. +[ ] Dynasty Cartridge challenge seeds using buildCartridge/parseCartridge and v2 if implemented. +[ ] No heavy new dependency unless justified. + +Wave 9 — Transparency and performance polish +[ ] Adaptive difficulty transparency panel reading existing adaptive-difficulty state; do not change balance logic. +[ ] Selector/rerender optimization pass for hot whole-state.game subscribers. +[ ] Add or preserve tests. + +Wave 10 — Future memory archive ADR only +[ ] Create docs/adr or docs/design note for future dynastyTimeline compaction. +[ ] Preserve landmark and major memory. Do not implement timeline compaction in this mission unless explicitly safe, isolated, and versioned separately. + +EXECUTION LOOP: +For each checkpoint: +1. Inspect relevant files and tests. +2. Make the smallest coherent patch for the next dependency. +3. Add/adjust tests first or alongside patch. +4. Run focused tests. +5. Fix failures. +6. Run broader tests when a wave completes. +7. Update MISSION.md with status, commands run, results, and next checkpoint. +8. Commit if git is available and repo policy allows; otherwise leave a clean diff and detailed mission log. +9. Continue automatically to the next checkpoint. + +WAVE 0 DETAILS — ARCHITECTURE TRUTH AND HYGIENE +Search active docs/prompts for stale terms: +- game.js monolith +- single-file browser sim +- three-file delivery +- legacy copy +- sync check +- CDN/Babel +Do not rewrite historical records unnecessarily. Fix active handoff docs, prompt templates, skills, or guides that would mislead Codex/Claude. Add wording: +- MFD is a pnpm workspace. +- Engine is packages/engine. +- Web is apps/web. +- Design system is packages/design-system. +- AGENTS.md is source of truth. +- Save migrations/golden fixtures and deterministic tests govern changes. + +Repo hygiene: +- Inspect generated artifacts and .gitignore. +- If apps/web/dist, .logs/screenshots, output, coverage, screenshots, or local logs are tracked and clearly generated, remove from tracking and ignore. +- If large docs such as STATUS.md are active and too large, create a short index/summary doc and mark the large file as historical only if safe. Do not destroy useful status history. + +WAVE 1A DETAILS — MEDIA SAVE DIET +If current SAVE_VERSION is 36 and 36→37 does not exist: +- bump SAVE_VERSION to 37. +- add migration 36→37. +- follow fixture policy. The known policy from prior review: save-version-drift.test expects max fixture version = SAVE_VERSION - 2 and an explicit checkedInFixtureVersions list. Verify in repo before editing. For 36→37 this likely means add v35.json and update checkedInFixtureVersions, not v37.json. +If current version differs, adapt using the same policy and do not rewrite already-used migrations. + +Implement helpers: +- trimMediaCycle(game): weeklyDigests.slice(-34), powerRankingHistory.slice(-34). +- trimLongRunningSaveCollections(game): calls trimMediaCycle and later eventLog retention once Wave 1B exists. + +Apply media trim after weekly digest/powerRankingHistory append and inside migration. + +Tests: +- Migration trims oversized weeklyDigests to 34. +- Migration trims oversized powerRankingHistory to 34. +- week/year/phase unchanged. +- Multi-season or focused integration keeps arrays <= 34. +- golden-save/save-round-trip/save-version-drift pass. + +WAVE 1B DETAILS — EVENTLOG RETENTION AND YEAR REPAIR +Create one helper file, likely packages/engine/src/systems/event-log-retention.ts, exporting year helpers and retention. + +Known eventLog consumers that retention must not break: +- league-news.ts recent tail. +- history.ts current-year team events for season archive. +- coaching-legacy.ts coach_retirement with 20-year lookback. +- CoachingTree.tsx coach_retirement labels across all history. +- TeamNeeds.tsx gm_strategy_shift. +- franchise-week.ts deadlineAlreadyResolved current year/week. +- selectors.ts selectCoachingCarouselNews recent coach moves. + +Timestamp overflow problem: +Existing timestamps often use year * 1000 + week * 10 + eventLog.length. Once eventLog.length is high, Math.floor(timestamp / 1000) can classify an event as a future year. Fix by stamping data.year/data.week and by reading event year safely. + +Implement: +readGameEventYear(event, currentYear): +1. If event.data.year is a finite number, return Math.trunc(data.year). +2. Else infer from known ID patterns. +3. Else return Math.min(Math.floor(event.timestamp / 1000), currentYear). + +inferEventYearFromKnownIdPattern(event): +Cover known patterns only: +- weekly_result-{year}-{week}-{index} +- playoff_result-{year}-{week}-{index} +- coach_retirement-{year}-{week}-{index} +- coach_hired-{year}-{week}-{index} +- coach_fired-{year}-{week}-{index} +- coach_departed-{year}-{week}-{index} +- coach_promoted-{year}-{week}-{index} +- off_field_event-{year}-{week}-{index} +- gm-strategy-{teamId}-{year}-{index} +- player-retired-{playerId}-{year}; parse year from the END because player IDs contain hyphens. +- trade-deadline-resolved-{year}-{week} +Do not pattern-guess unknown 4-digit numbers. +press_conference events have IDs like {conference.type}-{conference.id}; no year in ID. They must fall through to clamped timestamp fallback. + +withEventDate(data, year, week): +- returns object with existing data plus year and week when known. +- does not overwrite meaningful domain fields except year/week metadata. + +Stamp new events with data.year and data.week where known. Inspect and update at least: +- franchise-week-helpers.ts makeEvent +- off-field-events.ts pushLivingWorldEvent +- press-conference.ts record/creation path +- gm-strategies.ts event creation +- progression.ts makeRetirementEvent and milestone event paths +- coach-retirement.ts event builder +- coach-retention.ts event builder +- coaching-carousel.ts event builder +- coaching-market.ts event builder, including coach_promoted +- apps/web/src/app/store/game-store.ts deadlineResolvedEvent + +Fix deadlineResolvedEvent: +- Replace Date.now() timestamp with logical game-time timestamp based on game.year, game.week, and current eventLog length. +- Ensure data.year and data.week exist. + +Update consumers: +- history.ts eventYear/getTeamMajorEvents path should use readGameEventYear. +- coaching-legacy.ts getEventYear should use readGameEventYear. + +Retention taxonomy: +Always keep: +- all events where readGameEventYear(event, game.year) === game.year +- all coach_retirement events forever +- all trade_deadline_resolved events forever + +Keep latest 500 prior-year semantic events: +- gm_strategy_shift +- coach_hired +- coach_fired +- coach_departed +- coach_promoted +- player_retired +- milestone +- off_field_event + +Keep latest 100 prior-year disposable events: +- weekly_result +- playoff_result +- press_conference + +Unknown prior-year event types: +- Preserve in disposable tail unless inspection proves they are semantic. Do not delete unknowns aggressively. + +Retention must preserve original ordering. +Retention runs only: +- migration +- safe end-of-advance engine paths after event generation +Retention must not run: +- web commitGame +- selectors +- render paths +- mid-advance before event creation finishes + +Migration: +For existing eventLog entries: +- keep existing data.year if valid +- otherwise backfill data.year from ID pattern first, then clamped timestamp fallback +- backfill data.week when inferable from data or ID pattern +- then apply retention + +Tests: +- Migration: oversized eventLog backfills data.year. +- Migration: ID-derived year beats overflowing timestamp. +- Migration: existing valid event.data.year is not overwritten. +- Retention: all current-year events survive even if >200 exist. +- Retention: old weekly_result/playoff_result/press_conference bulk is trimmed to disposable tail. +- Retention: coach_retirement survives forever, including older than 20 years. +- Retention: trade_deadline_resolved survives. +- Retention: gm_strategy_shift survives through semantic retention. +- Retention: coach_promoted is semantic. +- Retention: milestone is semantic. +- Retention: player_retired is semantic but not forever. +- deadlineAlreadyResolved still returns true after retention in same deadline year/week. +- selectCoachingCarouselNews still returns recent coach moves after retention. +- Overflow safety: with eventLog.length = 900, create/stamp new event and readGameEventYear returns game.year. +- Stamping: makeEvent, pushLivingWorldEvent, deadlineResolvedEvent, and at least one coaching event produce data.year === game.year. +- Existing golden-save and save-round-trip tests pass. + +WAVE 1C DETAILS — REHYDRATE LOADED REFERENCES +Do not import private engine internals in web. +Add exported engine helper: +- rehydrateGameStateReferences(game: GameState): GameState +or +- normalizeRuntimeGameReferences(game: GameState): GameState +It should internally call syncPlayers(game) and return game. +Export through packages/engine/src/index.ts. +Call from apps/web persistence/import path after successful SaveStateSchema parse and ensureAgentsInitialized. +No SAVE_VERSION bump. + +Tests: +- Import/load cartridge through real persistence path. +- Pick rostered player. +- Assert loaded.teams[teamId].roster[index] === loaded.players[playerId]. +- Mutating through either path is visible through the other. + +WAVE 1D DETAILS — CARTRIDGE SIZE GUARD AND V2 +First add size guard: +- Test or utility proving cartridge size for a generated multi-season save stays within a reasonable regression threshold. +- Record sizeBytes where cartridge export already supports it or add non-invasive metadata. + +Then inspect buildCartridge/parseCartridge. If safe, implement CARTRIDGE_VERSION 2: +- v1 cartridges still parse unchanged. +- v2 export replaces rostered entries in players map with stubs like { $roster: teamId }. +- v2 parse restores players map from team.roster before SaveStateSchema validation. +- Persisted SaveState and IndexedDB shape stay unchanged. +- Add tests: v1 import, v2 export/import, roster/player reference identity after import, size reduction on typical save. +If not safe, do not force it. Ship guard and write docs/spec with exact blocker and continue. + +WAVE 2 DETAILS — WEB WORKER SIM BOUNDARY +Files likely: +- apps/web/src/app/store/sim.ts +- apps/web/src/app/store/sim.worker.ts +- apps/web/src/app/store/sim.test.ts +- apps/web/src/app/architecture-boundaries.test.ts + +Protocol: +Request: +- { id, kind: 'advanceWeek', game, options } +- { id, kind: 'previewHalftimeDecision', game } +Response: +- { id, kind: 'done', result } +- { id, kind: 'error', message, stack } + +Implement singleton Worker: +new Worker(new URL('./sim.worker.ts', import.meta.url), { type: 'module' }) +Fallback: +- if typeof Worker === 'undefined' +- if Worker construction throws +- if tests run in Vitest environment without Worker +Keep runAdvanceWeek and runPreviewHalftimeDecision signatures unchanged. +Update architecture boundary allowlist so direct engine sim imports are only allowed in sim.ts and sim.worker.ts. +No callbacks through postMessage. +No new deps. + +Tests: +- fallback calls engine functions +- Worker construction failure falls back +- architecture-boundary test passes +- optional determinism compare if test environment can instantiate Worker; otherwise document why fallback-only is covered + +WAVE 2.5 DETAILS — SHARED FINALIZER +Before Sim Ahead, extract duplicate store finalization from advanceWeek and resolveHalftimeDecision. +Keep helper local to game-store.ts first. +Name example: +- finalizeAdvancedGame(previousGame, nextGame, options) +It should cover existing duplicated behavior: +- governance ensure +- trade deadline routing +- expansion draft routing +- CBA routing +- user team system fit update +- postGameUi initialization +- press conference queue +- post-advance audio queue +- breakingNewsQueue +- playoff lore staging +- recap prompt reset +- syncCareerMeta +- pendingPlayoffLoreReveal +- tutorial completion +- commit/autosave behavior +Preserve behavior exactly. +Do not add Sim Ahead in this patch. + +Tests: +- existing advanceWeek tests pass +- existing halftime tests pass +- add coverage for deadline/expansion/CBA routing if missing + +WAVE 3 DETAILS — SIM AHEAD +Engine: +Add packages/engine/src/systems/sim-ahead.ts and export from @mfd/engine. + +Types: +SimAheadTarget: +- 'next_user_game' +- 'trade_deadline' +- 'end_regular_season' +- 'playoffs' +- { weeks: number } + +SimAheadStopReason: +- 'target_reached' +- 'user_game' +- 'trade_deadline' +- 'expansion_draft' +- 'cba_interrupt' +- 'phase_changed' +- 'user_injury' +- 'safety_guard' + +SimAheadFrame: +- year +- week +- phase +- weeksSimmed +- record/summary fields if cheap and serializable + +Product rule: +- next_user_game stops before the next user-team game. +- trade_deadline/end_regular_season/playoffs/{weeks:N} may sim through user games. +- true fast-forward targets stop on trade deadline, expansion draft, CBA interrupt, relevant phase boundary, severe or season_ending user-team injury, or safety guard. +- Use actual injury tiers: severe | season_ending. Do not use major. +- Disable halftime pauses for batch fast-forward. For next_user_game, stop before game so normal Advance Week handles halftime. + +Worker: +Add request: +- { id, kind: 'simulateWeeks', game, target } +Progress: +- { id, kind: 'progress', frame } +Done: +- { id, kind: 'done', result } +Do not send callbacks through postMessage. + +Store: +Add simAhead(target) action. +Commit once at final frame. +Autosave once at final frame. +If tab closes mid-sim, latest autosave should remain pre-sim; do not persist partial frames. +Reuse finalizer from Wave 2.5. + +UI: +Enhance apps/web/src/features/week-advance/WeekAdvance.tsx. +Add controls: +- Advance Week +- Sim Ahead: My Next Game / Trade Deadline / End Regular Season / Playoffs / 4 Weeks +Show progress ticker and final stop reason. +Disable relevant controls while simming. +No emoji in UI. Follow design-system tokens / Bloomberg aesthetic. + +Tests: +- simulateWeeks N weeks equals N sequential advanceFranchiseWeek calls when no stop fires. +- trade_deadline stops at deadline with tradeDeadlineState set. +- next_user_game stops before user game. +- severe/season_ending user injury stops. +- safety guard prevents infinite loops. +- store action commits once and autosaves once. +- Worker progress messages update UI/store state. +- WeekAdvance renders controls. + +WAVE 4 DETAILS — MFSN WEEKLY SHOW +Use existing /newsroom route and components: +- NewsroomDigest +- PowerRankingsTicker +- StorylineThreadCard +Add selectors in selectors.ts or existing selector module: +- selectWeeklyDigests +- selectLatestWeeklyDigest +- selectLatestDigestPowerRankings +- selectLatestDigestUserTeamSegment + +UI sections: +- This Week on MFSN +- Opening Headlines +- Analyst Desk / Hot Takes +- Power Ranking Movement +- Your Team Segment +- Storyline Board +- CTAs to Advance Week / Game Plan / Power Rankings +Read game.mediaCycle.weeklyDigests only. Do not generate during render. +Empty state before first digest. +Tests for empty state, latest digest, hot takes, user team segment, no render-side generation. + +WAVE 5 DETAILS — MATCHUP RADAR +Enhance existing WeekAdvance matchup/radar surface. +Add as much as existing data safely supports: +- rivalry heat +- revenge game hooks +- weather/stadium context +- primetime/MFN tag if available +- opponent power rank +- major user injuries +- one sentence “why this game matters” +No save schema change unless necessary. +Tests/render snapshots as repo prefers. + +WAVE 6 DETAILS — CHIP MEMORY CALLBACKS +Goal: Chip becomes the dynasty witness. +Add read-only callback triggers from: +- dynastyTimeline +- storylineThreads +- rivalries/revenge games +- coach history +- prior playoff losses/wins if accessible +Examples of content style: +- remembers prior collapse +- calls out draft pick receipts +- notes anniversary/revenge game +- “told you so” after a previous warning pays off +No shame, no sarcasm that attacks player competence. Keep it warm, witty, and dynasty-aware. +Tests: callback selector/trigger returns deterministic dialogue for crafted state. + +WAVE 7 DETAILS — SAVE HEALTH METER +Add trust UI in Settings or Save/Load: +- save version +- cartridge sizeBytes/export size if available +- autosave/manual save slot ages +- integrity/validation status +- “manual backup recommended before migration” note if helpful +No save schema change unless existing metadata supports it. +Tests for render and formatting. + +WAVE 8 DETAILS — SHARE/COMMUNITY +Shareable Season Recap: +- Inspect existing SeasonRecapCard. +- Add PNG/export/share if feasible using existing dependencies or safe browser APIs. +- Avoid heavy new dependency unless justified. + +Dynasty Cartridge Challenge Seeds: +- Use buildCartridge/parseCartridge. +- Add UI or copy/export flow for “play my save from this moment.” +- Work with v1/v2 cartridge compatibility. +Tests for export/import compatibility. + +WAVE 9 DETAILS — TRANSPARENCY AND PERFORMANCE +Adaptive difficulty transparency: +- Surface existing adaptive difficulty state/calculations. +- Make rubber-banding understandable and player-trust-building. +- Do not change balance logic unless tests are updated and scope is explicit. + +Selector/rerender pass: +- Identify hot components subscribing to whole state.game. +- Move hot paths to selectors. +- Preserve behavior. +- Use tests and build to catch mistakes. + +WAVE 10 DETAILS — DYNASTY TIMELINE ADR +Create a future-facing design note only: +- Problem: dynastyTimeline grows forever. +- Constraint: landmarks and majors are sacred. +- Proposal options for v38+ archive/compaction. +- Migration and UI implications. +Do not implement in this marathon unless everything else is done and a fully safe migration is obvious. Default: ADR only. + +TEST COMMANDS +Discover actual package scripts first. Preferred broad commands: +- pnpm --filter @mfd/engine test +- pnpm --filter @mfd/web test +- pnpm --filter @mfd/web build +- pnpm test:perft +- pnpm lint or equivalent if available +Run focused tests during patches, broad tests after waves. +If a command is unavailable or too slow/time-limited, document exact reason in MISSION.md and final response. + +MANUAL QA DOC +Create or update docs/qa/MFD_EVOLUTION_MANUAL_QA.md with browser tests Kevin should run: +1. Export longest dynasty before migration. +2. Load longest dynasty after v37/vNext migration. +3. Advance 3 weeks; export cartridge; note size. +4. Advance 10 more weeks; export; size growth should flatten. +5. Reload latest autosave; verify roster/standings/timeline/newsroom. +6. Test trade deadline path after Worker and Sim Ahead. +7. Test halftime decision after Worker. +8. Test Sim to Trade Deadline, Sim to Playoffs, Sim 4 Weeks, My Next Game. +9. Verify MFSN Weekly Show updates after weekly advance. +10. Verify no torn autosave if closing during Sim Ahead. + +FINAL RESPONSE REQUIREMENTS +When finished or blocked by environment limits, respond with: +Understanding: +- one paragraph of what changed and why. +Plan: +- completed waves and skipped/blocked waves. +Patch: +- file groups changed by wave. +Verification: +- every command run and result. +- any manual QA docs created. +Risks-Rollback: +- save migration risk and rollback note. +- any tasks intentionally deferred. +- exact restart point from MISSION.md if not complete. + +START NOW. +Do not ask for confirmation. Do not stop at planning. Create/update MISSION.md and begin Wave 0, then continue through the mission board in dependency order. +``` diff --git a/STATUS.md b/STATUS.md index cbaf6aa2..7c399a77 100644 --- a/STATUS.md +++ b/STATUS.md @@ -1,5 +1,32 @@ # STATUS +## Run Ledger - 2026-07-08 Council Shared Brief Proposal Packet + +Completed the `00_MFD_COUNCIL_SHARED_BRIEF.md` council deliverable as a docs/schema artifact. Added the missing structured proposal schema required by the brief, then produced a source-backed council proposal packet that scores current proposals, preserves the complete gameplay-slice fields, labels assumptions/falsification evidence, and triages older brief gaps against the current ledgers so already-shipped work is not reopened. + +### Files Changed + +- `schemas/MFD_COUNCIL_PROPOSAL_SCHEMA.json` +- `artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.json` +- `artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.md` +- `STATUS.md` + +### Verification + +- Passed: JSON parse for the schema and proposal packet. +- Passed: score-total consistency check across all proposal scoring fields. +- Passed: `npx --yes ajv-cli@5 validate --spec=draft2020 --strict=false -s schemas/MFD_COUNCIL_PROPOSAL_SCHEMA.json -d artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.json`. +- Passed: ASCII punctuation scan for the new schema/proposal files. + +### Checks Not Run + +- No engine/web/design tests, typecheck, build, browser smoke, playtest, or release gate were run because this was a docs/schema artifact only and did not touch runtime code, save schema, sim math, RNG, UI source, package scripts, or dependencies. +- Repo-local git status/diff is not useful for this extracted checkout because `git rev-parse --show-toplevel` resolves to `/Users/tkevinbigham/Downloads`, and this `MFD-main` folder appears there as an untracked directory alongside unrelated Downloads files. + +### Rollback + +Remove the three council artifact files and this ledger entry. Runtime rollback is not required. + ## Run Ledger - 2026-07-06 GOAT Director Patch 3.1 Team Window Read-Model Implemented Patch 3.1 — Team window read-model. `computeTeamWindow` is a web-only deterministic read-model that classifies CPU clubs as `ALL_IN`, `CONTEND`, `RETOOL`, or `REBUILD`, with `clear` or `mixed` confidence and 2-4 receipted drivers. The rubric reads only already-saved or render-derived inputs: roster/QB/core age and OVR, current cap space, draft-pick inventory, saved `team.gmStrategy`, saved `team.philosophy`, current record, team-needs cap flexibility, and saved `game.franchiseHistory`. diff --git a/artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.json b/artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.json new file mode 100644 index 00000000..f46c4bc5 --- /dev/null +++ b/artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.json @@ -0,0 +1,694 @@ +{ + "packetVersion": "1.0", + "briefFile": "00_MFD_COUNCIL_SHARED_BRIEF.md", + "generatedAt": "2026-07-08", + "workingRoot": "/Users/tkevinbigham/Downloads/MFD-main", + "evidenceBasis": [ + { + "kind": "brief", + "file": "00_MFD_COUNCIL_SHARED_BRIEF.md", + "lines": "1-131", + "claim": "The council brief requires proposal scoring, complete gameplay-slice fields, evidence discipline, and use of schemas/MFD_COUNCIL_PROPOSAL_SCHEMA.json." + }, + { + "kind": "current_ledger", + "file": "STATUS.md", + "lines": "1-40", + "claim": "The latest ledger records Patch 3.1 Team Window Read-Model and names Counterfactual FA/draft receipts as the best next slice." + }, + { + "kind": "current_ledger", + "file": "RELEASE_CONVERGENCE.md", + "lines": "1-47", + "claim": "The current release ledger records CI release-gate wiring, green G1-G6 gates, and a remaining YELLOW G7 requiring one uninterrupted local pass plus remote proof." + }, + { + "kind": "current_source", + "file": "packages/engine/src/config/difficulty.ts", + "lines": "1-73", + "claim": "The inspected source-truth save schema version is SAVE_VERSION = 36." + } + ], + "gapTriage": [ + { + "briefGap": "Full release gate is not enforced in CI/deploy.", + "currentStatus": "partially-mitigated", + "basis": "RELEASE_CONVERGENCE.md now says CI has a dependent release-gate job, but G7 remains YELLOW until one uninterrupted local 36/36 pass and one remote proof exist.", + "proposalIds": ["g7-release-certificate"] + }, + { + "briefGap": "No clean 25- or 50-year quality certificate.", + "currentStatus": "partially-mitigated", + "basis": "CODEX_IMPROVEMENT_PLAN.md now records opt-in goat-25y/goat-50y profiles with 0 high anomalies, but the public release certificate still needs a current default gate and remote proof.", + "proposalIds": ["g7-release-certificate"] + }, + { + "briefGap": "Draft war-room trade acceptance can desynchronize pick assets and draft order.", + "currentStatus": "superseded", + "basis": "packages/engine/src/systems/draft-war-room.ts now validates live assets and updates draft-order ownership; draft-war-room and store tests cover accepted offers and no-partial-apply behavior.", + "proposalIds": [] + }, + { + "briefGap": "Generated draft offers can reference assets that are not truly transferable.", + "currentStatus": "superseded", + "basis": "draftTradeCandidates filters to hasSourceDraftOrderPick, future assets come from owned team.draftPicks, and current tests cover source-backed offers.", + "proposalIds": [] + }, + { + "briefGap": ".mfd backups omit emotional browser-local sidecars.", + "currentStatus": "superseded", + "basis": "STATUS.md records Patch 0.3 Backup Trust making Combined Backup the primary portable export/import path and classic .mfd advanced current-save-only recovery.", + "proposalIds": [] + }, + { + "briefGap": "Several long-history save fields and game results remain permissively validated.", + "currentStatus": "open", + "basis": "packages/engine/src/save/schema.ts still contains z.any islands for ScheduledGame.result, franchiseHistory, playerArchive, eventLog, weekSummaries, playoffBracket, and related long-history fields.", + "proposalIds": ["long-history-schema-hardening"] + }, + { + "briefGap": "CPU strategy exists but lacks a durable player-facing intent history.", + "currentStatus": "partially-mitigated", + "basis": "CODEX_IMPROVEMENT_PLAN.md records saved-field intent ledger, market receipts, strategy-history display, and Team Window read-model work. Durable cross-window history remains a future schema-backed option.", + "proposalIds": ["shared-action-receipt-contract"] + }, + { + "briefGap": "Trick plays are planned but not consumed by live simulation.", + "currentStatus": "open", + "basis": "GamePlanSetup labels trick plays as planning only, and trick-plays tests still guard that game-sim does not import executeTrickPlay or shouldCallTrickPlay.", + "proposalIds": ["trick-play-live-drive-integration"] + }, + { + "briefGap": "Position coaches affect progression but lack a hiring/development lifecycle.", + "currentStatus": "superseded", + "basis": "Current store and CoachingStaff source expose initializePositionCoachesForTeam and upgradePositionCoachRole; the status/plan ledger records position-coach commands and season rollover tenure.", + "proposalIds": [] + }, + { + "briefGap": "Press conferences store quotes without meaningful downstream effects.", + "currentStatus": "open", + "basis": "PressConferenceModal still says Public quote only and No gameplay change; respondToPressConference only writes selectedTier and selectedResponse.", + "proposalIds": ["press-conference-low-stakes-consequence"] + }, + { + "briefGap": "Inbox read state is not durable.", + "currentStatus": "open", + "basis": "InboxTriage source rows say selectedMsg/filter are local only and durable read receipts remain outside the route until GameState exposes a saved receipt field.", + "proposalIds": ["durable-inbox-read-receipts"] + }, + { + "briefGap": "Formation-aware lineup validation is incomplete.", + "currentStatus": "open", + "basis": "DepthChart currently counts starters by position targets; no formation package validation is surfaced in the inspected route slice.", + "proposalIds": ["formation-aware-lineup-validation"] + }, + { + "briefGap": "Major actions do not share one consistent receipt contract.", + "currentStatus": "partially-mitigated", + "basis": "Many route-local receipts have shipped, but acceptTradeOffer still returns an EngineOutput with empty events and consequences after mutating rosters, news, social feed, and press conference queues.", + "proposalIds": ["shared-action-receipt-contract"] + }, + { + "briefGap": "Existing progressive-nav metadata is not used to reduce overload.", + "currentStatus": "partially-mitigated", + "basis": "The UI/UX convergence ledger records exposing static routes and using navigation/unlock metadata; further progressive disclosure is product-tuning, not a top current risk.", + "proposalIds": [] + }, + { + "briefGap": "Dynasty memory is split among routes and sidecars.", + "currentStatus": "partially-mitigated", + "basis": "Combined backup, dynasty callbacks, session recap, draft follow-through, and Game Day player-arc callbacks reduce the split. Remaining memory work should be tied to specific routes or saved history schemas.", + "proposalIds": ["long-history-schema-hardening"] + }, + { + "briefGap": "The game has lists and archives but does not consistently author era stories back to the player.", + "currentStatus": "partially-mitigated", + "basis": "Recent ledgers record callbacks/session recap and many read-model surfaces. This stays a later content/system compounding theme unless a concrete source gap is re-confirmed.", + "proposalIds": [] + }, + { + "briefGap": "Player arcs, grudges, agents, mentorships, injuries, playoff failures, and comeback seasons do not recur strongly enough at emotional moments.", + "currentStatus": "not-ranked-now", + "basis": "This is directionally valid, but the inspected current ledgers already shipped several callback/read-model slices. Rank concrete remaining source gaps after the higher-trust items.", + "proposalIds": [] + }, + { + "briefGap": "Direct-only history routes are easy to miss.", + "currentStatus": "partially-mitigated", + "basis": "UI/UX convergence exposed all static routes through the app shell and left only dynamic player routes contextual. Remaining CTAs should be event-specific.", + "proposalIds": [] + } + ], + "proposals": [ + { + "id": "trick-play-live-drive-integration", + "title": "Trick Play Live-Drive Integration", + "summary": "Consume the existing saved weekly-prep trick-play selections inside the live game drive loop with bounded frequency, seeded RNG, and a postgame receipt. This turns a high-emotion planning choice into a visible football consequence without adding a new save island.", + "briefAlignment": ["consequence", "trust", "memory", "clarity"], + "complexity": "M", + "confidence": "high", + "recommendedDisposition": "now", + "scoring": { + "playerImpact": 9, + "trustImprovement": 7, + "intelligenceExplainability": 3, + "consequenceStrength": 9, + "dynastyMemoryCompounding": 7, + "reuseOfExistingSystems": 8, + "technicalFeasibility": 6, + "deterministicTestability": 7, + "saveMigrationSafety": 8, + "cognitiveLoadEffect": 7, + "total": 71 + }, + "evidence": [ + { + "kind": "audit", + "file": "MFD_AI_SIM_AUDIT.md", + "lines": "34-46", + "claim": "Audit finding A02 says trick plays are not part of live game simulation.", + "quotedSourceEvidence": "GamePlanSetup.tsx:626-627, trick-plays.test.ts:198-230" + }, + { + "kind": "current_source", + "file": "apps/web/src/features/game-plan/GamePlanSetup.tsx", + "lines": "138-146, 626-627", + "claim": "The route labels trick-play count as planning-only and names executeTrickPlay and shouldCallTrickPlay as not live-drive consumers." + }, + { + "kind": "current_source", + "file": "packages/engine/src/systems/trick-plays.ts", + "lines": "188-267", + "claim": "Existing deterministic helpers already execute trick plays and decide call frequency from injected RNG and game context." + } + ], + "assumptions": [ + "The first safe slice can consume already-saved WeeklyPrepPlan.trickPlays rather than adding new persistent fields.", + "Postgame receipt can use existing game result or GameDay package presentation before adding a new long-term archive field." + ], + "falsificationEvidence": [ + "A source check showing game-sim already imports and calls executeTrickPlay or shouldCallTrickPlay during live drives.", + "A passing test that proves a saved trickPlay selection can alter a same-seed game result and produce a visible postgame receipt." + ], + "sliceDefinition": { + "playerMomentOrCpuTrigger": "Player saves Weekly Prep with one or more trick plays before simming the next user-team game.", + "decisionOrPolicy": "A planned trick play may be called at most a small bounded number of times per game, using existing shouldCallTrickPlay gating plus coach/team eligibility.", + "stateRead": "Read saved weekly prep plan, selected trick play ids, team staff gameplan rating/traits, game score/quarter/playoff context, and existing drive RNG channel.", + "stateMutation": "Mutate only normal game result/play-by-play fields and existing postgame receipt surfaces. Do not add GameState fields in the first slice unless the receipt must persist beyond existing result data.", + "deterministicResolutionRules": "All stochastic calls use the established seeded engine RNG path. Same seed plus same prep choices must produce the same call/no-call, yards, fumble, touchdown, and commentary.", + "immediateReceipt": "Game Day recap and/or post-week package shows a Trick Play Receipt with play name, context, success/failure, yards/turnover/touchdown, and source plan.", + "laterCallback": "If existing named-game or game-day memory systems can read the saved receipt, allow a later recap/callback row. Otherwise defer deeper memory to a second schema-backed slice.", + "saveAndMigrationImpact": "Prefer no save migration by deriving from existing weekly prep and existing game result/package payloads. If a new persistent receipt array is needed, require type, Zod schema, migration, seed default, fixtures, and old-save tests.", + "existingRouteAndSystemIntegrations": [ + "packages/engine/src/systems/trick-plays.ts", + "packages/engine/src/systems/game-sim.ts", + "apps/web/src/features/game-plan/GamePlanSetup.tsx", + "apps/web/src/features/game-day/GameDayRecap.tsx" + ], + "tests": [ + "packages/engine/src/systems/trick-plays.test.ts", + "packages/engine/src/systems/game-sim*.test.ts focused same-seed coverage", + "apps/web/src/features/game-day/GameDayRecap.test.tsx receipt rendering", + "bash scripts/check-math-random.sh", + "pnpm test:perft or direct playtest equivalent because sim behavior changes" + ], + "acceptanceMetrics": [ + "Same seed and same prep generate byte-stable trick-play outcomes in focused tests.", + "No Math.random hits are introduced.", + "Trick-play call rate remains within a documented small sanity range.", + "Postgame receipt appears only when a trick play is actually consumed." + ], + "rollbackBoundary": "Revert the game-sim integration, receipt UI/tests, and any docs. Existing saved weekly-prep trickPlay arrays remain valid and harmless because the helpers already existed." + } + }, + { + "id": "durable-inbox-read-receipts", + "title": "Durable Inbox Read Receipts", + "summary": "Let players clear regenerated inbox rows across route visits with a scoped browser-local receipt map keyed by dynasty and stable message id. This improves weekly clarity while avoiding a v36 GameState migration.", + "briefAlignment": ["clarity", "trust", "memory"], + "complexity": "S", + "confidence": "high", + "recommendedDisposition": "now", + "scoring": { + "playerImpact": 7, + "trustImprovement": 8, + "intelligenceExplainability": 2, + "consequenceStrength": 3, + "dynastyMemoryCompounding": 5, + "reuseOfExistingSystems": 7, + "technicalFeasibility": 9, + "deterministicTestability": 10, + "saveMigrationSafety": 8, + "cognitiveLoadEffect": 9, + "total": 68 + }, + "evidence": [ + { + "kind": "audit", + "file": "MFD_WIRING_AUDIT.md", + "lines": "29-43", + "claim": "Audit finding W10 says Inbox read state is regenerated display state and not durable.", + "quotedSourceEvidence": "InboxTriage.tsx:60-78" + }, + { + "kind": "current_source", + "file": "apps/web/src/features/inbox/InboxTriage.tsx", + "lines": "47-83", + "claim": "The current source panel says selectedMsg/filter/replay state is React only and does not write read receipts, browser storage, or GameState." + }, + { + "kind": "current_source", + "file": "apps/web/src/features/inbox/buildInboxMessages.ts", + "lines": "138-207", + "claim": "Inbox messages already carry stable ids, sender/type/read flags, and owner-personality/apology-thread inputs." + } + ], + "assumptions": [ + "Browser-local receipts are acceptable because this is a clarity convenience, not dynasty-critical history.", + "The sidecar should be included in combined backup only in a later portability slice if user value warrants it." + ], + "falsificationEvidence": [ + "A current source check showing Inbox already persists read receipts across reloads for regenerated messages.", + "A product decision that read receipts must travel with .mfd cartridges, which would force a save-schema proposal instead of this sidecar slice." + ], + "sliceDefinition": { + "playerMomentOrCpuTrigger": "Player opens Inbox, reads or dismisses a message, leaves the route, and returns later in the same dynasty.", + "decisionOrPolicy": "Mark message ids as read for the current dynasty only; clear stale entries when message ids no longer appear.", + "stateRead": "Read regenerated InboxMessage ids, dynasty id/current team context, and browser-local receipt map.", + "stateMutation": "Write only browser-local receipt sidecar entries such as mfd.inbox.read.v1:. Do not mutate GameState in this slice.", + "deterministicResolutionRules": "Message generation remains pure and deterministic from selectors. Read state overlays by exact id lookup with no RNG or wall-clock dependence except optional pruning metadata.", + "immediateReceipt": "The message row loses New status, urgent counts update, and source copy labels browser-local read receipt ownership.", + "laterCallback": "Returning to Inbox in the same browser/dynasty preserves cleared messages; new message ids still surface as unread.", + "saveAndMigrationImpact": "No SAVE_VERSION change. If later moved into GameState, add types, schema, migrations, defaults, and old-save tests.", + "existingRouteAndSystemIntegrations": [ + "apps/web/src/features/inbox/InboxTriage.tsx", + "apps/web/src/features/inbox/buildInboxMessages.ts", + "apps/web/src/lib or features/inbox sidecar helper" + ], + "tests": [ + "apps/web/src/features/inbox/InboxTriage.test.tsx for mark-read behavior and source copy", + "sidecar helper unit tests for per-dynasty isolation and malformed data recovery", + "focused browser reload smoke if a local app target is available" + ], + "acceptanceMetrics": [ + "Read message remains read after route remount and hard reload.", + "Different dynasty id does not inherit read receipts.", + "No GameState, save schema, sim, RNG, or engine files are changed." + ], + "rollbackBoundary": "Revert Inbox route/helper/tests. Existing browser-local mfd.inbox.read.v1 keys can be ignored or removed; no save repair is required." + } + }, + { + "id": "g7-release-certificate", + "title": "G7 Release Certificate", + "summary": "Convert the current stitched release evidence into a clean release certificate: one uninterrupted default local release-gate pass, then one remote GitHub Actions release-gate pass from the real repository, with ledgers updated only after proof exists.", + "briefAlignment": ["trust", "clarity"], + "complexity": "M", + "confidence": "high", + "recommendedDisposition": "now", + "scoring": { + "playerImpact": 6, + "trustImprovement": 10, + "intelligenceExplainability": 1, + "consequenceStrength": 2, + "dynastyMemoryCompounding": 3, + "reuseOfExistingSystems": 9, + "technicalFeasibility": 7, + "deterministicTestability": 10, + "saveMigrationSafety": 10, + "cognitiveLoadEffect": 7, + "total": 65 + }, + "evidence": [ + { + "kind": "current_ledger", + "file": "RELEASE_CONVERGENCE.md", + "lines": "1-47", + "claim": "G1-G6 are green and CI release-gate wiring exists, but G7 remains YELLOW until one uninterrupted local pass and one remote CI proof exist." + }, + { + "kind": "current_ledger", + "file": "STATUS.md", + "lines": "1-40", + "claim": "The latest targeted patch ran focused web tests, recursive typecheck, and randomness scan, but not the full release gate." + }, + { + "kind": "audit", + "file": "MFD_MASTER_AUDIT_REPORT.md", + "lines": "13-31", + "claim": "The original release verdict was YELLOW because wide public release needed stronger release proof.", + "quotedSourceEvidence": "scripts/release-gate.mjs:76-202, RELEASE_CONVERGENCE.md, .github/workflows/ci.yml, .github/workflows/deploy.yml" + } + ], + "assumptions": [ + "This proposal is an operational trust slice rather than gameplay implementation.", + "Remote proof requires the real git repository or a push/PR/dispatch path, which this extracted checkout may not have." + ], + "falsificationEvidence": [ + "A current RELEASE_CONVERGENCE.md entry showing a post-repair uninterrupted default 36/36 local pass plus remote GitHub Actions release-gate pass.", + "A repository setting or workflow change proving the release-gate job is already protected and required." + ], + "sliceDefinition": { + "playerMomentOrCpuTrigger": "Release operator prepares a public build or release-candidate handoff.", + "decisionOrPolicy": "Do not claim G7 GREEN until the exact default release gate passes locally in one uninterrupted run and the remote release-gate job passes from the real repository.", + "stateRead": "Read scripts/release-gate.mjs, RELEASE_CONVERGENCE.md, STATUS.md, package scripts, and GitHub Actions workflow result.", + "stateMutation": "No GameState mutation. Update release ledgers/docs only after proof exists.", + "deterministicResolutionRules": "Use the existing deterministic release-gate steps, Math.random audit, playtests, shadow regression, and browser smokes without changing thresholds during the certificate run.", + "immediateReceipt": "A release-certificate ledger entry with exact command, step count, runtime, failing/passing step names, and environment notes.", + "laterCallback": "Remote CI badge/protected-check status becomes the release blocker for future public artifacts.", + "saveAndMigrationImpact": "No save/schema impact.", + "existingRouteAndSystemIntegrations": [ + "scripts/release-gate.mjs", + ".github/workflows/ci.yml", + "RELEASE_CONVERGENCE.md", + "STATUS.md", + "docs/release/MFD_RELEASE_CANDIDATE_HANDOFF.md" + ], + "tests": [ + "node scripts/release-gate.mjs", + "Remote GitHub Actions release-gate job via push, PR, or workflow_dispatch" + ], + "acceptanceMetrics": [ + "Local gate reports 36/36 in one uninterrupted default command.", + "Remote release-gate job is green for the same code state.", + "Release docs state exactly what passed and do not reuse stitched proof as final proof." + ], + "rollbackBoundary": "Revert only documentation or workflow-protection edits if needed. No runtime rollback." + } + }, + { + "id": "shared-action-receipt-contract", + "title": "Shared Action Receipt Contract", + "summary": "Promote the scattered route-local receipt pattern into a shared contract starting with accepted trades, so major actions consistently return what changed, why it happened, source state, and later callback hooks.", + "briefAlignment": ["trust", "intelligence", "consequence", "clarity", "memory"], + "complexity": "M", + "confidence": "high", + "recommendedDisposition": "next", + "scoring": { + "playerImpact": 8, + "trustImprovement": 8, + "intelligenceExplainability": 7, + "consequenceStrength": 8, + "dynastyMemoryCompounding": 6, + "reuseOfExistingSystems": 8, + "technicalFeasibility": 7, + "deterministicTestability": 8, + "saveMigrationSafety": 7, + "cognitiveLoadEffect": 8, + "total": 75 + }, + "evidence": [ + { + "kind": "audit", + "file": "MFD_WIRING_AUDIT.md", + "lines": "29-43", + "claim": "Audit finding W26 says trade accept returns empty events/consequences despite news/social side effects.", + "quotedSourceEvidence": "trade-market.ts:406-477" + }, + { + "kind": "current_source", + "file": "packages/engine/src/systems/trade-market.ts", + "lines": "406-477", + "claim": "acceptTradeOffer mutates players/picks/cap, tx logs, press conferences, league news, and social feed, then returns events: [] and consequences: []." + }, + { + "kind": "current_ledger", + "file": "CODEX_IMPROVEMENT_PLAN.md", + "lines": "58-59", + "claim": "Many acquisition routes now render market receipts, but the plan still advises behavior work or durable history only with explicit tests." + } + ], + "assumptions": [ + "The first slice should define a minimal non-persistent EngineOutput receipt before adding saved eventLog history.", + "Routes can render the receipt from the resolved action result or from already-mutated saved fields while the contract matures." + ], + "falsificationEvidence": [ + "A current accepted-trade source path that already returns structured events/consequences consumed by Trade Center.", + "A single shared receipt type already used by trades, draft trades, free agency, cuts, depth changes, and press responses." + ], + "sliceDefinition": { + "playerMomentOrCpuTrigger": "Player accepts a generated trade or submits another major roster/acquisition action.", + "decisionOrPolicy": "Every major action returns a structured receipt with changed assets, source inputs, deterministic reason, warnings, and suggested follow-up route.", + "stateRead": "Read action inputs, prior team/player/cap/pick state, scenario constraints, route forecasts, and existing news/social/press side effects.", + "stateMutation": "For first slice, mutate existing action state only and populate EngineOutput.events/consequences. Persisting receipts into GameState eventLog is a later save-schema slice.", + "deterministicResolutionRules": "Receipt fields are deterministic pure summaries of the accepted action and existing state; any social/news RNG remains on existing seeded paths.", + "immediateReceipt": "Trade Center displays a standardized Action Receipt after accept/submit/counter with assets moved, cap delta, roster risk, news/social/press side effects, and follow-up route.", + "laterCallback": "Later slices can feed the same receipt into Inbox, team history, or eventLog once save schema is tightened.", + "saveAndMigrationImpact": "No migration for first EngineOutput-only version. Persisted receipt history would require GameState/eventLog schema and old-save tests.", + "existingRouteAndSystemIntegrations": [ + "packages/engine/src/types or systems EngineOutput contract", + "packages/engine/src/systems/trade-market.ts", + "apps/web/src/features/trades/TradeCenter.tsx", + "apps/web/src/app/store/game-store.ts" + ], + "tests": [ + "packages/engine/src/systems/trade-market.test.ts accepted-trade receipt", + "apps/web/src/features/trades/TradeCenter.test.tsx receipt render", + "focused source tests ensuring no route-only private receipt schema diverges" + ], + "acceptanceMetrics": [ + "Accepted trade no longer returns empty events/consequences.", + "Receipt describes every player/pick movement and cap-sync side effect.", + "No new save fields are introduced in v1." + ], + "rollbackBoundary": "Revert shared receipt type, accepted-trade receipt population, and UI/tests. Existing trade behavior remains unchanged if receipt rendering is removed." + } + }, + { + "id": "press-conference-low-stakes-consequence", + "title": "Low-Stakes Press Conference Consequences", + "summary": "Turn quote-only press conferences into a bounded, deterministic consequence system using existing saved queues/effects/news hooks, while preserving the current truth that game results are final.", + "briefAlignment": ["consequence", "memory", "clarity"], + "complexity": "M", + "confidence": "medium", + "recommendedDisposition": "next", + "scoring": { + "playerImpact": 7, + "trustImprovement": 5, + "intelligenceExplainability": 4, + "consequenceStrength": 8, + "dynastyMemoryCompounding": 7, + "reuseOfExistingSystems": 7, + "technicalFeasibility": 6, + "deterministicTestability": 7, + "saveMigrationSafety": 5, + "cognitiveLoadEffect": 6, + "total": 62 + }, + "evidence": [ + { + "kind": "audit", + "file": "MFD_WIRING_AUDIT.md", + "lines": "29-43", + "claim": "Audit finding W06 says press conferences are quote-only.", + "quotedSourceEvidence": "PressConferenceModal.tsx:101-111, game-store.ts:2623-2631" + }, + { + "kind": "current_source", + "file": "apps/web/src/features/game-day/PressConferenceModal.tsx", + "lines": "119-129", + "claim": "The current modal says the answer changes only the saved quote and not game result, headline, owner reaction, player changes, news, social reaction, or next-week state." + }, + { + "kind": "current_source", + "file": "apps/web/src/app/store/game-store.ts", + "lines": "2685-2694", + "claim": "respondToPressConference currently writes selectedTier and selectedResponse only." + } + ], + "assumptions": [ + "Effects should be bounded and legible, not hidden global modifiers.", + "The first consequence set should avoid retroactively changing completed game results." + ], + "falsificationEvidence": [ + "A current source path showing press response tier already changes owner/player/media/news or next-week state.", + "A product decision that press conferences should remain cosmetic roleplay only." + ], + "sliceDefinition": { + "playerMomentOrCpuTrigger": "Player answers a postgame press conference after a notable game or transaction.", + "decisionOrPolicy": "High/mid/low response tier applies one small, labeled effect such as owner patience, morale, media sentiment, or social/news framing, capped by scenario context.", + "stateRead": "Read pressConferenceQueue entry, selected tier/response, team owner/personality context, recent result/transaction context, existing off-field/timed-effect systems, and seed.", + "stateMutation": "Write selected response plus one bounded existing effect/news/social entry if the existing schema can represent it. If not, add explicit schema/migration coverage before persisting.", + "deterministicResolutionRules": "Tier mapping is deterministic from context and selected tier. Any stochastic flavor uses the existing seeded RNG channel, not render-time randomness.", + "immediateReceipt": "Press modal shows a Podium Consequence receipt before and after selection with effect magnitude, duration, and no retroactive result change.", + "laterCallback": "Inbox, newsroom, or Monday Briefing can resurface the quote/effect while the duration is active.", + "saveAndMigrationImpact": "Likely touches saved postGameUi/recentPressConferences/activeEffects or news/social arrays. If field shapes change, update types, Zod schema, migrations, seed defaults, and old-save tests.", + "existingRouteAndSystemIntegrations": [ + "apps/web/src/features/game-day/PressConferenceModal.tsx", + "apps/web/src/app/store/game-store.ts", + "packages/engine/src/systems/press-conference.ts", + "packages/engine/src/systems/off-field-events or activeEffects" + ], + "tests": [ + "focused store tests for response tier consequences", + "PressConferenceModal tests for preview and receipt copy", + "save/schema/migration tests if any persistent shape changes", + "bash scripts/check-math-random.sh" + ], + "acceptanceMetrics": [ + "Each tier has a clear bounded effect and rollback path.", + "Completed game result remains unchanged.", + "Old saves without the new effect data still load." + ], + "rollbackBoundary": "Revert store consequence mapping, UI receipt, tests, and any schema migration. If a new save version is introduced, rollback requires migration policy rather than simple source revert." + } + }, + { + "id": "long-history-schema-hardening", + "title": "Long-History Save Schema Hardening", + "summary": "Tighten one permissive long-history save island per slice, starting with ScheduledGame.result or weekSummaries, so deep dynasties fail early on corrupt data instead of surfacing broken history years later.", + "briefAlignment": ["trust", "memory", "clarity"], + "complexity": "L", + "confidence": "high", + "recommendedDisposition": "next", + "scoring": { + "playerImpact": 6, + "trustImprovement": 9, + "intelligenceExplainability": 1, + "consequenceStrength": 3, + "dynastyMemoryCompounding": 7, + "reuseOfExistingSystems": 6, + "technicalFeasibility": 6, + "deterministicTestability": 9, + "saveMigrationSafety": 4, + "cognitiveLoadEffect": 8, + "total": 59 + }, + "evidence": [ + { + "kind": "audit", + "file": "MFD_MASTER_AUDIT_REPORT.md", + "lines": "32-87", + "claim": "The master audit ranks permissive long-history save fields as a trust risk.", + "quotedSourceEvidence": "schema.ts:1777, 2038, 2052-2054, 2070-2074, 2100-2113, 2214" + }, + { + "kind": "current_source", + "file": "packages/engine/src/save/schema.ts", + "lines": "1649,1777,2038,2052-2054,2070-2074,2100-2113,2214", + "claim": "The current schema still has z.any and passthrough islands for several long-history structures." + }, + { + "kind": "current_source", + "file": "packages/engine/src/config/difficulty.ts", + "lines": "1-73", + "claim": "SAVE_VERSION remains 36, so any persistent schema tightening must preserve old-save import behavior deliberately." + } + ], + "assumptions": [ + "A narrow schema family per slice is safer than trying to type every z.any island at once.", + "Malformed historical rows should be migrated, sanitized, or rejected with diagnostics depending on player data risk." + ], + "falsificationEvidence": [ + "A current save/schema source check showing the named history family is already fully typed and validated.", + "Golden old-save tests showing malformed rows are already normalized before SaveStateSchema validation." + ], + "sliceDefinition": { + "playerMomentOrCpuTrigger": "Player imports or loads a long-running dynasty with years of game results, summaries, playoff data, records, and history.", + "decisionOrPolicy": "Tighten one high-value history family first and define compatibility behavior for missing/malformed legacy rows.", + "stateRead": "Read current type definitions, SaveStateSchema, migrations, historical fixtures, current generated v36 fixture policy, and all consumers of the selected history field.", + "stateMutation": "Mutate save parsing/migration behavior only. Runtime game state shape changes only if the selected family already has a TypeScript type contract.", + "deterministicResolutionRules": "Migration/sanitization is pure and deterministic from raw save payload; no RNG or wall-clock data.", + "immediateReceipt": "Import/load diagnostics name the first schema issue paths clearly when a save cannot be repaired.", + "laterCallback": "Deep history routes become safer because corrupt rows are rejected or normalized at import time.", + "saveAndMigrationImpact": "Save-sensitive by definition. Requires TypeScript types, Zod schema, migration/defaults if needed, golden/generated fixture updates, old-save tests, and SAVE_VERSION policy decision.", + "existingRouteAndSystemIntegrations": [ + "packages/engine/src/save/schema.ts", + "packages/engine/src/save/migrations.ts", + "packages/engine/src/types/sim.ts or season/history types", + "apps/web/src/app/store/persistence.ts" + ], + "tests": [ + "pnpm --filter @mfd/engine test -- save", + "golden-saves and save-round-trip tests", + "consumer tests for the selected history field", + "old-save malformed-row import test" + ], + "acceptanceMetrics": [ + "Selected z.any island is replaced by a bounded schema.", + "Old representative saves still migrate and parse.", + "Malformed representative rows produce deterministic repair or concise diagnostics." + ], + "rollbackBoundary": "Revert schema/migration/type/test changes. If SAVE_VERSION is incremented, rollback must preserve migration continuity and fixture policy." + } + }, + { + "id": "formation-aware-lineup-validation", + "title": "Formation-Aware Lineup Validation", + "summary": "Upgrade Depth Chart readiness from raw starter counts to formation/package validity, so the player knows whether the selected offense, defense, and special teams can actually field the required roles.", + "briefAlignment": ["trust", "clarity", "consequence"], + "complexity": "M", + "confidence": "medium", + "recommendedDisposition": "later", + "scoring": { + "playerImpact": 7, + "trustImprovement": 7, + "intelligenceExplainability": 2, + "consequenceStrength": 5, + "dynastyMemoryCompounding": 3, + "reuseOfExistingSystems": 7, + "technicalFeasibility": 7, + "deterministicTestability": 9, + "saveMigrationSafety": 9, + "cognitiveLoadEffect": 8, + "total": 64 + }, + "evidence": [ + { + "kind": "audit", + "file": "MFD_WIRING_AUDIT.md", + "lines": "29-43", + "claim": "Audit finding W12 says the Depth Chart starter target is not formation validation.", + "quotedSourceEvidence": "DepthChart.tsx:85-90, 215-221" + }, + { + "kind": "current_source", + "file": "apps/web/src/features/depth-chart/DepthChart.tsx", + "lines": "81-100,210-223", + "claim": "The current readout counts marked starters against position targets and does not inspect package-specific role legality." + }, + { + "kind": "current_source", + "file": "CODEX_GAME_GUIDE.md", + "lines": "3197", + "claim": "The guide warns that GameState.players and Team.roster must stay synchronized when moving or editing players." + } + ], + "assumptions": [ + "This can be a pure read-model first, with no lineup mutation beyond existing depth chart actions.", + "Package definitions should reuse existing positions/schemes constants rather than inventing route-only football rules." + ], + "falsificationEvidence": [ + "A current helper that already validates depth chart against offensive/defensive formation packages and is rendered on Depth Chart.", + "Sim code proving invalid formation starter counts cannot affect game outcomes and the UI count is intentionally sufficient." + ], + "sliceDefinition": { + "playerMomentOrCpuTrigger": "Player edits starters, returners, or game-plan formation before advancing a game week.", + "decisionOrPolicy": "Compute formation validity per selected scheme/package and surface exact missing/overloaded roles without auto-moving players.", + "stateRead": "Read roster, player positions, starter flags, injuries, depth chart assignments, special teams assignments, selected offensive/defensive schemes, and existing position/scheme constants.", + "stateMutation": "None for first read-model slice. Existing setStarter/returner actions remain the only commit paths.", + "deterministicResolutionRules": "Pure deterministic validation from current roster and scheme/package data.", + "immediateReceipt": "Depth Chart shows package readiness, missing role list, and the next safe route/action.", + "laterCallback": "Monday Briefing or Action Center can flag unresolved formation-invalid state before game simulation.", + "saveAndMigrationImpact": "No save/schema impact if pure read-model. Persisting package-specific lineup state would require full save work.", + "existingRouteAndSystemIntegrations": [ + "apps/web/src/features/depth-chart/DepthChart.tsx", + "packages/engine/src/config/positions.ts", + "packages/engine/src/config/schemes.ts", + "apps/web/src/features/monday-briefing/ActionCenter.tsx" + ], + "tests": [ + "focused pure helper tests for valid/missing/extra/injured package roles", + "DepthChart component tests for source copy and missing-role display", + "Monday Briefing test only if unresolved invalid state is surfaced there" + ], + "acceptanceMetrics": [ + "A roster with 22 starters but missing package role is flagged accurately.", + "No roster movement or save mutation occurs from validation render.", + "Existing depth chart starter tests continue to pass." + ], + "rollbackBoundary": "Revert helper/UI/tests. No save or engine state repair required if kept read-only." + } + } + ] +} diff --git a/artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.md b/artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.md new file mode 100644 index 00000000..1c71452a --- /dev/null +++ b/artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.md @@ -0,0 +1,41 @@ +# MFD Council Proposals - 2026-07-08 + +Source brief: `00_MFD_COUNCIL_SHARED_BRIEF.md` + +Schema: `schemas/MFD_COUNCIL_PROPOSAL_SCHEMA.json` + +Structured packet: `artifacts/mfd-council-proposals-2026-07-08/MFD_COUNCIL_PROPOSALS.json` + +## Current Read + +The shared brief is still the right product frame: trust, intelligence, consequence, memory, and clarity. Several of its listed high-confidence gaps are no longer current in this checkout, so this packet does not re-open them: + +- Draft war-room trade acceptance and generated-offer validity are superseded by current `draft-war-room.ts` live-asset validation, draft-order update logic, and focused engine/store tests. +- Combined Backup is now the primary portable backup path; classic `.mfd` is labeled advanced current-save-only recovery. +- Position coaches now have initialize/upgrade actions and season-rollover tenure. +- CPU intent and acquisition market receipts have been substantially improved by Team Needs intent, Team Window, FA/draft/trade market receipts, and strategy-history display. +- G6 route visual proof now covers 81/81 registered routes, not only the older 48-route matrix. + +The remaining "now" work should avoid old redo paths and focus on open, source-confirmed gaps. + +## Ranked Board + +| Rank | Proposal | Disposition | Complexity | Confidence | Score | +| ---: | --- | --- | --- | --- | ---: | +| 1 | Shared Action Receipt Contract | next | M | high | 75 | +| 2 | Trick Play Live-Drive Integration | now | M | high | 71 | +| 3 | Durable Inbox Read Receipts | now | S | high | 68 | +| 4 | G7 Release Certificate | now | M | high | 65 | +| 5 | Formation-Aware Lineup Validation | later | M | medium | 64 | +| 6 | Low-Stakes Press Conference Consequences | next | M | medium | 62 | +| 7 | Long-History Save Schema Hardening | next | L | high | 59 | + +## Best Immediate Slice + +If the next agent is implementing, choose between: + +1. `durable-inbox-read-receipts` for a small, reversible clarity slice with no save/schema work. +2. `trick-play-live-drive-integration` for the strongest gameplay-consequence slice, with sim/perft verification. +3. `g7-release-certificate` if the objective is release trust rather than feature work. + +Do not redo draft war-room trade integrity, combined backup primary path, or position-coach lifecycle unless live source evidence shows those ledgers are stale. diff --git a/schemas/MFD_COUNCIL_PROPOSAL_SCHEMA.json b/schemas/MFD_COUNCIL_PROPOSAL_SCHEMA.json new file mode 100644 index 00000000..2995212e --- /dev/null +++ b/schemas/MFD_COUNCIL_PROPOSAL_SCHEMA.json @@ -0,0 +1,327 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://mfd.local/schemas/MFD_COUNCIL_PROPOSAL_SCHEMA.json", + "title": "MFD Council Proposal Packet", + "type": "object", + "additionalProperties": false, + "required": [ + "packetVersion", + "briefFile", + "generatedAt", + "workingRoot", + "evidenceBasis", + "gapTriage", + "proposals" + ], + "properties": { + "packetVersion": { + "type": "string", + "minLength": 1 + }, + "briefFile": { + "type": "string", + "const": "00_MFD_COUNCIL_SHARED_BRIEF.md" + }, + "generatedAt": { + "type": "string", + "format": "date" + }, + "workingRoot": { + "type": "string", + "minLength": 1 + }, + "evidenceBasis": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/evidenceItem" + } + }, + "gapTriage": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "briefGap", + "currentStatus", + "basis", + "proposalIds" + ], + "properties": { + "briefGap": { + "type": "string", + "minLength": 1 + }, + "currentStatus": { + "type": "string", + "enum": ["open", "partially-mitigated", "superseded", "not-ranked-now"] + }, + "basis": { + "type": "string", + "minLength": 1 + }, + "proposalIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "proposals": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/proposal" + } + } + }, + "$defs": { + "evidenceItem": { + "type": "object", + "additionalProperties": false, + "required": [ + "kind", + "file", + "claim" + ], + "properties": { + "kind": { + "type": "string", + "enum": ["brief", "audit", "current_ledger", "current_source", "current_test", "assumption"] + }, + "file": { + "type": "string", + "minLength": 1 + }, + "lines": { + "type": "string" + }, + "claim": { + "type": "string", + "minLength": 1 + }, + "quotedSourceEvidence": { + "type": "string" + } + } + }, + "score": { + "type": "integer", + "minimum": 0, + "maximum": 10 + }, + "scoring": { + "type": "object", + "additionalProperties": false, + "required": [ + "playerImpact", + "trustImprovement", + "intelligenceExplainability", + "consequenceStrength", + "dynastyMemoryCompounding", + "reuseOfExistingSystems", + "technicalFeasibility", + "deterministicTestability", + "saveMigrationSafety", + "cognitiveLoadEffect", + "total" + ], + "properties": { + "playerImpact": { + "$ref": "#/$defs/score" + }, + "trustImprovement": { + "$ref": "#/$defs/score" + }, + "intelligenceExplainability": { + "$ref": "#/$defs/score" + }, + "consequenceStrength": { + "$ref": "#/$defs/score" + }, + "dynastyMemoryCompounding": { + "$ref": "#/$defs/score" + }, + "reuseOfExistingSystems": { + "$ref": "#/$defs/score" + }, + "technicalFeasibility": { + "$ref": "#/$defs/score" + }, + "deterministicTestability": { + "$ref": "#/$defs/score" + }, + "saveMigrationSafety": { + "$ref": "#/$defs/score" + }, + "cognitiveLoadEffect": { + "$ref": "#/$defs/score" + }, + "total": { + "type": "integer", + "minimum": 0, + "maximum": 100 + } + } + }, + "sliceDefinition": { + "type": "object", + "additionalProperties": false, + "required": [ + "playerMomentOrCpuTrigger", + "decisionOrPolicy", + "stateRead", + "stateMutation", + "deterministicResolutionRules", + "immediateReceipt", + "laterCallback", + "saveAndMigrationImpact", + "existingRouteAndSystemIntegrations", + "tests", + "acceptanceMetrics", + "rollbackBoundary" + ], + "properties": { + "playerMomentOrCpuTrigger": { + "type": "string", + "minLength": 1 + }, + "decisionOrPolicy": { + "type": "string", + "minLength": 1 + }, + "stateRead": { + "type": "string", + "minLength": 1 + }, + "stateMutation": { + "type": "string", + "minLength": 1 + }, + "deterministicResolutionRules": { + "type": "string", + "minLength": 1 + }, + "immediateReceipt": { + "type": "string", + "minLength": 1 + }, + "laterCallback": { + "type": "string", + "minLength": 1 + }, + "saveAndMigrationImpact": { + "type": "string", + "minLength": 1 + }, + "existingRouteAndSystemIntegrations": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "acceptanceMetrics": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "rollbackBoundary": { + "type": "string", + "minLength": 1 + } + } + }, + "proposal": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "title", + "summary", + "briefAlignment", + "complexity", + "confidence", + "recommendedDisposition", + "scoring", + "evidence", + "assumptions", + "falsificationEvidence", + "sliceDefinition" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "title": { + "type": "string", + "minLength": 1 + }, + "summary": { + "type": "string", + "minLength": 1 + }, + "briefAlignment": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["trust", "intelligence", "consequence", "memory", "clarity"] + } + }, + "complexity": { + "type": "string", + "enum": ["S", "M", "L", "XL"] + }, + "confidence": { + "type": "string", + "enum": ["low", "medium", "high"] + }, + "recommendedDisposition": { + "type": "string", + "enum": ["now", "next", "later", "reject"] + }, + "scoring": { + "$ref": "#/$defs/scoring" + }, + "evidence": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/evidenceItem" + } + }, + "assumptions": { + "type": "array", + "items": { + "type": "string" + } + }, + "falsificationEvidence": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "sliceDefinition": { + "$ref": "#/$defs/sliceDefinition" + } + } + } + } +}