Phase 2: Accuracy & Analysis Depth — cast timeline, healer metrics, game-data regen, regression net - #15
Merged
Conversation
- Add scripts/record-wcl-fixtures.mjs (recorder in token-audit.mjs family): mints OAuth token, POSTs GraphQL, resolves healer source at run time - Wire npm run record-fixtures in package.json - Record six fixtures from public report ZjKgNYxVcAqR8pGJ fight 23: demo-player-dps, demo-player-healer (scoped + un-scoped healing), demo-raid-overview, demo-raid-combatant-info, demo-raid-death-events (fight has zero deaths), demo-timeline-casts (paginated cast events) - lib/__fixtures__/README.md documents provenance and resolves RESEARCH.md assumptions A1-A5 against the real API, including the A3 correction: the sourceID-scoped Healing table DOES carry per-ability overheal; what it lacks is activeTime, which appears only on the un-scoped row - No credential material committed; pulled Vercel env file discarded Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
… contracts - lib/__fixtures__/fixtures.test.ts: shape guard over all six fixtures, asserting exact field names README.md recorded (not RESEARCH.md guesses) - lib/wcl-queries.ts: TIMELINE_CASTS_QUERY + TIMELINE_CASTS_PAGE_QUERY using the real events(dataType: Casts) argument names and cast-event field names; healingByPlayer added to both healing queries so player-level overheal and activeTime are available with no second round trip - lib/wcl-types.ts: Cast Timeline and Healer Metrics type contracts (WCLCastEvent, TimelineRow, CastTimelineResult, HealerTableRow, HealerMetricsComputed, HealerComparison) plus optional AnalysisResult.healer and TopPlayerFullData.healerRow - lib/constants.ts: explicit RATE_LIMITS.timeline bucket (tighter than analyze — one request can page cast-events up to 20 times) - lib/utils.ts: shared formatFightTime, reproducing RaidOverview's private helper so the death timeline and cast timeline can't drift apart - lib/analysis-engine.ts: export JUNK_SPELL_IDS/isJunkSpell so the cast timeline reuses the one junk-spell exclusion set npx vitest run lib/__fixtures__/fixtures.test.ts passes (6/6); this test is a shape guard over data Task 1 already recorded, so it never had a failing RED state to catch — documented as an intentional exception, not a gate violation. npx tsc --noEmit and npm run lint are clean on every file this task touches. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
- lib/timeline-engine.ts: pure buildCastTimeline() transform (cast events + casts table + actors -> ordered TimelineRow[]), mirroring buildRaidOverview's shape. Filters begincast probes (only completed "cast" events survive), junk spells via isJunkSpell, and unlisted ability ids; resolves ranked display names and Self/undefined targets (WCL's -1 "no target" sentinel). - lib/timeline-engine.test.ts: 8 assertions driven by the real recorded demo-timeline-casts.json fixture plus synthetic junk/self/no-target events. - app/api/timeline/route.ts: POST /api/timeline — validated, rate-limited (timeline bucket), single-flight cached via cachedApiHandler, paginates cast-events up to MAX_TIMELINE_PAGES=20 (denial-of-service bound), sets truncated on cap. - app/analyze/[reportCode]/hooks/useTimeline.ts: lazy-fetch hook mirroring useCLA's shape; auto-runs only when the timeline sub-tab is active (no WCL fetch until the tab opens). - app/components/CastTimeline.tsx: vertical cast log — Card shell, loading skeletons, destructive Alert on error, empty-state copy, 40px rows with timestamp/icon/SpellLink name/target columns, lazy-loading icon with graceful onError fallback to a placeholder square. - app/components/AnalysisView.tsx / AnalyzeClient.tsx: seventh "timeline" ptab value, TabsTrigger/TabsContent pair, reportCode/fightId/sourceId wired down from AnalyzeClient's existing selection state. Deviation note: buildRankedNames is duplicated locally in the pure engine rather than imported from app/components/SpellLink.tsx (a "use client" module) — importing a client-boundary export into this server-consumed lib file risks resolving to a client-reference proxy in Next.js's RSC build rather than the real function, so the small ranking algorithm is re-implemented verbatim instead.
…aths - useTimeline.ts: two posthog.capture call sites — timeline_viewed (report_code, fight_id, source_id, cast_count, truncated) on success, timeline_error (report_code, fight_id, source_id, error) on a failed response or a thrown request. Outcome captured across both branches so the analytics call stays a single site per outcome rather than one per branch, per docs/OPS-01-SHIP-GATE.md step 4's one-event-per-interaction rule. - CastTimeline.tsx: row list wrapped in a max-h-[400px] overflow-y-auto (overflow-x-hidden) container so a long log scrolls inside the card instead of stretching the page; only the ability-name column flexes. Error/loading/content states were already mutually exclusive from Task 1. - AnalysisView.tsx's Timeline TabsTrigger and TabsList classes are unchanged — confirmed no disabled attribute, no spinner, trigger stays selectable during a fetch or an error.
…ed generator - Resolves each era's build from https://wago.tools/api/builds by matching product + version prefix, never a fresh guess or a shared latest build (RESEARCH.md Pitfall 5) — Classic+TBC (wow_anniversary), WotLK and Cata (both wow_classic, disambiguated by version prefix). - Derives ENCHANT_NAME / GEM_NAME / GEM_STAT maps from SpellItemEnchantment, SpellName, SpellEffect, ItemSparse and GemProperties CSV exports, joined and combined into the same "label (raw stat text)" style the pre-existing hand-authored lib/cla-constants.ts used. - Per-era, per-map row-count floors are enforced before any file is written; a zero-row map is always fatal. Cross-era ID collisions are enumerated, never resolved silently. - lib/generated/game-data-overrides.json is the single hand-authored data file (3 sourced entries: enchant id 88, whose client data is empty in all three eras, and enchant ids 2343/2566, whose only linked spells in client data are internal QA/test spells) plus the STAT_TYPE_BAD_FOR_ROLES policy; lib/generated/game-data-overrides.ts is its typed accessor. - Ran `npm run regen-game-data` for real against live wago.tools and committed the three generated era modules (game-data.classic-tbc.ts, game-data.wotlk.ts, game-data.cata.ts) — resolved builds 2.5.6.69546 / 3.4.5.63697 / 4.4.2.60895, all matching the currently pinned builds. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…name pair - lib/generated/game-data.test.ts composes the three era modules (a small local helper, not imported from the app — that composition module is 02-06's artifact) and merges the overrides on top, asserting all eight required behaviours plus the override/registry invariant. - Deviation: Tests 1-6 assert directly against ENCHANT_NAME_CLASSIC_TBC / GEM_NAME_CLASSIC_TBC / GEM_STAT_CLASSIC_TBC rather than the later-wins composed map. The exhaustive generated data (thousands of rows per era, not a hand-curated subset) surfaces real cross-era ID reuse the curated map never hit — e.g. enchant 3003 is "Glyph of Ferocity" in the TBC client and "Arcanum of Ferocity" in WotLK's; gem 32196 is "Runed Crimson Spinel" in TBC/WotLK and "Brilliant Crimson Spinel" in Cata. A blind later-wins merge would silently clobber the pinned TBC fact with an unrelated later-era item sharing the same numeric id — exactly the cross-era misattribution this phase exists to prevent. Each era module stays independently correct; only the merge was unsound for asserting a specific era's historical fact, so the test now asserts against the correct era directly. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…alers - lib/healer-metrics.ts: computeHealerMetrics/averageTopHealerMetrics, the single source of truth for healer throughput/efficiency/uptime (D-08), extracted verbatim from the raid-overview engine's prior inline formulas - lib/wcl-fetchers.ts: fetchTopPlayers reads the un-scoped healingByPlayer row and stores it as TopPlayerFullData.healerRow on the healer path - app/api/analyze/route.ts: healer branch reads its own healingByPlayer row, builds a HealerComparison via the shared helper against averaged top-healer metrics, without touching the hps ranking metric or partition scoping - lib/analysis-engine.ts: buildAnalysisResult accepts healerComparison and assigns it to AnalysisResult.healer only for the healer role - app/components/DpsComparison.tsx: renders Overheal/Uptime rows beneath the Top bar for healers, colored via the shared overhealColor/activityColor helpers (added to lib/constants.ts, alongside percentileColor/percentileBg — pulled forward from Task 2 since this task's own tsc/lint/token-audit gate requires the import to resolve; Task 2 reuses them unchanged) - lib/wcl-types.ts: HealerComparison gains hasHealing so the player's own percentage can render an em dash instead of a misleading measured zero Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…lper - lib/raid-overview-engine.ts: buildRaidOverview calls computeHealerMetrics for both the per-player throughput/activity branch and the healerMetrics list, replacing the inlined overheal/uptime formulas; a healer with no healing row is now emitted with zeroed metrics (totalHealing: 0) instead of being dropped from the list, so a healer dead at the pull still appears - app/components/RaidOverview.tsx: imports overhealColor/activityColor from lib/constants and formatFightTime from lib/utils, deleting the three module-private duplicates (pure import swap, byte-identical logic); HealerPanel renders an em dash instead of a measured-looking 0% overheal when totalHealing is 0 - lib/healer-metrics.test.ts: cross-surface parity assertion (buildRaidOverview and computeHealerMetrics agree field-for-field for the same row and fight duration) plus a zero-healing-row assertion, closing D-08's guarantee from the raid-overview side Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…ast rhythm - buildCastTimeline now emits idle and death TimelineRow kinds, merged chronologically alongside cast rows - idleThresholdMs = max(2000ms, 3x this player's own median inter-cast gap) -- relative to the player, never a per-class constant (D-03) - at most one death row per fight, positioned by fightTimeMs rather than appended at the end - castCount continues to count cast rows only, so idle/death bands never inflate the truncation notice or timeline_viewed's cast_count - app/api/timeline/route.ts passes the already-queried deathEvents through to the engine; truncated continues to be set only on the page-cap exit path - 9 new timeline-engine.test.ts behaviours (fixture-derived threshold math plus 8 synthetic-sequence cases); the tracer's abilityName assertion is scoped to kind === "cast" since idle/death rows are structural markers with no ability Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…for the cast timeline
- Per-ability filter chips (flex flex-wrap gap-1.5, All reset chip) built
from the engine's abilityCounts; nothing hidden by default (D-04); each
chip's count always reads the unfiltered total. Neutral bg-surface-3
selected state, no gold accent -- chips are multi-select
- Hand-rolled fixed-40px-row windowing (no virtualisation dependency, per
RESEARCH.md) with a 10-row overscan buffer above/below the viewport so
scrolling never blanks; recomputes on scroll, resize (ResizeObserver)
and filter change
- Idle-gap divider (dashed border-status-warn, Lucide Pause) and death
band (badge-bad, Lucide Skull) row renderers for the new TimelineRow
kinds from 02-05's engine change
- Truncation notice row ("Log truncated -- showing first {N} casts")
renders inside the scroll container whenever the route reports
truncated:true, never hidden by the ability filter
- useTimeline.ts exports captureFilterUsed, firing timeline_filter_used
(report_code, fight_id, ability_count, hidden_count) once per chip
interaction -- the hook's third posthog.capture call site
- AnalysisView.tsx threads captureFilterUsed into CastTimeline as
onFilterToggle (required wiring beyond the plan's stated
files_modified -- the callback lives in the hook AnalysisView already
calls; see SUMMARY Deviations)
- Filter-selection and result-change state resets use React's documented
"adjust state during render" pattern rather than useEffect, since an
effect that only calls setState synchronously trips
react-hooks/set-state-in-effect (newly enforced by this repo's eslint
config); the DOM scrollTop reset stays in a real effect since it has no
setState call
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
- Move all 178 CONSUMABLE_DB curation rows (category/isSuboptimal/ betterAlternative) into lib/generated/game-data-overrides.json as CONSUMABLE_CURATION, dropping the name field which is now generated. - Add a consumable-name verification pass to scripts/regen-game-data.mjs: resolves each curated id's display name directly from each era's SpellName table, records cross-era corroboration in `verifiedIn`, and falls back to an explicit consumableNames override (with a source note) for the 38 ids no regenerated era can resolve (4 pre-Anniversary Classic ids, 34 MoP-range ids — MoP is deliberately out of scope per D-10). - Emit lib/generated/game-data.consumables.ts (178 rows) from a live wago.tools run; add a row-count floor at the curation registry size so a partial resolution is a hard failure, not a silent gap. - Consumable-name cross-era collisions resolve Classic+TBC-first (not newest-era-wins): live data surfaced 3 ids (28497, 33721, 22756) where a genuinely TBC consumable's spell id is reused by an unrelated WotLK/Cata spell — the same cross-era ID-reuse phenomenon 02-03 found in the enchant/gem data. Newest-wins would have silently mislabeled a real TBC elixir. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…lision precedence
- Add lib/generated/index.ts as the single composition point for
ENCHANT_NAME_DB, GEM_NAME_DB, GEM_STAT_DB, CONSUMABLE_DB and UNVERIFIED_IDS.
- Reduce lib/cla-constants.ts to a thin re-export: no inline id-map literals
remain; type declarations, CLASS_BUFF_FAMILIES and EXPECTED_TALENT_POINTS
stay unchanged, and every existing import site works untouched.
- GEM_STAT_DB's name is derived from the composed GEM_NAME_DB rather than
stored twice, making cla-constants.test.ts's name-sync invariant structural.
Deviation from the plan's literal "later era wins" composition order: eras
compose Classic+TBC-first (earliest-resolved-era wins a collision), not
newest-era-wins. Live wago.tools data proved a flat later-wins merge is NOT
behaviour-preserving at the exhaustive-data scale — enchant id 3003, gem id
32196 and enchant id 2667 all collide across eras with an unrelated item
(RESEARCH.md Pitfall 5), and a later-era-wins merge silently replaces the
real TBC fact lib/cla-constants.test.ts pins with an unrelated item that
happens to reuse the same numeric id. TBC-first precedence is the only
ordering that keeps the test suite's pinned facts correct, and matches
ParseForge's product focus (Classic/TBC analyzer). Every collision is still
enumerated, never silently applied.
A second, larger deviation surfaced verifying this task's own hard gate
(cla-constants.test.ts passing unmodified): consumable buff-aura names
resolved directly from SpellName.Name_lang diverge from the item's
curated display name far more often than expected — 92 of 178 curated
consumables (52%) needed a consumableNames override beyond the 38 already
added in Task 1, split across three patterns: (1) 26 ids where the client
omits the "Elixir of "/"Flask of " item-type prefix from the buff-aura
label; (2) 54 ids, mostly food buffs, where distinct items collapse to an
identical generic buff-aura name ("Well Fed") or the buff-effect name
diverges from the item's flavor name; (3) 2 Cata weapon-enhancement ids
(96264, 96294) whose resolved value looks unrelated to the curated item and
could not be corroborated against any other era — flagged for the
docs/GAME-DATA-AUDIT.md human-check rather than trusted. All 130 total
consumable overrides preserve the pre-regeneration, previously-verified
name; every CONSUMABLE_DB value now matches its pre-cutover name exactly,
confirmed by a full 178-row parity check.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…r review - Add the --markdown emitter's remaining sections to scripts/regen-game-data.mjs's report builder (done in the prior commit): a generated-file "do not edit" header, an era-builds table with a previous-build column, a row-count-vs- floor table, the consumable-name pass summary, an unverified-overrides section listing every UNVERIFIED_IDS entry with its source note, and a changed-values-since-previous-run section backed by a node_modules/.cache/regen-game-data/previous-run.json sidecar. - Re-run `npm run regen-game-data -- --markdown docs/GAME-DATA-AUDIT.md` for real against live wago.tools. Builds unchanged since 02-03/PR #11 (2.5.6.69546 / 3.4.5.63697 / 4.4.2.60895); all floors met; 178/178 consumable names resolved; changed-values section empty (no upstream data drift since this session's earlier runs). 809 enchant + 295 gem cross-era collisions enumerated, consistent with 02-03's count. - Reviewed the unverified-overrides list (133 entries: 3 enchant, 130 consumable) and confirmed every one is either genuinely unresolvable via this pipeline's per-id lookups or a deliberate accuracy-preserving override — flagged ids 96264/96294 (Cata weapon enhancements) remain the one pair whose resolved value could not be corroborated and is preserved from pre-regeneration curation pending a future item-name-based derivation pass. - Confirmed idempotent regeneration (byte-identical apart from the generation timestamp) and re-ran the full verification gate: 101/101 tests, tsc clean, touched files lint clean. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
- 9 behaviours for generateSuggestions' new healer-only rules (D-07): overheal, uptime, effective-HPS-gap firing/non-firing, DPS active-time rule suppression for healers (and preservation for DPS), description content, zero-sample-count gating, and priority sort order - Add "healing" to the ImprovementSuggestion category union so the tests typecheck against the category the new rules will emit - Verified RED: 6/9 new tests fail against pre-change generateSuggestions (via temporary stash of the implementation), confirming they exercise real behaviour, not vacuous assertions Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…ime rule
- generateSuggestions() gains an optional sixth `healer?: HealerComparison`
parameter, passed through from buildAnalysisResult's existing
healerComparison param — the five original parameters and their order
are unchanged
- The DPS-shaped active-time (ABC) rule is now gated on
`playerRole !== "healer"`; damage dealers still receive it unchanged
- Three new healer rules (D-07), each gated on the role, the presence of
a healer comparison, and a nonzero top-healer sample count so an empty
comparison population produces no relative advice (T-02-21):
- High overheal: fires when the player's overheal exceeds the top
healers' by a named ratio (HEALER_OVERHEAL_RATIO = 1.3) AND clears an
absolute percentage-point floor, so a near-zero top value can't flag
any nonzero overheal as a fault
- Low healing uptime: fires when uptime falls below the top healers'
by a named ratio (HEALER_UPTIME_RATIO = 0.9)
- HPS gap despite efficient healing: fires only when neither rule above
fired, using the existing dps.gapToTop/playerDps/topDps figures
(T-02-22 — never recomputes a gap the comparison card already shows)
- All multipliers are named module-level constants with rationale
comments, not inline literals, and are expected to be retuned against
real logs
- Every fired healer description quotes both the player's own value and
the top value for that metric
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
- ComparisonSummary.tsx: add `healing: "Healing"` to categoryLabels so the new D-07 suggestion cards get a proper badge in the UI and the Discord copy, instead of falling back to the raw lowercase category key - usePlayerAnalysis.ts: analysis_complete now carries player_role, and, when the payload includes a healer comparison, overheal_percent, activity_percent, top_overheal_percent and suggestion_count — makes healer adoption of the new surface measurable at the OPS-01 gate - Consolidated the two analysis_error call sites (HTTP-error branch and exception branch) into a single captureAnalysisError() closure so this file keeps exactly two posthog.capture call sites (one success, one error), matching the ship gate's one-call-site-per-interaction grep — the file previously had three literal call sites for two interactions Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…overview-engine - lib/cla-engine.test.ts: missing-enchant detection by slot name, flask presence (synthetic — no fixture records a buff-uptime table), gem-vs-role mismatch, class-buff availability, one full buildCLAResult snapshot — all driven by the recorded demo-raid-overview/demo-raid-combatant-info fixtures - lib/raid-overview-engine.test.ts: healer metrics agreement with computeHealerMetrics (D-08), role-then-throughput sort order, death timeline ordering/fight-relative timing/non-player exclusion/death-table fallback (synthetic events over real player ids — fight 23 has zero recorded deaths), one full buildRaidOverview snapshot - Committed snapshots under lib/__snapshots__/ Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…ry and error classification - lib/wcl-client.test.ts drives the real wclQuery through vi.stubGlobal fetch sequences (no MSW, no new dependency) plus a vi.mock of ./kv-cache so no Redis env or network access is required - Covers: token mint + GraphQL success, 401 refresh-and-retry (asserted via fetch call count), 429 exhausting all retries -> rate_limited, 500 exhausting all retries -> upstream, AbortError on every attempt -> timeout, GraphQL error text classification (not_found/private/upstream), the userMessage boundary (present, no raw-detail leak), and missing WCL_CLIENT_ID/WCL_CLIENT_SECRET rejecting with both var names - Retry/timeout scenarios run under fake timers (vi.resetModules per test isolates wcl-client's module-scope token/query caches); full suite stays well under 30s - lib/wcl-client.ts untouched; no new test dependency introduced Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…OPS-01 gate - Local-gate step now states out loud that a non-zero npm test exit blocks a production deploy — no override, no "known failure" allowance - New subsection names the seven test files that make up this phase's reach (cla-engine, raid-overview-engine, wcl-client, timeline-engine, healer-metrics, generated game-data guard, fixtures shape guard) alongside the pre-existing suite; states explicitly that no coverage-percentage threshold is enforced and why - Records that committed lib/__snapshots__/ snapshots are updated only deliberately, with the diff reviewed — a reflexively-refreshed snapshot converts the regression net into a green light that certifies nothing Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…O invariants, PostHog grep, game-data review - Local gate: tsc clean, 135/135 tests, theme-parity PASS, token-audit 0 gate-relevant findings; unscoped npm run lint exits 1 but is confined to untracked .codex/ scaffolding (scoped run over app/lib/components/scripts is clean apart from one pre-existing documented warning) - npm run seo-invariants exits 0 for all 11 routes; /analyze/[reportCode]/page.tsx confirmed unchanged this phase (last touched a4aaf75, pre-Phase-1) with a param-free canonical - timeline_viewed, timeline_error, timeline_filter_used and analysis_complete each verified at exactly one posthog.capture call site; documents a shell-quoting bug in the plan's own verify script that undercounts single-file matches - docs/GAME-DATA-AUDIT.md read: 133 unverified overrides, 0 changed values since the previous run — closes ACC-01's D-12 review-artifact requirement Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Developer decision: preview-first (preview deploy, real-browser sweep, then prod) - Push + PR already done by orchestrator: origin/main fast-forwarded to 55d2010, growth/phase-2-accuracy-depth pushed and PR #15 opened - Task 3 in this dispatch runs the PREVIEW half only Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…ted checks - vercel whoami precondition confirmed (alexandermayes) - vercel deploy --scope loot-list-plus --yes (no --prod) — READY, https://parseforge-7yjzs8yw5-loot-list-plus.vercel.app - Automated route/SEO checks against the preview blocked by Vercel team SSO (expected, same as Phase 1's preview) — recorded, not silently skipped - PostHog/Search Console/production deploy deferred to post-approval half Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…sign-off Records the developer's preview sweep approval (11 routes, both themes, Timeline 375px pass, real-gear game-data names, healer row), the production deployment facts (dpl_5bwk1fJJNuZXkoC5poPGFZQpGy6c, aliased parseforge.gg), and post-deploy evidence gathered by this dispatch: seo-invariants re-run clean against production, an 11-route + sitemap/robots 200 sweep, and a live POST /api/timeline proving the Timeline path works end-to-end against production Redis and WCL credentials (castCount/idleThresholdMs match the 02-05 calibration exactly). PostHog and Search Console checks are recorded honestly as no-data — this dispatch has no PostHog/gscServer MCP tool available, mirroring 01-09's identical GSC-tool-unavailability note — and carried forward to the next gate rather than fabricated or dropped. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
Phase 2 shipped to production (dpl_5bwk1fJJNuZXkoC5poPGFZQpGy6c, aliased parseforge.gg) via a preview-first deploy the developer approved twice. OPS-01 gate closed with a dated Phase 2 sign-off; PostHog/GSC post-deploy checks recorded honestly as no-data (MCP tools unavailable to this dispatch) and carried forward. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs
…ext, defer Phase 2 security review
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2 — Accuracy & Analysis Depth (requirements ACC-01..ACC-04, OPS-01). Nine GSD plans, all executed sequentially on
mainwith atomic commits; planning artifacts under.planning/phases/02-accuracy-analysis-depth/.lib/timeline-engine.ts, paginated/rate-limited/cached/api/timeline, lazyuseTimelinehook,CastTimeline.tsxwith idle-gap rows (max(2000ms, 3×median inter-cast gap)), death marker, per-ability filter chips, windowed rendering and a truncation notice. PostHog:timeline_viewed,timeline_error,timeline_filter_used(one call site each).lib/healer-metrics.tsis the single source of truth for effective HPS / overheal % / uptime on both the player comparison card and the raid Healer Breakdown (D-08 parity test). Three healer suggestion rules thresholded against top healers; DPS-shaped rules no longer reach healers.npm run regen-game-datagenerator with per-era build pinning and row-count floors; three generated era modules + one sourced overrides file;lib/cla-constants.tsis now a thin re-export. Era precedence is Classic/TBC-first (later-era-wins corrupted pinned pairs because client builds reuse IDs across eras). Review artifact:docs/GAME-DATA-AUDIT.md(133 explicit overrides, 0 changed values).lib/__fixtures__/(npm run record-fixtures) drivefixtures.test.ts,timeline-engine.test.ts,healer-metrics.test.ts,analysis-engine.test.ts,cla-engine.test.ts,raid-overview-engine.test.ts,wcl-client.test.ts,game-data.test.ts. Suite: 14 files / 135 tests, ~1 s.docs/OPS-01-SHIP-GATE.mdnow states a red suite blocks a deploy.Notable data-driven deviations are documented in each plan's SUMMARY (02-06 precedence + override count; 02-02
begincast/-1target handling; 02-01 corrected RESEARCH assumption A3).Test plan
npx tsc --noEmitcleannpm test— 14 files / 135 tests passingnpm run theme-parity,npm run token-audit— 0 gate-relevant findingsnpm run seo-invariants— 11 routes, analyze canonical still param-freeeslint app lib components scriptsclean (unscoped run only reports the untracked local.codex/dir)🤖 Generated with Claude Code
https://claude.ai/code/session_01FeRkPoQDHb1UFVM3zrRsjs