# Practitioner report — metric reference

_Auto-generated from `report-metrics.js`. Do not edit by hand — regenerate by running `nasalReport.exportMarkdownManual()` in the dashboard console._

## Indices

Three orthogonal clinical questions, each on a 0–100 scale. Higher is better in all cases. Indices are interim definitions during pilot; the version suffix locks the formula for case-study reproducibility.

### Sealing (`sealing_v1`)

**Tier:** interim  
**Question:** How much of the session the mouth was closed. The primary closure measure.

### Sustainment (`sustainment_v1`)

**Tier:** interim  
**Question:** Whether the patient held up across the session or faded. Combines first-vs-second-half delta and block-to-block retention.

### Recovery (`recovery_v1`)

**Tier:** interim  
**Question:** When the patient slipped, how quickly they recovered and what cue was needed. Combines latency, persistence, and gentle-vs-firm ratio.

## Base metrics

| Metric | Label | Units | Tier | Higher better? |
|---|---|---|---|---|
| `sealed_pct` | Sealed % | % | stable | yes |
| `longest_sealed_streak_ms` | Longest sealed streak | ms | stable | yes |
| `events_per_minute` | Events / minute | count/min | stable | no |
| `latency_to_first_event_ms` | Latency to first event | ms | stable | yes |
| `sealed_pct_first_half` | Sealed % (first half) | % | stable | yes |
| `sealed_pct_second_half` | Sealed % (second half) | % | stable | yes |
| `half_delta_pp` | Half delta (2nd − 1st) | pp | stable | yes |
| `block_retention_pp` | Block retention (last − first) | pp | stable | yes |
| `mean_latency_after_gentle_ms` | Mean latency after gentle prompt | ms | stable | no |
| `mean_latency_after_firm_ms` | Mean latency after firm prompt | ms | stable | no |
| `mean_persistence_after_prompt_ms` | Mean persistence after prompt | ms | stable | yes |
| `gentle_prompt_count` | Gentle prompts delivered | count | stable | — |
| `firm_prompt_count` | Firm prompts delivered | count | stable | no |
| `firm_ratio` | Firm ratio | — | stable | no |
| `quality_flag` | Quality flag | — | stable | — |
| `face_missing_ms` | Face missing | ms | stable | no |
| `too_close_ms` | Camera too close | ms | stable | no |
| `effective_duration_ms` | Effective duration | ms | stable | — |

## Base metric definitions

### `sealed_pct` — Sealed %

**Description:** Proportion of session time the mouth was closed below the calibrated jawOpen threshold. The primary closure measure.  
**Formula:** session.score (top-level), equivalent to sum(closed_ms) / sum(all_ms) × 100  
**Source:** top (`score`)  
**Units:** %  
**Tier:** stable

### `longest_sealed_streak_ms` — Longest sealed streak

**Description:** Longest continuous run of sealed mouth during the session. Measures sustained closure capacity.  
**Formula:** max(continuous run of telemetry samples below threshold)  
**Source:** session_metrics (`longest_sealed_streak_ms`)  
**Units:** ms  
**Tier:** stable

### `events_per_minute` — Events / minute

**Description:** Frequency of mouth-open events per minute. Indicates how often closure breaks down.  
**Formula:** open_events_count / (duration_ms / 60000)  
**Source:** session_metrics (`events_per_minute`)  
**Units:** count/min  
**Tier:** stable

### `latency_to_first_event_ms` — Latency to first event

**Description:** Time from session start until the first mouth-open event. Long latency suggests warm-up; short latency suggests low closure stamina from the start.  
**Formula:** time of first telemetry sample with jaw > threshold  
**Source:** session_metrics (`latency_to_first_event_ms`)  
**Units:** ms  
**Tier:** stable

### `sealed_pct_first_half` — Sealed % (first half)

**Description:** Sealed % during the first half of the session. Baseline for fade-detection.  
**Formula:** sum(closed_ms) / sum(all_ms) × 100, restricted to t < duration / 2  
**Source:** session_metrics (`sealed_pct_first_half`)  
**Units:** %  
**Tier:** stable

### `sealed_pct_second_half` — Sealed % (second half)

**Description:** Sealed % during the second half of the session. Compared against first half to detect fatigue or stabilization.  
**Formula:** sum(closed_ms) / sum(all_ms) × 100, restricted to t ≥ duration / 2  
**Source:** session_metrics (`sealed_pct_second_half`)  
**Units:** %  
**Tier:** stable

### `half_delta_pp` — Half delta (2nd − 1st)

**Description:** Percentage-point change in sealed % from first half to second half. Positive = improvement across the session; negative = fatigue.  
**Formula:** sealed_pct_second_half − sealed_pct_first_half  
**Source:** derived  
**Units:** pp  
**Tier:** stable

### `block_retention_pp` — Block retention (last − first)

**Description:** Percentage-point change in sealed % from the first block of the session to the last. Positive = sustaining or improving across blocks; negative = fatiguing under continued or escalating demand.  
**Formula:** last_block.sealed_pct − first_block.sealed_pct (only when blocks ≥ 2)  
**Source:** derived  
**Units:** pp  
**Tier:** stable

### `mean_latency_after_gentle_ms` — Mean latency after gentle prompt

**Description:** Mean time to close the mouth after a gentle prompt was delivered. Lower = more responsive to soft cues.  
**Formula:** mean(prompt_event.latency_to_close_ms) where prompt_event.kind = "gentle"  
**Source:** session_metrics (`mean_latency_after_gentle_ms`)  
**Units:** ms  
**Tier:** stable

### `mean_latency_after_firm_ms` — Mean latency after firm prompt

**Description:** Mean time to close the mouth after a firm prompt was delivered. Lower = more responsive when escalation was needed.  
**Formula:** mean(prompt_event.latency_to_close_ms) where prompt_event.kind = "firm"  
**Source:** session_metrics (`mean_latency_after_firm_ms`)  
**Units:** ms  
**Tier:** stable

### `mean_persistence_after_prompt_ms` — Mean persistence after prompt

**Description:** Mean time the mouth stayed closed after a prompt-driven recovery, before the next event. Higher = recovery sticks.  
**Formula:** mean(prompt_event.persistence_after_ms)  
**Source:** session_metrics (`mean_persistence_after_prompt_ms`)  
**Units:** ms  
**Tier:** stable

### `gentle_prompt_count` — Gentle prompts delivered

**Description:** Count of gentle prompts delivered during the session.  
**Formula:** count(prompt_event where kind = "gentle")  
**Source:** session_metrics (`gentle_prompt_count`)  
**Units:** count  
**Tier:** stable

### `firm_prompt_count` — Firm prompts delivered

**Description:** Count of firm (escalated) prompts delivered during the session. Lower = patient mostly responded to gentle cues.  
**Formula:** count(prompt_event where kind = "firm")  
**Source:** session_metrics (`firm_prompt_count`)  
**Units:** count  
**Tier:** stable

### `firm_ratio` — Firm ratio

**Description:** Proportion of all delivered prompts that were firm rather than gentle. Lower = patient mostly settled with gentle cues; higher = escalation more often needed.  
**Formula:** firm_prompt_count / (gentle_prompt_count + firm_prompt_count)  
**Source:** derived  
**Units:** —  
**Tier:** stable

### `quality_flag` — Quality flag

**Description:** Aggregate quality flag: "good", "framing_issue" (face missing or too close >10% of effective time), or "interrupted" (recovered from a crash/interruption).  
**Formula:** derived from face_missing_ms, too_close_ms, ended_by, recovery_source  
**Source:** activity_metrics (`quality_flag`)  
**Units:** —  
**Tier:** stable

### `face_missing_ms` — Face missing

**Description:** Total time during the session when no face landmarks were detected.  
**Formula:** sum of face-missing intervals  
**Source:** activity_metrics (`face_missing_ms`)  
**Units:** ms  
**Tier:** stable

### `too_close_ms` — Camera too close

**Description:** Total time during the session when the face filled >55% of the camera frame, triggering the distance prompt.  
**Formula:** sum of too-close intervals  
**Source:** activity_metrics (`too_close_ms`)  
**Units:** ms  
**Tier:** stable

### `effective_duration_ms` — Effective duration

**Description:** Wall-clock duration minus backgrounded gaps (>500ms between rAF callbacks). The "real" attention time.  
**Formula:** sum of (now - lastTickT) where (now - lastTickT) ≤ 500ms  
**Source:** activity_metrics (`effective_duration_ms`)  
**Units:** ms  
**Tier:** stable

## Baseline

Baseline is the mean of base metrics and indices across the patient's first **3 baseline-phase video sessions**. This matches the existing phase-advancement rule (`baseline_sessions_required = 3`). Until 3 baseline sessions are complete, the report shows raw values without baseline deltas, with a note: "Baseline in progress (n/3)." After completion, every later session is reported with both raw values and deltas from baseline.

## Scope

The clinical report includes **video-class sessions only**. Game and reader sessions are visible elsewhere in the dashboard but are not folded into Sealing / Sustainment / Recovery. This matches the existing phase-advancement logic ("advancement counts only video-class sessions").


---

## How to read the session report

The practitioner report (`session-report.html`) displays a single session in a printable layout. Open it by clicking **Open report →** at the top of any expanded video session in the dashboard.

### Header strips
Two rows of four cells each. The first identifies the patient, session number, date, and current protocol phase. The second shows wall-clock vs. effective duration, overall sealed %, and modality.

### Indices
Three cards on a 0–100 scale — **Sealing**, **Sustainment**, **Recovery**. Each card shows:
- The current session's index value (large)
- A bar from 0 to 100 with the index's fill in green
- A dashed marker on the bar showing the patient's baseline value (when established)
- The baseline number and the delta from baseline below the bar

If the patient has fewer than 3 baseline-phase video sessions, baseline values and deltas are not shown — the report says "Baseline pending" and only raw values appear.

### Trajectory sparkline
Below the index cards, a small chart shows Sealing across all of the patient's video sessions chronologically. The current session is the larger dot with a dark outline. The dashed horizontal line is the baseline mean.

### Interpretation
A short paragraph block, each line prefixed with `→`. Generated by deterministic rules over the metric values — no AI, no speculation. The rules are defined in `session-report.html` and can be audited there.

Possible content:
- **Sealing** — whether closure improved, declined, or held steady against baseline
- **Sustainment** — whether the patient faded or improved within the session
- **Recovery** — gentle-vs-firm split when prompts were issued; "no prompts" is treated as a positive signal
- **Quality** — flagged when face-missing or too-close intervals exceed 10%

### Within-session shape
Block table + flat metrics table. Block table shows each block's intensity tier, sealed %, event count, and yawn count. Below it, half-split values, longest streak, latency to first event, events per minute.

### Response to prompts
Counts of gentle and firm prompts, firm ratio, and mean latencies/persistences. Skipped entirely when no prompts were issued.

### Quality & integrity
Pills indicating session conditions: face missing duration, too-close duration, effective-vs-wall ratio, recovery from interruption, atypical session end. A "good" pill is shown for clean sessions.

### Footer
Version of the metrics module used to compute this report (e.g. `report-metrics v0.1.0`) and the session ID prefix. Useful when citing in case studies.

---

## Scope reminder

The clinical report covers **video-class sessions only**. Game and reader sessions appear elsewhere in the dashboard but are not folded into Sealing, Sustainment, or Recovery. This matches the existing phase-advancement rule in the database: "advancement counts only video-class sessions."

## Versioning policy

- **Stable** metrics — definitions never change. Examples: `sealed_pct`, `longest_sealed_streak_ms`. Safe to cite by name.
- **Interim** metrics — definitions may revise after pilot data. The three indices are interim during the pilot phase. When a formula changes, a new versioned id is added (`sealing_v2`) rather than the old one being redefined. Historical reports keep their `_v1` values; new reports start producing `_v2` values from a documented date.
- **Experimental** metrics — diagnostic only, not for clinical reading. Hidden from default report view.

## Citing this report

When citing in a case study or paper, include:
1. The metrics module version (footer of any session report, e.g. `report-metrics v0.1.0`)
2. The specific index id used (e.g. `sealing_v1`)
3. The number of baseline sessions used (3 by default)

Example: "Sealing improved from 61.3 to 84.0 (Δ +22.7, sealing_v1, report-metrics v0.1.0, baseline n=3)."
