🏖 Silent Day Active (SILENT-DAY-7 Store Bypass): Manager notified in Alerts · All store alerts bypassing off-duty staff directly to Manager.
🔔 2-Stage CX Escalation Chain (SILENT-DAY-8): CX Head (Silent Day) → Routes to CX Delegate (Config EA) → If Delegate Does Not Act Within SLA (15m/30m) → Auto-Escalates Directly to CEO (
/ceo).← Back to Oven Command
CONTRACT-T1-10-NEW-PRODUCT-LAUNCH-MODULE.md
# G2 3-Legged Design Contract: New Product Launch (NPL) Engine (`T1-10` / `EPIC-NEW-PRODUCT-LAUNCH-ENGINE`)
- **Epic ID:** `T1-10` / `EPIC-NEW-PRODUCT-LAUNCH-ENGINE`
- **G1 Intake:** [`INTAKE-T1-10-NEW-PRODUCT-LAUNCH.md`](file:///Users/srinivas/workspaces/harleys/runtime/agy-eval/intake-master/OUT/INTAKE-T1-10-NEW-PRODUCT-LAUNCH.md)
- **Feature Flags (seeded `off`):** `npl_launch_engine_live` (`authority = "admin"`), `npl_kiosk_cert_live` (`authority = "admin"`)
- **Proposed Disjoint Write Paths:** `app/npl.py`, `app/npl_views.py`, `app/npl_scorecard.py`, `db/013_new_product_launch.sql`, `tests/unit/test_t1_10_npl.py` *(migration `013` assumes `T1-9` holds `012`; Eng to confirm at build kickoff. Zero overlap with `T1-8`/`T1-9` paths.)*
- **Status:** **G2 DESIGN READY FOR OWNER READOUT — not yet build-signed.** The three legs below are a single-author draft; Product / UI-UX / Eng review per the Oven 3-legged gate is still required before build.
---
## Leg 1 — PRODUCT
### 1.1 Roles & Access
| Role | Can do |
|---|---|
| **CEO/CXO / Super Delegate** | Create idea note; taste-gate sign-off; see full dashboard incl. margin; record Kill/Scale/Iterate decision |
| **Launch Lead** (new persona tag on an existing user, not a new role system) | Own the launch; advance stages; set flag scope; receive escalations |
| **Head Chef / R&D** | Stage 1 recipe + sensory scorecards |
| **Finance / Category** | Stage 2 COGS/pricing/SKU gate |
| **CK Head** | Stage 2 scheduling, Stage 3 dispatch |
| **Compliance (`T1-8`)** | Allergen/FSSAI declaration |
| **Area Manager** | Receives nudge escalation; sees outlets in their area |
| **Outlet Manager / Shift Lead** | Cold-chain receipt, crew certification verification, launch-day tasks (own outlet only) |
| **Crew (Barista/Pastry/FOH)** | Kiosk certification + display check; sees **no** cost/margin/ranking |
### 1.2 Stage State Machine
`DRAFT → S0_IDEA → S1_RND → S2_COMMERCIAL → S3_PILOT → S4_ENABLEMENT → S5_LIVE → S6_REVIEW → {PROMOTED | EXTENDED | REFORMULATE | SUNSET}`; any state may go to `ON_HOLD` or `CANCELLED` (reason required). Forward transitions require the stage gate row `status = 'PASSED'`. Backward = `RETURNED` with reason (never silent).
Strategic tiers drive defaults: `Flagship` (chain-wide target, 14d review), `Seasonal LTO` (end date mandatory, auto-Sunset prompt at end date), `Regional Pilot` (single city scope).
### 1.3 Stage Exit Criteria (machine-checked where possible)
| Stage | Gate checks (all required) |
|---|---|
| S0 | title, tier, target launch date, target margin %, positioning text, launch lead set |
| S1 | ≥ 1 sensory scorecard; `taste_signoff_by` = CXO; shelf-life + hold-time set; `chilled_min_c/max_c` if chilled; **`T1-8` allergen/FSSAI declaration ref present**; `recipe_version` frozen |
| S2 | `food_cost_pct <= food_cost_gate_pct`; `sku_code` set; `pos_mapping_status ∈ {MAPPED, MANUAL_STUB}`; packaging sign-off; CK slot booked |
| S3 | flag scope set; ≥ 1 pilot outlet received batch **and** cold-chain check `PASS` (reading within range) |
| S4 | per outlet in scope: required-role certification coverage ≥ threshold; Outlet Manager verified |
| S5 | launch-day task completion tracked (informational gate; stage auto-closes after day 3) |
| S6 | day-14 scorecard generated; human decision recorded |
### 1.4 Dignity / Privacy Rules
- No cross-staff certification leaderboard. Readiness is shown **per outlet** only; a person's cert status is visible to them and their managers up the chain.
- Margin, COGS, food-cost % never sent to Kiosk or `/manager` payloads (server-side omission, tested).
- Customer pulse stores no name/phone.
### 1.5 Kill / Scale / Iterate Scorecard (recommendation only)
Inputs (per launch, outlets `ACTIVE_SELLING`): units/outlet/day, attach rate (if available), sold-out hour, wastage %, pulse positive %, crew pulse positive %. Thresholds live in `hop.npl_scorecard_config` (Super Admin editable; **seeded conservative placeholders, to be calibrated after launch #1 and #2**). Output enum: `PROMOTE_TO_CORE | EXTEND_LTO | REFORMULATE | SUNSET | INSUFFICIENT_DATA`. If < 50% of in-scope outlets have ≥ 7 days of data, output is `INSUFFICIENT_DATA` — never a guess. Decision is recorded by CEO/CXO with a free-text reason.
---
## Leg 2 — UI / UX
### 2.1 Routes
| Route | Surface | Behaviour |
|---|---|---|
| `/admin/launches` | Admin list | Cards/table: launch name, tier, current stage chip, target date, funnel mini-bar, risk flag (any overdue gate). Filter by tier/stage/city. |
| `/admin/launches/new` | Idea-note drop | Fields: title, tier, target date, positioning, target margin %, attachments (photo/sketch), **voice note** (reuses `T2-8` recorder; transcript editable; text fallback). Creates launch in `S0_IDEA`. |
| `/admin/launches/{id}` | Launch Command View | Tabs: **Pipeline** (7-stage rail with gate checklists), **Recipe & Costing** (restricted), **Rollout** (outlet table + flag scope), **Enablement**, **Launch Day**, **Adoption** (dashboard), **Decision**. |
| `/manager` — **Launch Readiness card** | Outlet Manager (own outlet) | Shows each active launch: stock received? cold-chain check, crew certified `x/y`, display photo status, today's launch tasks. Cert verification queue lives here (**Card 3** item). **No money figures.** |
| `/tasks` — **Kiosk Launch Certification & Display Check** | Crew | (a) 30s bilingual card (`EN \| HI`, large icons + audio via `T2-8`); (b) "Taste, Plate & Pitch": take first-display photo → pick/enter 3 pitch bullets → 3-question storage/shelf-life quiz (pictorial, tap answers); (c) submit → status `PENDING_VERIFY`. |
### 2.2 UX Rules
- Kiosk flow ≤ 4 screens, ≤ 2 min total; every screen has audio replay; quiz allows retry, failure routes to manager coaching, **not** a punitive mark.
- Stage rail shows *why* a gate is blocked (plain-language list of missing items).
- Dashboard data-source chip on every metric: `POS`, `MANUAL`, `UNAVAILABLE`. Unavailable metrics render a dash with reason, never zero.
- Hindi strings stored alongside English in `hop.npl_launches.card_i18n` (JSON); missing Hindi → card blocked from push (no silent English-only to Kiosk) `[DESIGN CHOICE]`.
### 2.3 Dashboard Layout (Adoption tab)
1. **Enablement Funnel** (4 horizontal stages with counts + %).
2. **First-7-Day strip:** units/outlet (sparkline), attach rate, sold-out hour distribution, wastage %.
3. **Voice of Ground & Customer:** latest pulse items, reaction-code tallies, playable voice notes.
4. **Decision Gate panel:** day counter, scorecard result, recommendation, decision form.
---
## Leg 3 — ENGINEERING
### 3.1 Data Model (`db/013_new_product_launch.sql`, schema `hop`)
```sql
CREATE TABLE hop.npl_launches (
launch_id BIGSERIAL PRIMARY KEY,
title TEXT NOT NULL,
tier TEXT NOT NULL CHECK (tier IN ('FLAGSHIP','SEASONAL_LTO','REGIONAL_PILOT')),
stage TEXT NOT NULL DEFAULT 'DRAFT', -- state machine in 1.2
idea_note_text TEXT,
idea_media_ref JSONB, -- photo/sketch/voice refs
positioning TEXT,
target_margin_pct NUMERIC(5,2), -- RESTRICTED
food_cost_pct NUMERIC(5,2), -- RESTRICTED
food_cost_gate_pct NUMERIC(5,2),
target_launch_date DATE NOT NULL,
lto_end_date DATE, -- required when tier = SEASONAL_LTO
sku_code TEXT,
pos_mapping_status TEXT DEFAULT 'UNMAPPED' CHECK (pos_mapping_status IN ('UNMAPPED','MAPPED','MANUAL_STUB')),
recipe_version INT,
recipe_frozen_at TIMESTAMPTZ,
shelf_life_hours INT, hold_time_hours INT,
storage_class TEXT CHECK (storage_class IN ('AMBIENT','CHILLED')),
chilled_min_c NUMERIC(4,1) DEFAULT 2.0, chilled_max_c NUMERIC(4,1) DEFAULT 6.0,
compliance_decl_ref TEXT, -- link to T1-8 record
card_i18n JSONB, -- {en:{...}, hi:{...}}
flag_scope TEXT NOT NULL DEFAULT 'PILOT' CHECK (flag_scope IN ('PILOT','CLUSTER','CHAIN')),
launch_lead_id BIGINT NOT NULL,
created_by BIGINT NOT NULL,
decision TEXT CHECK (decision IN ('PROMOTED','EXTENDED','REFORMULATE','SUNSET')),
decision_by BIGINT, decision_reason TEXT, decided_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now(),
CHECK (tier <> 'SEASONAL_LTO' OR lto_end_date IS NOT NULL)
);
CREATE TABLE hop.npl_stage_gates (
gate_id BIGSERIAL PRIMARY KEY,
launch_id BIGINT NOT NULL REFERENCES hop.npl_launches,
stage TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'OPEN' CHECK (status IN ('OPEN','PASSED','RETURNED','WAIVED')),
checks JSONB NOT NULL, -- [{key,label,ok,evidence_ref}]
signed_by BIGINT, signed_at TIMESTAMPTZ, return_reason TEXT,
waived_by BIGINT, waive_reason TEXT, -- WAIVED only by Super Admin; never for allergen check
UNIQUE (launch_id, stage)
);
CREATE TABLE hop.npl_outlet_readiness (
launch_id BIGINT NOT NULL REFERENCES hop.npl_launches,
outlet_id BIGINT NOT NULL,
scope_wave TEXT NOT NULL CHECK (scope_wave IN ('PILOT','CLUSTER','CHAIN')),
go_live_date DATE,
batch_received_at TIMESTAMPTZ,
coldchain_reading_c NUMERIC(4,1), coldchain_result TEXT CHECK (coldchain_result IN ('PASS','FAIL')),
coldchain_by BIGINT,
crew_required INT DEFAULT 0, crew_certified INT DEFAULT 0,
display_photo_ref TEXT, display_posted_at TIMESTAMPTZ, display_verified_by BIGINT,
active_selling_at TIMESTAMPTZ,
state TEXT NOT NULL DEFAULT 'PLANNED'
CHECK (state IN ('PLANNED','STOCKED','CERTIFIED','DISPLAY_VERIFIED','ACTIVE_SELLING','BLOCKED')),
PRIMARY KEY (launch_id, outlet_id)
);
CREATE TABLE hop.npl_crew_certifications (
cert_id BIGSERIAL PRIMARY KEY,
launch_id BIGINT NOT NULL REFERENCES hop.npl_launches,
outlet_id BIGINT NOT NULL,
person_id BIGINT NOT NULL,
role_tag TEXT NOT NULL CHECK (role_tag IN ('BARISTA','PASTRY','FOH')),
display_photo_ref TEXT, pitch_bullets JSONB, -- exactly 3 strings
quiz_score INT, quiz_attempts INT DEFAULT 0,
status TEXT NOT NULL DEFAULT 'ASSIGNED'
CHECK (status IN ('ASSIGNED','PENDING_VERIFY','CERTIFIED','COACHING','RETURNED')),
verified_by BIGINT, verified_at TIMESTAMPTZ,
CHECK (verified_by IS NULL OR verified_by <> person_id), -- no self-certification
UNIQUE (launch_id, person_id)
);
CREATE TABLE hop.npl_cx_pulse (
pulse_id BIGSERIAL PRIMARY KEY,
launch_id BIGINT NOT NULL REFERENCES hop.npl_launches,
outlet_id BIGINT NOT NULL,
source TEXT NOT NULL CHECK (source IN ('CUSTOMER','CREW')),
reaction_code TEXT, -- LOVED/OK/TOO_SWEET/PRICE/... (config list)
voice_ref TEXT, transcript TEXT, -- optional, <=15s customer / <=60s crew
captured_at TIMESTAMPTZ DEFAULT now() -- no person identifiers for CUSTOMER
);
CREATE TABLE hop.npl_daily_metrics ( -- feed target for manual or ODS adapter
launch_id BIGINT, outlet_id BIGINT, biz_date DATE,
units_sold INT, units_wasted INT, sold_out_at TIME,
attach_count INT, -- NULL = unavailable
source TEXT NOT NULL CHECK (source IN ('MANUAL','ODS')),
PRIMARY KEY (launch_id, outlet_id, biz_date)
);
CREATE TABLE hop.npl_scorecard_config (key TEXT PRIMARY KEY, value NUMERIC, updated_by BIGINT, updated_at TIMESTAMPTZ DEFAULT now());
```
Indexes: `(launch_id)` on all child tables; `(outlet_id, state)` on readiness; `(launch_id, captured_at DESC)` on pulse. Row-level access enforced in the service layer; RESTRICTED columns excluded from any query used by `/manager` or `/tasks` (tested).
### 3.2 Service Interfaces (`app/npl.py`)
- `advance_stage(launch_id, actor)` — recomputes gate `checks`; refuses unless all OK; writes audit row.
- `set_flag_scope(launch_id, scope, actor)` — see 3.4 (Flags).
- `record_coldchain(launch_id, outlet_id, reading_c, actor)` — result derived server-side from `chilled_min_c/max_c`; FAIL sets outlet `BLOCKED` and raises a nudge task.
- `submit_certification(...)` / `verify_certification(cert_id, actor)` — enforces `actor != person`, actor must be Outlet Manager or Shift Lead of that outlet.
- `recompute_outlet_state(launch_id, outlet_id)` — pure function from readiness facts → state; idempotent.
- `SalesFeed` adapter protocol: `ManualFeed` (reads `npl_daily_metrics`, from `21:30` log) now; `OdsFeed` stub raises `NotImplemented` until `T3-0` is built.
- `scorecard(launch_id)` → enum + per-metric breakdown + data-source map.
### 3.3 Feature Flags (`T2-6b` plane)
| Flag | Authority | Default | Effect when `off` |
|---|---|---|---|
| `npl_launch_engine_live` | `admin` | `off` | `/admin/launches*` return 404; no NPL jobs/nudges scheduled; `/manager` card hidden |
| `npl_kiosk_cert_live` | `admin` | `off` | `/tasks` certification & display-check hidden; no launch-day tasks injected; safe to enable per-outlet via the plane's outlet scoping |
Enable order: engine first, then kiosk. Kiosk flag requires engine flag on (enforced at read time). Both honour the plane's **Safe Mode Kill Switch**. Per-launch rollout scope (`PILOT → CLUSTER → CHAIN`) is **data in `flag_scope`**, applied by writing outlet-scoped overrides through the plane's API; NPL never edits flag tables directly. Pilot seed: `outlet_nan`. *(Eng to confirm plane exposes an outlet-scoped override API; if not, this is a dependency gap, not something NPL should work around.)*
### 3.4 Integration Hooks (exact)
| System | Hook | Detail |
|---|---|---|
| **HOP Tasks / Shift Spine** | `npl.inject_launch_day_tasks(launch_id, outlet_id, date)` | On go-live date, creates 3 tasks in the outlet's shift spine, idempotent on `(launch, outlet, date, slot)`: **Display & Hero Photo `08:30`**, **Freshness Check `14:00`** (hold-time/temp), **Sold-Out/Wastage/Reaction Log `21:30`** (writes `npl_daily_metrics` `source=MANUAL` + pulse). Also injects Stage-4 cert task on Kiosk `/tasks` at enablement start. Uses the standard task-create API; outlet timezone respected. |
| **Accountability & Nudge (`T1-0b/T1-7`)** | Rules registered as *config rows* in the nudge console, not code | Ladder `T+0, +5m, +15m, +30m` reused. Rule `npl_cert_incomplete`: due `go_live − 24h`; chain Outlet Manager → Area Manager → Launch Lead → CXO Console. Rule `npl_display_missing`: due `09:00` launch day, same chain. Rule `npl_coldchain_fail`: immediate to Outlet Manager + CK Head. Messages bilingual; quiet-hours rules (`DF-3`) honoured. Nudge completion callbacks update readiness. |
| **Compliance (`T1-8`)** | `compliance.get_declaration(ref)` | S1 gate requires an allergen + FSSAI declaration reference; hard block, not waivable. If `T1-8` not deployed: manual upload placeholder with `compliance_decl_ref = 'MANUAL:<file>'`, flagged on dashboard. |
| **Feature Flags (`T2-6b`)** | `flags.set_outlet_scope(flag, outlet_ids, state)` | See 3.3. |
| **Success/CX Telemetry (`T2-6`)** | Emit counts-only events: `npl.stage_passed`, `npl.outlet_state_changed`, `npl.cert_verified`, `npl.display_posted`, `npl.pulse_captured`; contribute an "NPL Launch" card to the per-outlet Pilot Success card. | Counts only — no free text/PII in telemetry (matches `EN-01`). |
| **Central ODS (`T3-0`)** | `SalesFeed` adapter | Deferred; manual feed until built. |
| **Vaani (`T2-8`)** | Card renderer + recorder | Soft dependency; text fallback if `voice_guide_live` off. |
### 3.5 Scheduled Jobs
- `npl_gate_watch` (every 15 min): recompute outlet states, fire overdue gate nudges.
- `npl_scorecard_day14` (daily 06:00): for launches at day 14 since first `ACTIVE_SELLING`, compute scorecard and notify CEO/CXO.
- `npl_lto_end_watch` (daily): LTO within 7 days of end → prompt Decision Gate.
### 3.6 Audit
Every stage advance, return, waive, cert verification, flag-scope change, and decision writes an immutable audit row (actor, before/after, timestamp).
### 3.7 Acceptance Tests (`tests/unit/test_t1_10_npl.py`)
1. Cannot advance S1→S2 without allergen/FSSAI ref; waive attempt on that check is rejected even for Super Admin.
2. `SEASONAL_LTO` without `lto_end_date` rejected (DB CHECK).
3. Cold-chain reading `7.2 °C` ⇒ `FAIL`, outlet `BLOCKED`, nudge raised; `5.0 °C` ⇒ `PASS`.
4. Self-certification rejected at DB and service layer.
5. `/manager` and `/tasks` JSON never contain `target_margin_pct`, `food_cost_pct`, or money fields.
6. `recompute_outlet_state` idempotent; state moves STOCKED→CERTIFIED→DISPLAY_VERIFIED→ACTIVE_SELLING only on facts.
7. Launch-day task injection idempotent across re-runs/restarts.
8. Nudge rules `npl_cert_incomplete` (T-24h) and `npl_display_missing` (09:00) escalate through the 4 rungs and the stated chain.
9. Scorecard returns `INSUFFICIENT_DATA` when < 50% of outlets have 7 days; attach rate `UNAVAILABLE` renders a dash, not 0.
10. With both flags `off`: all NPL routes 404/hidden, no jobs run. Kiosk flag on while engine off ⇒ hidden.
11. Missing Hindi card text blocks Kiosk push.
12. No cross-person cert ranking endpoint exists; per-person status visible only to self + managers up chain.
### 3.8 Delivery Slices (suggested)
- **Slice A:** schema + stages S0–S2 + admin routes + flags.
- **Slice B:** S3 rollout, readiness, cold-chain, flag-scope hook.
- **Slice C:** Kiosk certification + `/manager` card + launch-day task injection + nudge rules.
- **Slice D:** metrics feed (manual), dashboard, scorecard, decision gate.
- **Later:** ODS adapter after `T3-0`.
---
## Open Items Carried to Owner Readout
1. Accept **manual-count** sales feed until Central ODS exists? (default in this contract: yes)
2. Is **customer-spoken** voice capture wanted, or reaction-code taps only? (default: taps; voice optional behind config)
3. Initial scorecard thresholds (placeholders until launch #1/#2 calibrate).
4. Crew-coverage threshold for "enabled" (default 100% of rostered crew for next 3 shifts).
5. Confirm `T2-6b` supports outlet-scoped overrides via API.
6. Launch Lead as persona tag on `T2-7` multi-role model — confirm.

OneOps