Files
forge/claude_artifacts/audit-20260827-180746.md
T
Dmytro Tkachenko 28d817ebe9 Init
2026-08-29 11:59:28 +03:00

23 KiB
Raw Blame History

audit — Gap analysis: "Let it Snow" → FORGE (what has NOT been ported)

Date: 2026-08-27 · Author: audit (read-only) · Scope: full parity sweep Checklist source: FORGE-parity-spec.md (29 charts + PM-Insights + boards + data contract) Compared against: live FORGE code (server/*.ts, client/src/**, extension/*) and the initial app source (/Users/dmytrotkachenko/WebstormProjects/Let It Snow/Let it Snow/*.js, verified by grep — files are huge).

Legend: done · 🟡 partial · missing. Value: MH must-have · NTH nice-to-have · LOW.


0. Executive summary

FORGE has delivered a solid read-only spine: full data ingest (966 tickets + finance + jira + config), a server analytics engine, the Overall tab (~9 of 11 charts), the Active tab (5 of 6), the 6 SLA heatmaps (charts 2429), PM Insights (alert groups + waiting-PO + per-PM roll-up), a 6-column board with list view + filtered Excel export, and new RBAC/Users/Tokens admin (beyond the original).

What is not ported is roughly half of the analytics surface and nearly all of the operator tooling:

  • PMs-KPI tab is only the 6 heatmaps — charts 1823 (workload, engagement + dual calendar heatmaps, missing-cost, awaiting-PO money, brand/market sub-cards) are absent, and the heatmaps have no period/year granularity and no editable norms.
  • Board is missing the 7th synthetic column, 13 of 16 card fields, all 14 sorts, all 6 group-by permutations, and ~9 of 11 filters (region, staleness, jira, requester, custom-label…).
  • Whole features missing: the standalone Jira board, the Size CALC estimator, the finance view, and the entire options/config editor surface (brand colours, norms, thresholds, card-field toggles, colleagues/region, currency, custom labels).
  • Sync is shallow: active-RITM only, 9 fields, no SCTASK, no Jira, no closed, no finance, no statusDurations collectors.
  • Overall/Active charts lack the YoY pies (#5/#6), CGO overlay, #/% toggle, Jira status durations (#17), and time-in-status min/median/max (#15).

Per the spec, AI Advisor / at-risk, weather are intentionally out of scope (confirmed below).


1. Overall analytics tab — client/src/pages/Overview/index.tsx + server/analytics.ts:getOverview

# Item Status Note / where it lives
1 Opened per month GroupedBars — multi-year grouped, bars/line toggle, YoY badge.
2 Closed per month GroupedBars, same.
3 Revenue per month (cost/tickets, ±YoY) GroupedBars + Cost/Tickets toggle in Overview; GBP via fixed FX.
4 Lifetime-at-close histogram + median/avg/closed tiles BarList 6 buckets + median tile. avgDays/closed computed server-side.
5 Opened YoY pie (fair/full) No opened-YoY donut. Would live in Overview as a DonutChart; server has openedByMonth but no fair-window (same-day-of-year) series. MH
6 Closed YoY pie (fair/full) Same — no closed-YoY donut / fair-vs-full comparison. MH
7 By business unit (per-year, brand drill) 🟡 Flat BarList (byBusinessUnit), no per-year split, no brand drill-down. server/analytics.ts:169. NTH
8 By requester (bars + YoY, market drill) 🟡 ExpandBarList drills requester→brand (spec wants →market), no YoY. analytics.ts:170. NTH
9 Ticket-share donut (#/%, top 18 + Others) 🟡 DonutChart on byRequesterShare (full 966 dist — the REDO fix). No #/% toggle, no top-18+Others rollup. NTH
10 By brand (per-year segments, market drill, YoY) 🟡 ExpandBarList brand→market counts only. No per-year segments, no YoY, no size/status segmentation. NTH
11 By market 🟡 Flat BarList, top-20. No brand drill / segments. LOW
Toggle: bars/line GroupedBars per-chart.
Toggle: ±YoY 🟡 Shown as a computed badge, not a user toggle; only on the month GroupedBars. LOW
Toggle: cost/tickets 🟡 Only on the Revenue chart; original applies it across money charts. LOW
Toggle: #/% No percentage mode anywhere. NTH
Growth badges 🟡 Only the month-total YoY badge; no per-series/per-requester growth. LOW
CGO overlay Original overlays a "CGO" series per group (dashboard.js CgoBars/CGO_REQUESTER, toggle). Not in FORGE — needs a CGO flag on tickets + overlay in GroupedBars. NTH

Bottom line: the month/revenue/lifetime core is faithful; the two YoY pies are missing outright, and the by-dimension charts are flat counts without the per-year segmentation, drill, and YoY the original had.


2. Active tab — client/src/pages/Active/index.tsx (client-computed from active tickets)

# Item Status Note / where it lives
12 Open-not-closed by month TrendChart area, openedByMonth.
13 By status (kanban cats) BarList over BOARD_COLUMNS, state colours.
14 Age histogram (0-50…200+ d) BarList, 5 buckets.
15 Time-in-status min/median/max 🟡 Only median days-in-state per column (Active/index.tsx:62). No min/max grouped mini-bars. NTH
16 By brand, status-segmented (market drill) 🟡 Flat BarList of active-by-brand. No status segmentation, no market drill. NTH
17 Jira status durations (workflow order) Requires statusDurations from jira_status_mapnot ingested (only status/statusChangedAt/key land in jira JSONB; see scripts/dump-to-archives.mjs:36). No chart. MH for Jira ops.

3. PMs-KPI tab — client/src/pages/SlaKpi/index.tsx + server/analytics.ts:getSlaHeatmaps

The whole tab is currently only the 6 SLA heatmaps. The original "PMs KPI" tab was 12 items (1829).

3a. The 6 SLA heatmaps (2429) — present but reduced

# Item Status Note
24 On-Time Delivery heatmap otd metric, PM×size, on-time% vs otd norms.
25 Avg days to close heatmap avgclose vs avgdays.
26 Time to assign PM heatmap assign = max(0, fulfillment→firstAssigned) vs asla.
27 Time to send preview heatmap preview = toDoAt→inUatAt vs psla.
28 Avg TTFR heatmap ttfr = ttfrMinutes/1440 vs ttfr norms (hrs).
29 Avg PM response heatmap cresp = clientRespMinutes/1440 vs cresp norms.
Current + previous year side by side getSlaHeatmaps computes one all-time grid; original renders cur-yr + prev-yr heatmaps. MH
Period granularity (year / Q1-Q4 / month) No period control at all. MH
Editable norms panels Norms are read-only from app_config/config.json; original has inline norm editors (buildNormsPanel etc.) that persist. Needs a write endpoint + UI. MH
Hidden PMs from pm_kpi_settings getSlaHeatmaps:223 has a stubbed hidden = new Set() — never populated. NTH

3b. Charts 1823 — entirely missing

# Item Status Note / where it'd live
18 Workload by month (revenue prev/cur yr, per-PM) New GroupedBars+per-PM series; new server agg (closed cost by PM×month). MH
19 PM Engagement (backlog/Assist✓/Jira✓/Stale) + dual daily-activity calendar heatmaps Needs CalendarHeatmap component (GitHub-style) + activity/jira-movement ingest per day. Original: EngagementKpi() with PM-row accordion. MH
20 Missing final cost (ranked PM bars + drill) AssigneeBars + list; server: closed rows where finalCost null, grouped by PM. Partial data exists. MH (margin-leak)
21 Awaiting PO (ranked, Cost / Avg-d / Max-d, sortable) 🟡 Insights has a count + L1/L2/L3 + revenue, but no per-PM Cost/Avg-days/Max-days sortable table. MH
22 No-cost by brand / market Two BucketBars. NTH
23 Awaiting-PO by brand / market Two BucketBars. NTH

Bottom line: PMs-KPI is the largest single gap — 6 of 12 charts absent, and the 6 present ones lack year/period slicing and the editable norms that make them operational.


4. Board — client/src/pages/Board/index.tsx, utils/board.ts, components/Board/BoardCard

Item Status Note / where it lives
7-state columns 🟡 6 columns built (unassigned, open, hold, wip, replied, awaiting; board.ts:18). 7th synthetic Closed / Awaiting-PO column is missing (original CLOSED / AWAITING PO SYNTHETIC overlay). MH
Customer-replied pseudo-status boardColumn/isAwaitingAgency — WIP + last touch ∉ colleague roster. Faithful.
Column SLA subtitles + per-column count col.slaVerb(thr) + count badge.
Group-by (6 permutations of WIP·Replied·Awaiting) Column order is fixed; original has 6 orderings (wip-agency-awaitawait-agency-wip). NTH
16 toggleable card fields (card_fields) BoardCard is a fixed layout showing ~8 fields (number, jira status, link, shortDesc, brand·market, assignee, in-state days, lifetime, last activity). None toggleable. Missing: description, raisedBy, dueDate, stateChanged, comments, createJira, jiraBar, teamsLink. Original fields: cf-stateBadge/shortDesc/description/assignee/assignmentGroup/brandMarket/raisedBy/lifetime/dueDate/stateChanged/lastActivity/comments/createJira/showJiraStatus/showJiraBar/teamsChannelLink. NTH
Custom labels registry Original custom_label system (per-ticket labels + filter). Absent. NTH
Staleness / PO / jira-stuck / missing-cost badges BoardCard has no badges beyond the jira-status chip. NTH
Sort (14 options) No sort control. Original: default, number, status-age-asc/desc, lifetime-asc/desc, jira-assignee, jira-status, brand, cost-asc/desc, due-asc/desc, analyzed. NTH
Filters: search / brand / market / assignee Present in the board header.
Filters: region EU/LATAM sentinels latamAssignees is ingested (config.json) but the assignee dropdown has no EU/LATAM region grouping. NTH
Filters: requester, custom-label, jira-assignee, jira-status None. NTH
Filters: staleness (stale/updated/inactive/PO/jira-stuck/missing-cost), hide-empty, hide-cost Original group-by chips + status-label filters. Absent. NTH
List view TicketTable toggle (fixed columns, not the same field set).
Card click behaviour 🟡 Opens an in-app TicketDetailModal (+ a SNOW link icon). Original opens ServiceNow directly. Acceptable divergence.
Filtered Excel export Newutils/excel.utils.ts, 17 cols, honours active filters.
Azure DevOps focus board Out of scope per spec (§0). LOW

5. PM Insights — client/src/pages/Insights/index.tsx + server/insights.ts

Item Status Note / where it lives
Alert groups (Unassigned, Open/Assigned, On-Hold, WIP-stalled, WIP-no-Jira, Customer-replied, Awaiting) insights.ts 9 groups; support rules jiraBreached/clientOwed/isAwaitingAgency ported.
Informational groups (Lifetime ≥90d, Inactive requester) Present, excluded from Total Alerts.
∑ Total Alerts + Revenue-at-Risk Distinct-count reconciled (verifier-log).
Waiting-PO count + revenue + L1/L2/L3 🟡 Counts + revenue present; no per-level (L1/L2/L3) drill-down lists. insights.ts:140. NTH
PM-KPI table (# Tickets · $ Revenue · ⚠ Rev-at-Risk) 🟡 Table present; not sortable, and hidden-PMs from pm_kpi_settings not honoured. NTH
Excel export of the insights subtree (PM→Size→Ticket) Original xl-export.js writes PM/Size/Ticket-grouped sheets. FORGE has board export only. MH
Region / PO / people filters Original insights has EU/LATAM region + PO + people filters. Absent. NTH
Per-PM size stats No size breakdown inside the PM roll-up. NTH
"Missing tickets" overlay Original reconciliation overlay (tickets present in one source, absent in another). Absent. LOW

6. Jira board page — MISSING ENTIRELY (MH for Jira ops, else NTH)

Original jira-board.html + jira-board.js (2378 LOC): columns from jira_board_state, cards, RITM↔Jira link (jira-ritm-link.js), quick-filters, settings panel (board URL, scan controls, PO-cache clear), toasts, scan countdown. FORGE has no route, no page, no jira_board_state ingest (dump-to-archives.mjs only reads jira_status_map). Would live as client/src/pages/JiraBoard/ + a jira_board archive/table + a /api/jira/board endpoint.


7. Sync / extension — extension/background.js (+ popup.js, options.js)

FORGE's collector is active-RITM only, shallow. Original background.js (5219 LOC) is far deeper.

Aspect Status Note
SNOW active RITM scan pageCollector pages sc_req_item, assignment_groupLIKEMarketing Web Presence.
Field depth 🟡 Only 9 fields (number, short_desc, state, assigned_to, group, opened_at, due_date, updated_by, updated_on). No brand/market/BU/requester/cost/activity timeline/state-change history. Rich fields survive only from the seed, kept alive by COALESCE upsert. MH
SCTASK support RITM-only. Original scans SCTASK too (background.js sctask/SCTASK). NTH
Closed-ticket collector No closed scan; closed data is seed-only, goes stale. MH
Jira collector (+ statusDurations) No Jira scanning; jira_status_map/statusDurations/jira_board_state never refreshed. MH
Finance xlsx scan Original scans the SharePoint finance workbook (finance_xlsx); FORGE finance is a one-shot seed from the dump. MH
Popup = RITM list + Jira tab 🟡 FORGE popup is sync-only (server URL + token + "Sync now"). Original popup is a RITM browser + Jira tab. LOW (by design — web app replaces it)

8. Finance — server/db.ts:applyFinance + scripts/dump-to-archives.mjs:106

Item Status Note
Finance ingest (cost / currency / PO / invoiced) finance.json (860 rows) → fills finalCost/poNumber/invoiced, recomputes size.
Milestone / PM / state columns 🟡 Parsed into finance.json (milestone, pm, state) but never surfaced — DB has no columns for them and no UI reads them. NTH
Finance view (invoicing / milestone / filters) No finance page. PO/invoiced only appear as an Excel column + waiting-PO signal. NTH

9. Size CALC tool — MISSING ENTIRELY (NTH)

Original size-calc.js (692 LOC): a labor→size estimator (inputs by role × hours × rate, config lis_size_calc_cfg, MATCH/composite algorithms). FORGE has a server sizeOf(cost) helper (db.ts:156) that assigns sizes to tickets, but no interactive estimator tool/page. Would live as client/src/pages/SizeCalc/ + a lis_size_calc_cfg config key.


10. Config / options editors — MISSING ENTIRELY (mixed)

FORGE loads config read-only from config.json/app_config (seeded once). The original options.html/ options.js (629 LOC) is a rich settings surface with no FORGE equivalent (the FORGE Admin page is Users+Tokens only). Missing editors, each GET /api/config reads but nothing writes:

Setting (original id) Status Value
Brand colours editor (brand_colors, add/reset) NTH — 45 brands seeded, not editable in-app.
SLA norms editors (otd/avgdays/asla/psla/ttfr/cresp) MH — required by §3a editable-norms gap.
Insights thresholds editor (insights_thresholds) NTH
Size cost-thresholds editor (otd_cost_thresh) NTH
Card-field toggles (cf-*, 16) + mobile fields (mf-*) NTH — pairs with the board card-fields gap.
Colleague names (colleagueNames) / LATAM assignees (latamAssignees) NTH — seeded, not editable.
Display currency (displayCurrency) LOW — fixed GBP.
Custom labels (custom-labels, clear-all) NTH
SNOW field colours (color-*, 6) LOW — SNOW-page injection only, N/A to web app.
Card layout breakpoints (card_layout) LOW
Scan intervals / enables (analyticsIntervalHours, snRescanInterval, dashboardEnabled, jiraNotificationsEnabled, soundEnabled, analyzeButtonsEnabled) LOW — extension/SNOW-page behaviour.
Teams bot (teamsBotEnabled, chat names) LOW — out of scope.

11. Other

Item Status Note
AI Advisor / at-risk (advisor-*.js, advisor-server/) (intended) Excluded per spec §1 ("user excluded AI"). Confirmed out of scope — do not port.
Teams integration (teamsChannelLink, teams bot) Out of scope-ish. LOW
Figma links (figma-links.js, 244 LOC) Optional per spec. LOW
Weather — (no gap) Excluded per spec and dead in the original — manifest keeps open-meteo/nominatim host perms but no weather JS remains. Nothing to port.
Docs page (docs.html/docs.js) In-app help. LOW
Archive import / export 🟡 scripts/dump-to-archives.mjs + npm run reseed is a manual CLI path; no in-app import/export UI. LOW
SCTASK support See §7 — RITM-only. NTH
Extension popup RITM browser 🟡 See §7. LOW
New in FORGE (beyond original): RBAC (4 roles), Users/Tokens Admin page, session auth, Postgres, server analytics engine Net-additive; no parity gap.
Legacy/unrouted FORGE pages: pages/Dashboard, pages/Stats Dashboard is unrouted (superseded by Board); Stats is a thin /stats count page. Housekeeping, not a gap.

Phase A — PMs-KPI completion (highest value, biggest gap).

  1. Add year/period granularity + current-vs-previous-year to the 6 SLA heatmaps (getSlaHeatmaps + period control). (MH)
  2. Editable norms — a POST /api/config/norms write path + inline norm panels (also unblocks the options editor). (MH)
  3. Charts 18 (workload/month per-PM), 20 (missing final cost), 21 (awaiting-PO Cost/Avg/Max sortable). (MH)
  4. Chart 19 engagement + CalendarHeatmap (needs per-day activity/jira-movement ingest). (MH, larger)
  5. Charts 22/23 brand-market sub-cards. (NTH)

Phase B — Overall/Active fidelity. 6. YoY pies #5/#6 (fair-window + full) as DonutCharts. (MH) 7. #17 Jira status durations (requires statusDurations ingest — see Phase D). (MH) 8. Per-year segmentation + drill + YoY on by-BU/by-brand/by-market/by-requester; #15 min/median/max; #16 status-segmented brand; #/% toggle; CGO overlay. (NTH)

Phase C — Board operability. 9. Sort (14) + group-by (6) controls. (NTH) 10. Toggleable card fields (16) + badges (stale/PO/jira-stuck/missing-cost). (NTH) 11. Filters: region EU/LATAM, requester, jira-status/assignee, staleness, custom labels. (NTH) 12. 7th synthetic Closed/Awaiting-PO column. (MH)

Phase D — Deeper sync (unblocks a lot above). 13. Extend the collector: richer RITM fields + closed scan + Jira (statusDurations, jira_board_state) + finance scan + SCTASK. (MH — currently rich data is seed-only and goes stale.)

Phase E — Insights depth. 14. Excel export of the insights subtree (PM→Size→Ticket); waiting-PO L1/L2/L3 drill; sortable PM table + hidden-PM support; region/PO/people filters. (MH for export, else NTH.)

Phase F — Standalone features. 15. Jira board page (needs Phase D jira ingest). (MH for Jira ops.) 16. Options/config editor page (brand colours, thresholds, card fields, colleagues/region, currency, custom labels). (NTH.) 17. Size CALC estimator; finance view (milestone/invoicing — data already parsed). (NTH.)

Do not port: AI Advisor/at-risk, weather (excluded); Teams/Figma/ADO board/docs page (low/optional).


13. Addendum — refinements from the source inventory (corroborated)

A parallel deep read of the initial app's source confirmed the above and adds:

  • Board columns: the original renders 5 live columns (Open/Assigned, On Hold, WIP, Customer-replied, Awaiting) + the synthetic Closed/Awaiting-PO column; Unassigned is folded into Open/Assigned. FORGE builds 6 (splits Unassigned out) and still lacks the synthetic 7th — the §4 "7th column MH" gap stands.
  • Per-chart Excel export: in the original every analytics chart carries its own Excel button (_xlData/_xlBtnxl-export.js). FORGE has no per-chart export — only the board export. Add as an NTH gap across Overall/Active/PMs-KPI. Insights groups also each export (see §5, MH).
  • Missing-tickets overlay lives on the 3 SLA/OTD charts (OTD/AvgDays/Assign/Preview), grouping the timestamp-less tickets by current Jira status (reachedUat / beforeUat / noJira) — reclassify the §5 "missing-tickets overlay" as part of the PMs-KPI SLA charts (still ).
  • Collector scope: the original SN scan handles RITM + SCTASK + INC (not just RITM), computes per-ticket statusDurations from status-change history, and runs a Jira scan as a tail after every SN scan. FORGE's collector does none of this (§7) — add INC alongside the SCTASK gap.
  • Threshold value drift: FORGE config.json has waitingPo3: 18 and customerReplied: 1, vs the original defaults waitingPo3: 21 / customerReplied: 3. Minor, but reconcile when the norms/thresholds editor lands (§10). LOW
  • Engagement KPI reads the live board (not ticketsMeta), with twin Assist|Jira calendar heatmaps and a ~14-column sortable drill-down — confirms the §3b #19 scope (MH, larger build).
  • Size CALC uses 4 currencies (GBP/EUR/MXN + MXN·Brazil uplift ×1.111), roles DEV/CM/QA + auto-PM, PM-budget tiering (pmThreshold/pmHi/pmLo) — confirms §9.
  • AI Advisor is a full masked-digest + Node-bridge-to-Claude-CLI subsystem (advisor-*.js + advisor-server/) with the At-risk breach engine — correctly excluded, do not port.
  • Archive import/export exists as in-app ⬇ Archive / ⬇ Active buttons in the original (FORGE has only the dump-to-archives.mjs CLI) — the §11 🟡 stands.

None of these change the phase priorities; they sharpen scope for Phases A, D, and E.

Next

principal — sequence Phase A (PMs-KPI) as the next build; confirm which board/insights sub-items are in v1 scope.