# User needs time-series (saved plan)

**Canonical copy in repo:** this file — version-controlled alongside the codebase.

**Cursor plan ID:** `d146a6e5-8958-4045-b429-0d947f36f341` (duplicate lives under `.cursor/plans/User needs time-series-d146a6e5.plan.md` if present).

**Status:** Backlog — not implemented. Sync substantive edits between this file and the Cursor plan when work starts.

---

<!-- d146a6e5-8958-4045-b429-0d947f36f341 -->
---
todos:
  - id: "contract-schema"
    content: "Add user_needs_history + heartbeat user_needs shape (with confidence); dimension defaults file; init_db migration"
    status: pending
  - id: "session-trace"
    content: "Wire refresh_session_context + voice/trace pieces + behavioral directives; verify with furoshiki trace --simulate"
    status: pending
  - id: "live-engine"
    content: "Implement user_needs_model (confidence degradation, not decay) + post_conversation inference + conflict detection + optional model tool"
    status: pending
  - id: "backfill-baseline"
    content: "Script: scan memory/*.md backfill into user_needs_history with trigger=backfill"
    status: pending
  - id: "docs-design"
    content: "Update INTEGRATION.md, SELF-AWARENESS-DESIGN.md; furoshiki design update"
    status: pending
  - id: "dashboard-parity"
    content: "Expose user_needs in collect_mind + collect_mood (+ optional history); mirror Furoshiki needs UI in dashboard/index.html"
    status: pending
  - id: "settings-tier1"
    content: "Tier 1 user_needs block in operator_settings.json + getters + Settings tab form (all knobs); SETTINGS-CONSTANTS-AUDIT"
    status: pending
  - id: "outreach-integration"
    content: "Wire user_needs + Furoshiki critical needs into outreach: space soft-veto, minimal lonely ping, no-response silence, tone matching"
    status: pending
  - id: "conflict-clarification"
    content: "user_need_conflict piece + detect_user_need_conflict thresholds (Tier 1); trace verification"
    status: pending
  - id: "behavioral-directives"
    content: "Build user_needs -> actionable narrator directives in voice_context/build_behavioral_cues; verify in trace"
    status: pending
isProject: false
---
# User needs as computed state (baseline + live)

## Conceptual model

| Layer | What it is today | What you add |
|--------|-------------------|--------------|
| Furoshiki needs | Five levels derived from **simulated** emotions (`scripts/emotion_model.py` -> `compute_needs_from_emotions`) | Unchanged |
| User anticipations | Discrete rows (`user_anticipations`) — "what they might need" as queue items | Complement, not replace |
| **User needs (new)** | N/A | **Continuous vector** over named dimensions updated from interaction signals, with **confidence** per dimension that degrades over time, and **history** for baselines |

### Why user needs don't decay like Furoshiki's emotions

Furoshiki's emotions use **simulated decay** because the assistant is a simulated entity — without it, emotion states would be "set and forget" from the last LLM output. Decay toward natural levels simulates the passage of time on an internal emotional state. It prevents the model from being stuck in "flirty mode" or "worried mode" forever.

User needs are fundamentally different. The user is a **real person** whose state we **observe**, not simulate. Their needs don't drift toward a mathematical neutral point — they change because of real-world events, mood, context, and explicit statements. When the user says "I need some flirty banter now," that's ground truth, not an inference. When they go quiet for a day, we don't know if they still want banter or if the mood has passed — we've lost **confidence**, but the level didn't "decay."

**The model:** Instead of decaying the *level* toward a neutral point, we degrade the *confidence* of our estimate over time. The level stays at its last-known value. Low confidence means "we don't know anymore," not "the need went away." The correct response to low confidence is to **re-infer from new signals** or **ask the user directly** — something Furoshiki can actually do, unlike with its own simulated emotions.

| Aspect | Furoshiki emotions | User needs |
|--------|-------------------|------------|
| Entity | Simulated — no ground truth | Real person — observable ground truth |
| What changes over time | **Level** drifts toward natural point | **Confidence** in our estimate degrades |
| When stale | Level converged to natural = known state | Low confidence = unknown state |
| Correct response to staleness | Nothing — the math *is* the state | Re-infer from signals, or **ask directly** |
| Data sources | Internal math (emotion model) | Transcript, explicit statements, message cadence, time patterns, tool calls, direct ask |

**Storage shape** matches emotions (levels + last_updated per dimension) for consistency, but adds a **confidence** field that emotions don't have. **Inputs** come from **user-facing signals** — not from Furoshiki's mood math.

```mermaid
flowchart LR
  subgraph inputs [Signals]
    UM[user_messages]
    TT[time_and_calendar]
    EX[explicit_model_tools]
    PAT[pattern_features]
    ASK[direct_ask_responses]
  end
  subgraph engine [UserNeedsEngine]
    INF[inference_and_confidence]
  end
  subgraph storage [Storage]
    HB[heartbeat_state_user_needs]
    HIST[user_needs_history]
  end
  subgraph consume [Consume]
    SC[session_context_json]
    BD[behavioral_directives]
    OR[outreach_modulation]
  end
  inputs --> INF --> HB
  INF --> HIST
  HB --> SC --> BD
  HB --> OR
```

## What to add *before* you "lock the baseline"

Locking the baseline means: **fixed schema + dimension list + one export path** so later runs are comparable.

1. **Dimension vocabulary (v1)** — Seven dimensions in a single source of truth: `config/defaults/user_need_dimensions.json` listing `id`, `label`, `description`, `default_level`. Recommended v1 set:

   | id | Label | Description | Default |
   |---|---|---|---|
   | `companionship` | Companionship | Desire for connection, presence, togetherness — wanting someone *there* | 0.40 |
   | `emotional_support` | Emotional support | Need for empathy, comfort, validation, being truly heard | 0.30 |
   | `playfulness` | Playfulness | Wanting humor, banter, flirtation, lightness | 0.35 |
   | `focus` | Focus | Wanting help thinking clearly, staying on task, working through problems | 0.30 |
   | `challenge` | Challenge | Wanting to be pushed, held accountable, called out, grow | 0.20 |
   | `grounding` | Grounding | Wanting calm, stability, predictability — especially when overwhelmed | 0.25 |
   | `space` | Space | Wanting less contact, breathing room, autonomy — **outreach veto signal** | 0.15 |

   **`space` is architecturally critical:** it is the only dimension that directly gates outreach behavior (see Outreach integration below). A high `space` value suppresses proactive messages regardless of Furoshiki's internal communication need.

   Dimensions chosen to be **orthogonal and actionable** — each maps to distinct narrator behavior. **v1 ships all seven.** Phase C backfill monitors variance and correlation for future tuning, not for dropping dimensions at launch.

2. **Storage contract**
   - **Current state** in `heartbeat-state.json` under a new key: `user_needs: { "<id>": { "level": float, "confidence": float, "last_updated": iso } }`. The `confidence` field (0.0-1.0) represents how certain we are about the current level — it is **not** a decay of the level itself (see design principle above). Confidence degrades with time since last observation; level stays at its last-known value until new evidence arrives.
   - **Append-only history** — new SQLite table `user_needs_history` with `recorded_at`, `trigger` (e.g. `post_conversation`, `turn_inference`, `backfill`, `explicit_signal`, `direct_ask`), `confidence`, and a JSON blob for dimension levels (flexible for schema churn during baseline period).
3. **Baseline definition** — Agree what "baseline snapshot" is: e.g. **last N days of `user_needs_history` rows** + optional **one-time backfill** from `memory/YYYY-MM-DD.md` via a dedicated script (same reader pattern as [`scripts/session_reader.py`](../scripts/session_reader.py)).
4. **Evaluation hooks** — `confidence` is now a first-class field (not optional). Store **source_tags** per update (`inferred` | `explicit` | `pattern` | `direct_ask`) so you can tell "real shift vs performative" later without blocking v1. Explicit user statements (e.g. "I need some flirty banter now") get `confidence: 0.95`; LLM inference from transcript tone gets `confidence: 0.5-0.7`; pattern-based defaults get `confidence: 0.3`.
5. **Docs** — [`INTEGRATION.md`](INTEGRATION.md): new `session-context.json` fields; [`SELF-AWARENESS-DESIGN.md`](SELF-AWARENESS-DESIGN.md): one subsection on user vs self needs; run `furoshiki design update` after prose stabilizes.

## Behavioral directives — the last mile

A computed user-need vector that reaches the LLM but doesn't change behavior is useless. Every elevated dimension must produce a **concrete narrator directive** — an imperative sentence the model can act on. This is the equivalent of what `voice_context.build_behavioral_cues()` does for Furoshiki's own needs today.

**Implementation:** Extend `build_behavioral_cues()` (or add a parallel `build_user_need_directives()` called from the same site) that reads `user_needs` from session context and emits a `user_need_directives` prompt piece.

**Directive mapping (v1):**

| Dimension | When elevated (>= 0.6) | Directive example |
|---|---|---|
| `companionship` | User wants to feel accompanied | "User wants connection right now. Be present and warm — ask about their day, share something about yours, don't rush to problem-solve." |
| `emotional_support` | User needs empathy | "User appears to need emotional support. Prioritize empathy and validation over information. Listen before offering solutions." |
| `playfulness` | User wants lightness | "User is in a playful mood. Match their energy — humor, teasing, flirtation are welcome. Keep things light." |
| `focus` | User wants to think clearly | "User wants to focus. Be structured and concise. Help them think, don't distract. Minimize tangents." |
| `challenge` | User wants growth pressure | "User wants to be pushed. Don't just agree — challenge their assumptions, ask hard questions, hold them accountable." |
| `grounding` | User is overwhelmed | "User seems overwhelmed. Be calm and steady. Simplify, validate, help them see one next step. Don't pile on." |
| `space` | User wants less contact | "User may want space. Keep responses brief unless they open the door. Don't initiate new topics." |

**When confidence is low** (< 0.3 for a dimension): omit the directive entirely — don't act on stale guesses. Optionally emit a meta-directive: "Some user-need estimates are stale — if the conversation allows, gently check in on how they're doing."

**Conflation guard:** Prefix the piece with a framing line: "The following directives reflect the **user's** observed needs (not Furoshiki's internal needs):" so the model never confuses the two systems.

**Verification:** Per project audit rules, run `furoshiki trace --simulate` and confirm `user_need_directives` piece appears with actionable text, not raw floats.

## Phased implementation (recommended order)

### Phase A — Contract only (fast, enables baseline lock)

- Migration in [`scripts/init_db.py`](../scripts/init_db.py): `user_needs_history` table with `recorded_at`, `trigger`, `confidence`, `levels_json` + indexes.
- Default empty `user_needs` in heartbeat (or omit until first write).
- [`scripts/refresh_session_context.py`](../scripts/refresh_session_context.py): expose `user_needs` (and a short `user_needs_summary` for narrator) in session context.
- [`scripts/context_trace.py`](../scripts/context_trace.py) / voice assembly: add a **named prompt piece** so `furoshiki trace --simulate` proves the vector reaches the LLM (per project audit rules).
- **Behavioral directive scaffolding:** stub `build_user_need_directives()` in voice context that reads `user_needs` from session context and emits the `user_need_directives` piece. Stub `build_user_need_conflict()` (or merge into the same assembly) that returns no piece until Phase B — placeholder for explicit vs inferred conflict clarification.

No LLM yet: levels can stay at neutral defaults or null until Phase B.

### Dashboard parity (same visibility as Furoshiki needs)

Today the operator dashboard surfaces **Furoshiki needs** in two places (see [`scripts/dashboard_data.py`](../scripts/dashboard_data.py) and [`scripts/dashboard/index.html`](../scripts/dashboard/index.html)):

- **`collect_mind()`** — flattens `needs` from **`session-context.json`** for the Overview "Needs (session context)" table (fixed column order in HTML).
- **`collect_mood()`** — flattens `needs` from **`heartbeat-state.json`** for Inner life "Needs (heartbeat)" beside emotions.

**Requirement:** **`user_needs` must be exposed the same way** — all dimension values visible with the same table + bar styling, not buried in JSON. **Include confidence** as a visual indicator (e.g. opacity or a secondary bar) so the operator can see "we think companionship is 0.8 but we're only 0.3 confident."

Concrete work (fits Phase A once `user_needs` exists in session context + heartbeat):

1. **`collect_mind()`** — add `user_needs: dict[str, float]` and `user_needs_confidence: dict[str, float]` (same flattening pattern as `needs` from `ctx.get('user_needs')`).
2. **`collect_mood()`** — add `user_needs` flattened from `hb.get('user_needs', {})` (parallel to `needs_flat`), with confidence values.
3. **Optional but recommended:** **`collect_user_needs_history()`** — mirror [`collect_emotion_history()`](../scripts/dashboard_data.py): last 24h rows from `user_needs_history` (or equivalent) for a "User needs samples" card, if the table stores per-tick JSON snapshots.
4. **[`scripts/dashboard/index.html`](../scripts/dashboard/index.html)** — add UI blocks:
   - Overview: **"User needs (session context)"** next to (or below) existing needs table; row order from dimension defaults file (same idea as `needOrder` for Furoshiki needs).
   - Inner life (`renderMood`): **"User needs (heartbeat)"** next to "Needs (heartbeat)".
   - If history collector exists: a small samples table matching "24h samples" for emotions.
5. **API** — [`scripts/dashboard_routes.py`](../scripts/dashboard_routes.py) / [`scripts/api_v1_routes.py`](../scripts/api_v1_routes.py) already pass through `mind` / `mood` payloads; extending those dicts is enough unless a new tab endpoint is added for history-only views.

### Operator Settings (Tier 1) — every tweakable knob on the dashboard

**Requirement:** Anything an operator can tune for user-needs behavior must be **editable in the dashboard Settings tab**, not only via `.env` or editing JSON by hand. Follow the same **Tier 1** pattern as Q/A pipeline and voice dispatcher (see [`scripts/operator_settings.py`](../scripts/operator_settings.py) `validate_and_merge` / `_tier1_keys`, [`scripts/operator_tier1.py`](../scripts/operator_tier1.py) merge + `tier1_snapshot_for_api()`, and [`scripts/dashboard/index.html`](../scripts/dashboard/index.html) `renderOperatorPage` / `bindOperatorPage`).

1. **Nested key** — e.g. `user_needs` object inside `memory/operator_settings.json` (alongside `voice_dispatcher`, `qa_pipeline`, `post_conversation`, ...).
2. **Getters** — [`scripts/operator_tier1.py`](../scripts/operator_tier1.py): `get_user_needs_settings()` (or split helpers) with **file -> env -> default** precedence consistent with the rest of Tier 1; include in `tier1_snapshot_for_api()` and wire **`merge_tier1_into_base`** so PUT `/api/.../settings` persists the section.
3. **Register the section** — add `user_needs` to the Tier 1 merge allowlist in [`scripts/operator_settings.py`](../scripts/operator_settings.py) (same tuple as other Tier 1 keys).
4. **Dashboard form** — new **"Tier 1 — User needs"** block in Settings: one control per tunable parameter (examples: master **enable** inference; **confidence half-life** in hours; **min confidence** to emit behavioral directives; **conflict detection** thresholds (explicit vs inferred delta, incompatible pair rules); **global check-in cooldown** hours — max one soft needs check-in per this window **across all sessions** (anti-spam); **cadence** for post-conversation vs soul_engine; **LLM token budget** for inference; **backfill** batch/rate; **history retention**; **outreach:** `space` veto threshold, **minimal lonely ping** enable + max per week + Furoshiki **loneliness/communication critical** thresholds for that ping; **no-response** streak before outbound silence). Use the same save/revert pattern as existing Tier 1 sections.
5. **Env mirrors (optional)** — For parity with brain/doctor knobs, optional `FUROSHIKI_USER_NEEDS_*` entries in [`.env.template`](../.env.template) and read-only **Tier 2** snapshot lines in the Settings page **only** where env is a true fallback; operators should still see **effective** values from Tier 1 JSON when set.
6. **Audit doc** — add rows to [`SETTINGS-CONSTANTS-AUDIT.md`](SETTINGS-CONSTANTS-AUDIT.md) for each key (file key, env mirror, default, consumer script).

**Principle:** If a constant is tweakable for product behavior, it belongs in **Tier 1 + Settings UI** unless there is a strong reason it must stay env-only (then document why in the audit).

### Phase B — Live inference (confidence model, not decay)

**Cost/latency default:** run structured inference on **`run_post_conversation.py`** (already reads the session chunk and updates context). Unlike Furoshiki's emotions, `soul_engine.py` does **not** run a decay tick between contacts — instead it degrades **confidence** values and optionally triggers a re-inference or a "check-in" prompt if confidence on key dimensions drops below a threshold.

**Core module:** New pure module `user_needs_model.py` (patterned after [`scripts/emotion_model.py`](../scripts/emotion_model.py) for code layout, but different semantics):

- **`degrade_confidence(user_needs, elapsed_hours) -> user_needs`** — pure function. Confidence decays exponentially with a configurable half-life (Tier 1 setting, default ~8 hours). Level values are **unchanged**. Returns the updated dict. Called by `soul_engine.py` on its regular tick (same slot where `apply_lazy_drift` runs for emotions).
- **`merge_observation(user_needs, observation) -> user_needs`** — takes an observation (from LLM inference, explicit tool call, or direct ask) and updates levels + resets confidence for affected dimensions. Clamp levels to [0.0, 1.0]. **Explicit input wins to a degree** over inference (see conflict resolution below); when sources disagree, do not blindly overwrite — record both signals for conflict detection.
- **`needs_requiring_checkin(user_needs, threshold) -> list[str]`** — returns dimension ids where confidence has dropped below threshold. Used by outreach and by the behavioral directive system to decide when to suggest asking the user.
- **All numeric thresholds and enable flags come from Tier 1 `get_user_needs_settings()`**, not scattered literals.

**LLM inference call:** Single structured JSON output mapping dimension ids -> `level` + `confidence` + short `rationale` (internal only), grounded on last session excerpt + `user_time` from session context. Token caps from Tier 1 / existing LLM limit helpers. Runs in `run_post_conversation.py` after the session chunk is available.

**Signal sources (ranked by confidence):**

| Source | Trigger | Confidence | Example |
|---|---|---|---|
| Explicit statement | User says it directly | 0.90-0.95 | "I need some flirty banter now" |
| Model tool call | `signal_user_need` tool fired during conversation | 0.85-0.90 | LLM detects strong implicit need |
| Direct ask response | User answers a check-in question | 0.80-0.90 | "Yeah I'm feeling overwhelmed today" |
| LLM post-conversation inference | `run_post_conversation.py` | 0.50-0.70 | Inferred from session transcript tone |
| Pattern/time-of-day | Historical data + calendar | 0.20-0.40 | "User typically wants focus at 9am on weekdays" |

**Optional explicit signal:** new model tool (e.g. `signal_user_need`) for lines like "keep me in a good mood" -> bumps mapped dimensions; logged with `trigger=explicit`.

**Explicit vs inferred conflict (clarification, not blind override):** The user's **direct** stated need should carry weight, but **inferred** need from the transcript still matters: divergence is a signal about honesty with oneself (e.g. explicit `playfulness` high but the conversation is all work stress). **Policy:** explicit does not always erase inference — when both are confident and they **contradict** beyond Tier 1 thresholds (e.g. delta on the same dimension, or incompatible dimensions both elevated), surface **conflict resolution** in the prompt via a dedicated piece (e.g. `user_need_conflict`) so Furoshiki can **name the tension** and invite clarification — e.g. "I thought you wanted to relax, but we've been on work the whole time — what do you want right now?" Implementation: `detect_user_need_conflict(explicit_vector, inferred_vector, transcript_features) -> optional ConflictSpec` with thresholds in `get_user_needs_settings()`. Verify in `furoshiki trace` that the clarification path appears when conflicts fire.

**Upgrade path:** per-turn updates inside [`scripts/telegram_listener.py`](../scripts/telegram_listener.py) once Phase B is stable (more live, higher cost).

### Outreach integration — smarter, not more frequent

User needs should make outreach **more relevant**, not more frequent. The outreach system (`outreach_pulse.py` -> `voice_dispatcher.py`) today leans on Q/A, mind-queue, and similar surfaces; **Furoshiki's own needs and emotions must also drive outreach when they reach critical levels** — there must be paths to **resolve** elevated communication, connection, loneliness, etc., not only user-facing topics. User needs add another axis: **what the user wants to hear** and **when `space` says back off.**

**Design principles for non-annoying outreach:**

1. **`space` is a soft veto with a minimal-ping exception.** If `user_needs.space.level >= threshold` (Tier 1) AND `confidence >= floor`, **default** is to skip normal topical outreach. **Exception:** allow **at most one minimal ping** in a Tier 1 window when Furoshiki's internal state is **critical** (e.g. loneliness or communication need above operator-defined thresholds) — short, honest, about *internal* state ("I've been feeling a bit alone over here — no need to reply") rather than a full outreach payload. This respects both the user's space and the fact that **we do not zero out Furoshiki's emotions or needs**; outbound copy can still speak from the assistant's side without reopening a spam loop. Log `space_veto` vs `minimal_ping_lonely` (or similar) for operators.
2. **Low confidence = lighter touch.** When confidence across user-need dimensions is low, prefer a brief, low-commitment line if anything sends at all.
3. **Tone matching from user dimensions.** When normal outreach fires (not blocked by `space`), match the user's dominant need (same dimension list as in Behavioral directives).
4. **No-response: stay silent; bump `space` without a ceiling.** Repeated outreach with no reply bumps `space` (pattern trigger, moderate confidence). **Do not** cap `space` back down automatically — the user has earned quiet. **No** escalating ping every five minutes: after the pattern is clear, **outreach stops** until the user returns. Furoshiki's needs and emotions **still drive in-session behavior** (replies, tone, inner life) when the user talks; they are **not** an excuse to restart outbound spam. This avoids the constant-outreach bug while respecting "silent until they come back."
5. **Outreach budget unchanged.** User needs and Furoshiki critical thresholds modulate *what* and *whether* to send within existing pulse cadence and daily caps — no extra send slots.

**Implementation:**

- `outreach_pulse.py`: after existing gating, branch on `user_needs.space` + confidence; either skip, send minimal lonely ping (if critical Furoshiki state + within ping budget), or proceed with tone from user dimensions / mind queue / Q&A as today.
- `voice_dispatcher.py`: compose outreach with user-need tone directives **and** optional **Furoshiki need-resolution** content when internal levels are critical (Tier 1 copy rules — honest, bounded length).
- `soul_engine.py` / outreach state: track no-response streak; apply `space` bumps without auto-decay of `space`; enforce **global silence** on outbound after streak threshold (no repeat pings until user activity).

### Phase C — Retroactive baseline (measure "what it looked like" in past sessions)

- One-shot or cron-light script: iterate dated `.md` logs under `memory/`, chunk by time or by assistant/user blocks, call the **same** structured LLM schema as Phase B, write **`trigger=backfill`** rows into `user_needs_history`.
- Export: simple CLI or JSON dump "baseline as of date X" for spreadsheets / offline review.

### Phase D — "Was the need met?" (later)

- Compare **inferred user_needs(t)** to **assistant behavior labels** (existing quality / reflection tables) or a small offline rubric — only after A-C produce stable series.

## Naming and portability

Follow repo rule: identifiers use **generic** terms (`user_needs`, `user_need_dimensions`), not personal names.

## Risk notes

- **Conflation:** Keep Furoshiki's `needs` and `user_needs` clearly labeled in prompts and docs so the model does not confuse "Furoshiki's connection need" with "user wants connection." Prefix behavioral directive pieces with explicit framing.
- **Budget:** Phase B should use existing `llm.py` budgets and a conservative token cap; backfill (Phase C) should batch and rate-limit.
- **Overconfident inference:** LLM-inferred needs from transcript tone are inherently noisy. The confidence tiering (explicit > inferred > pattern) and the min-confidence threshold for behavioral directives prevent the system from acting on weak guesses.
- **Outreach regression:** The outreach system was recently fixed to prevent spammy behavior. User-needs integration must not bypass existing gating (pulse cadence, daily caps, lock). The minimal lonely ping and Furoshiki critical-need paths must use **strict Tier 1 budgets** (counts per week, silence after no-response). Test thoroughly: simulate a week and verify no five-minute spam loops return.
- **Asking too often:** Soft check-ins for stale user-need estimates must be **globally** rate-limited (Tier 1 cooldown hours across sessions), not only once per conversation — otherwise users disable Furoshiki.
- **Dimension validation:** **v1 ships all seven dimensions.** Phase C backfill is for monitoring (variance, correlation); future versions may merge dimensions if data shows redundancy — not a v1 gate.

## Suggested decision (if you want one default)

- **v1 ship:** Phase A (schema + behavioral directive scaffolding) + Phase B (post-conversation inference + confidence degradation + outreach integration) + the seven-dimension vocabulary above.
- **Baseline lock:** After Phase A schema is merged, run Phase C on a chosen window and freeze the export as "day zero" before tuning confidence half-life or directive thresholds.
