- Goals & Scope
- Glossary & Key Definitions
- Policy Decisions
- Architecture & Flow
- Data & Storage
- Templates, Shifts & Caps
- Ingestion → Evaluation Pipeline
- Scoring Model (Formulas)
- Mid‑Month Activation & Provisional
- Vape Rules
- Worked Examples
- Non‑Functional Requirements
- API Stubs (Shapes)
- UX Copy & Errors
- Edge Cases & Rules
- Acceptance Criteria
- Analytics (Events)
Table of Contents
1) Goals & Scope
Goals
- Deliver a fair, transparent, auditable IEQ certification with compact storage.
- Support clear roll‑ups from device → asset → facility with minimal ambiguity.
- Make tenant shift edits possible (if allowed) while capping Non‑Active misuse.
Scope
In Scope (Facilities): Retail Stores, Villas, Offices, Schools.
Out of Scope (for now): other facility types (e.g., clinics, hospitals, DCs). Automated HVAC control and occupancy inference.
2) Glossary & Key Definitions
- Asset: A scoring unit under a facility (Location/Space/Subspace).
- Bucket: Fixed window. Hourly (Temp/RH/Odor/Cold/Water) or 15‑min (CO₂; 96/day).
- Within (device): 1 if device’s bucket value is inside the active band at bucket time; otherwise 0.
- Expected (device): Temp/RH/Odor/Cold/Water = D×24, CO₂ = D×96; D = days in the scoring window.
- Actual (device): number of device buckets actually produced in the window.
- Within% (asset/facility): within_bucket_count / Expected × 100.
- Provisional: First 7 scoring days; counts toward math but certificate is hidden.
- Trending: Month‑to‑date indication only; not a certificate.
3) Policy Decisions (Final)
- Roll‑up: Facility score = simple average of its assets’ scores (equal weight).
- Per‑device completeness gates (monthly): Silver ≥ 70%, Gold ≥ 80%, Platinum ≥ 90%.
- Retention: Monthly rollups kept forever; device‑bucket rows purged month close + 7 days.
- Shift Editing: Super Admin may allow tenant edits; daily Non‑Active caps enforced at asset and facility scopes; Cold/Water cannot be Non‑Active.
- Mid‑Month Start: Start after 48h stability → next local midnight; Provisional 7 days; partial‑month denominator from scoring start.
- Vape KPI: Days with ≥1 event during Active hours / days in window < 10%.
- Tie rule: For IAQ/Odor/CO₂ quorum, ≥ 50% within counts as within; equals (ties) are within.
4) Architecture & Flow
Component responsibilities: Ingest → Evaluate → Roll‑up → Publish → Admin.
5) Data & Storage (Lean)
Device×Bucket (upsert) - bucketStartUtc, value_last, within (0/1) - Upsert semantics; last‑write‑wins inside bucket window - No long‑term raw time series Retention - Purge Device×Bucket +7 days after month close - Keep MonthlyRollup indefinitely
6) Templates, Shifts & Caps
Templates
- Threshold bands per parameter family. Versioned with effective_from; prospective only.
- Cold/Water: enforced 24/7 bands; cannot be set to Non‑Active.
Shifts & Tenant Editing
- Policy: allowTenantShiftEdit, dailyMaxNonActiveHours, exemptParameters=["Cold","Water"].
- Tenant edits validated against caps; prospective only; historical results unchanged.
Caps (Daily‑Only)
- Daily max Non‑Active hours per asset and per facility (both enforced, choose stricter); default 8h/day.
- Monthly caps are removed in this version.
7) Ingestion → Evaluation Pipeline
- Normalize timestamps to UTC; track facility TZ for band evaluation and UI.
- Bucketize: Hourly for Temp/RH/Odor/Cold/Water; 15‑min for CO₂.
- Device×Bucket Persist: upsert value_last; compute within against band at bucket local time.
- Quorum (asset per bucket):
- IAQ/Odor: valid if ≥ ceil(n/2) devices report; asset within=1 if ≥ 50% of reporting devices within.
- CO₂: same, per 15‑min bucket.
- Cold/Water: all devices must report; asset within=1 only if all within.
- Roll‑up: compute counters and percentages; facility = average of assets.
8) Scoring Model (Formulas)
Let D = number of days in the scoring window for the month (partial allowed). Expected (per device): - Temp/RH/Odor/Cold/Water: Expected_device = D × 24 - CO₂: Expected_device = D × 96 Actual (per device): Count of device buckets produced. Completeness_device = (Actual / Expected_device) × 100 Asset Level (apply quorum): - Promote device buckets → asset buckets using quorum rules. - Expected_asset equals the device expected for that family. - Actual_asset = count of valid asset buckets (quorum satisfied). - Within%_asset = (within_asset_bucket_count / Expected_asset) × 100 Per‑Device Completeness Gates (month): - If any mapped device for a family has Completeness_device below tier gate (70/80/90), cap that family at Silver(⚠). Facility Level: - For each family, facility Within% = simple average across assets. - Final level uses tier bars: IAQ/Odor (70/80/90), Cold/Water require ≥ 99%. - Vape KPI: days_with_event_in_active / days_in_window < 10%.
9) Mid‑Month Activation & Provisional
- Activation gate: Admin completed + Tenant confirmed install.
- 48h stability: ignore buckets until 48h of healthy ingestion pass.
- Scoring start: next local midnight after stability.
- Provisional: days 1–7 counted but certificate hidden.
- Partial‑month denominator: Expected starts from scoring start (e.g., start on the 22nd of a 30‑day month → D = 9).
10) Vape Rules
- Primary: VapeIndex ≥ 80 for ≥ 10s → 1 event; dead‑time 120s.
- Fallback: TVOC ≥ 800 ppb AND PM1.0 ≥ 75 µg/m³ for ≥ 10s → 1 event; dead‑time 120s.
- KPI: Days with ≥ 1 event during Active hours divided by days in window must be < 10%.
11) Worked Examples (Facility)
11.1 Retail (Activation on 20th; start 22nd → D=9)
Illustrative assets/devices, quorum per hour, Cold 24/7 rule. Example outcomes: IAQ/Odor within% 88–92%; Cold within% 99.2%. Completeness cap: A3 Temp device=75% → Temp capped at Silver(⚠); Facility level Gold.
11.2 Office (Two floors; no Cold/Water)
IAQ within% averages to ~86%; completeness ≥ 90%. Level = Gold.
11.3 Tie Case (2 sensors per asset)
[1,1] → within; [1,0] → within (tie); [0,0] → out. Single reporting within=1 → within; within=0 → out.
12) Non‑Functional Requirements (SLOs)
- Ingestion latency p95 ≤ 10 minutes; roll‑up job p95 < 5 minutes/facility/hour.
- Device‑offline alert: IAQ/Odor > 2h without buckets; CO₂ > 30m.
- Certificate close within 30 minutes after month end.
- Storage budget: only Device×Bucket for current month + purge buffer; MonthlyRollup forever.
13) API Stubs (Shapes)
// Admin Policy (per facility)
{
"allowTenantShiftEdit": true,
"caps": {
"dailyMaxNonActiveHours": 8,
"scope": ["asset","facility"],
"exemptParameters": ["Cold","Water"]
}
}
// Bucket Upsert (internal)
{
"facilityId":"F1","assetId":"A1","deviceId":"D1",
"param":"TEMP","bucketStartUtc":"2025-08-22T10:00:00Z",
"valueLast":24.3,"within":1
}
// Monthly Rollup (denormalized)
{
"scope":"asset","scopeId":"A1","param":"TEMP","month":"2025-08-01",
"expected":216,"actual":210,"withinCount":194,
"completenessPct":97.2,"withinPct":89.8,
"flags":{"gate":"gold"}
}
14) UX Copy & Errors (EN/AR)
- EN: “You can set up to {hours}h of Non‑Active per day. Cold/Water are always Active.”
AR: “يمكنك ضبط ما يصل إلى {hours} ساعة غير نشطة يوميًا. معايير البرودة/المياه فعّالة دائمًا.” - EN: “Change saved. Applies to new buckets only.”
AR: “تم حفظ التغيير. سيتم تطبيقه على الدُفعات الجديدة فقط.” - EN: “Blocked: proposed Non‑Active exceeds the daily cap of {hours}h.”
AR: “مرفوض: الإعداد المقترح لغير النشط يتجاوز الحد اليومي وهو {hours} ساعة.” - EN: “New devices are enrolled for next month’s scoring.”
AR: “سيتم إدراج الأجهزة الجديدة في تقييم الشهر القادم.”
15) Edge Cases & Rules
- New device mid‑month → excluded from current month; starts next month’s cohort.
- Device removal/replacement → replacement starts next month (no mid‑month swap).
- Template change → prospective from effective_from; historical unchanged.
- Shift edit (tenant) → prospective only; must respect daily cap; Cold/Water exempt.
- Late data → accepted while month open; purge after month close +7 days.
- DST/TZ changes → evaluate bands using facility local time; handle 23/25h days.
- Quorum tie → ≥ 50% within counts as within.
- Incomplete devices but quorum OK → family may still be capped at Silver(⚠).
- VapeIndex unavailable → fallback rule (TVOC+PM1.0) applies.
- Cold/Water violations → facility ineligible if Within% < 99% for these families.
16) Acceptance Criteria
- New device Aug 10 → not included in Aug; appears in Sep.
- Template effective Aug 12 → buckets from Aug 12 00:00 local use new band; earlier unchanged.
- Asset with 2 Temp sensors, one within & one out → asset within (≥ 50% rule).
- Any device in Temp family has completeness 75% → Temp capped at Silver(⚠).
- Vape events on 4 days in 30‑day window → 13.3% → Fail the <10% bar.
- Tenant proposes 10h/day Non‑Active with cap 8h → Blocked with explanatory error.
17) Analytics (Events)
ingest.bucket_upsert { facility, asset, device, param, bucketStartUtc, within }
scoring.rollup_update { scope, param, expected, actual, within_count }
policy.shift_edit_attempt { tenant, before, after, allowed, violation }
policy.shift_edit_blocked { capType:"dailyMaxNonActiveHours", capValue:8, proposedValue:… }
cert.trending_computed { level }
cert.month_close_published { month, level, reasons }
alert.device_offline { device, last_seen }
alert.cold_water_breach { asset, bucket }