Files
forge/claude_artifacts/audit-20260827-180746.md
T

302 lines
23 KiB
Markdown
Raw Normal View History

2026-08-29 11:59:28 +03:00
# 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](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_map`**not 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-await``await-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 | ✅ | **New**`utils/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. |
---
## 12. Recommended next phases (prioritized)
**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 `DonutChart`s. *(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`/`_xlBtn``xl-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.