Elnor Repo Reader

DOC24_AMENDMENT_PERSISTENT_ONBOARDING_CURIOSITY.md

Current Specs/DOC24/DOC24_AMENDMENT_PERSISTENT_ONBOARDING_CURIOSITY.md

Short text page 8d9e8aae26df. Generated 2026-06-09T01:23:58.539Z from commit dbaa25962edc11ab30e8d4ca1715f9ae5bf77331. Worktree: clean.

Open readable HTML page · Open raw txt · Open path URL

ELNOR REPO READER TEXT MIRROR
Original path: Current Specs/DOC24/DOC24_AMENDMENT_PERSISTENT_ONBOARDING_CURIOSITY.md
Source repo: /Users/OpenClaw1/Elnor/Elnor Specs
Git branch: main
Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331
Generated: 2026-06-09T01:23:58.539Z

---

# DOC24 Amendment — Persistent Onboarding Curiosity

**Date:** 2026-04-12  
**Target:** DOC24 R2.5 §§11.5, 12.4D, 12.4E  
**Cross-doc obligations:** DOC72 §27.7 (PauseOnboardingRequest guardrails)  
**Status:** Proposed amendment  
**Principle:** Onboarding is perpetual. The system should always be curious about things it doesn't know. The governance mechanisms that prevent annoyance must not compound into effective silence.

---

## 1. §11.5 — Probe Budget: Add Contextual Question Cost Tier

**Current text (§11.5):**

```
Default policy values in this revision:
- initial budget: `5`
- clarifying question cost: `-1`
- topic-shift question cost: `-2`
- successful action replenishment: `+1`
- explicit onboarding session entry: reset to full onboarding profile budget
```

**Replace with:**

```
Default policy values in this revision:
- initial budget: `5`
- contextual_probe cost: `-0.5`
- clarifying question cost: `-1`
- topic-shift question cost: `-2`
- successful action replenishment: `+1`
- explicit onboarding session entry: reset to full onboarding profile budget
```

**Add after the existing `ProbeBudget` schema:**

```ts
type ProbeClassification = "contextual_probe" | "clarifying_question" | "topic_shift_question";

/**
 * Contextual probe: a question about the thing the user is ALREADY working on.
 * "I noticed you referenced Paramount — is that a case you're working on?"
 * Cost: -0.5 (half a standard question). Rationale: the user is already in context,
 * the question is immediately relevant, and the answer directly improves the current
 * interaction. This is the cheapest form of probing.
 *
 * Clarifying question: a question that seeks to disambiguate the user's current request.
 * "Did you mean Henderson the case or Henderson Financial?"
 * Cost: -1 (standard). Rationale: necessary for task completion but adds friction.
 *
 * Topic-shift question: a question about something unrelated to the current task.
 * "While I have you — what's your preferred citation format for state court filings?"
 * Cost: -2 (expensive). Rationale: interrupts flow, may feel intrusive.
 */
function classifyProbe(probe: {
  relatedToCurrentEntity: boolean;
  relatedToCurrentTask: boolean;
  triggeredByGapInCurrentContext: boolean;
}): ProbeClassification {
  if (probe.relatedToCurrentEntity || probe.triggeredByGapInCurrentContext) {
    return "contextual_probe";
  }
  if (probe.relatedToCurrentTask) {
    return "clarifying_question";
  }
  return "topic_shift_question";
}
```

**Rationale:** The current flat cost model treats "is Paramount a case?" (asked while the user is working on Paramount) the same as "what citation format do you prefer?" (asked out of nowhere). The first is nearly free in terms of user friction because the user is already thinking about Paramount. The second interrupts focus. Differentiating the cost encourages the system to ask contextually relevant questions more freely while remaining conservative about off-topic probing.

---

## 2. §12.4D — Gap Detection: Add Knowledge Density Gap Rule and Proactive Curiosity Rule

**Current text (§12.4D):**

```
The system maintains a rule-based gap detection engine with at least 7 gap rules:
```

**Replace "at least 7 gap rules" with "at least 9 gap rules" and add two rows to the gap rule table:**

| Gap rule | Trigger | System action |
|---|---|---|
| Missing entity name | User references entity not in graph | Offer to create entity |
| Missing preference for action family | Action executed 3+ times with different parameters | Propose preference capture |
| Missing terminology rule | User corrects Elnor's term usage | Propose terminology preference |
| Missing workflow default | Same workflow choice made 3+ times | Propose workflow default |
| Stale entity | Entity not validated in 90+ days, used in routing | Offer refresh |
| Missing constraint | User corrects an action Elnor took | Propose constraint |
| Missing folder linkage | User manually navigates to folder 3+ times for same entity | Propose folder link |
| **Knowledge density gap** | **Work context referenced in 3+ interactions AND has fewer than 15 confirmed entities** | **Propose targeted onboarding: "I don't know much about [context] yet — want to tell me about it?"** |
| **Dormant curiosity reactivation** | **No system-initiated probes in 14+ days AND open gaps exist (any rule)** | **Force one gentle contextual probe on next interaction, bypassing LLM self-suppression** |

**Add after the gap rule table:**

### 12.4D.1 Knowledge Density Gap — Detail

The knowledge density gap detects work contexts where the system's knowledge is thin relative to the user's engagement. This prevents the common failure mode where the user starts working on a new matter and the system passively watches for weeks without asking anything.

```ts
type KnowledgeDensityGapCheck = {
  work_context_id: string;
  work_context_name: string;
  interaction_count: number;           // interactions referencing this context
  confirmed_entity_count: number;      // confirmed entities linked to this context
  density_threshold: number;           // default 15
  interaction_threshold: number;       // default 3
  gap_detected: boolean;
};

function checkKnowledgeDensityGap(ctx: {
  work_context_id: string;
  interaction_count: number;
  confirmed_entity_count: number;
  config: { density_threshold: number; interaction_threshold: number };
}): boolean {
  return ctx.interaction_count >= ctx.config.interaction_threshold
    && ctx.confirmed_entity_count < ctx.config.density_threshold;
}
```

The density threshold (default 15) represents a minimum useful knowledge base for a work context: at least a few key entities (parties, judge, key documents, relevant concepts). Below this threshold, the system can't meaningfully assist with context-aware work and SHOULD proactively seek to fill the gap.

The density gap check runs nightly as part of the reflection loop. When a gap is detected, it produces a `GapDetectionResult` with `gap_rule: "knowledge_density_gap"` and `work_context_id`. This feeds into the trigger table (§12.4E) under the existing "Gap detection finds ≥ 3 open gaps" row — BUT see §12.4E amendments below for per-context cooldown.

### 12.4D.2 Dormant Curiosity Reactivation — Detail

The dormant curiosity rule prevents the compound suppression problem: probe budget exhaustion + DOC8 friction adaptation + LLM self-suppression + cooldowns can combine to produce a system that never asks anything for weeks.

```ts
type DormantCuriosityCheck = {
  last_system_initiated_probe_at: string | null;
  dormancy_threshold_days: number;     // default 14
  open_gap_count: number;
  dormant: boolean;
  reactivation_required: boolean;
};

function checkDormantCuriosity(ctx: {
  last_system_initiated_probe_at: string | null;
  open_gap_count: number;
  config: { dormancy_threshold_days: number };
}): boolean {
  if (ctx.open_gap_count === 0) return false;
  if (!ctx.last_system_initiated_probe_at) return true;
  const daysSinceLastProbe = daysBetween(ctx.last_system_initiated_probe_at, now());
  return daysSinceLastProbe >= ctx.config.dormancy_threshold_days;
}
```

When dormant curiosity reactivation fires:

1. The system selects the HIGHEST PRIORITY open gap (by gap rule priority: knowledge_density_gap > stale_entity > missing_constraint > missing_preference > others).
2. The selected gap is delivered as a `contextual_probe` (cost -0.5, per §11.5 amendment) on the NEXT interaction where the user's request touches the relevant work context or entity.
3. The probe bypasses the LLM's `PauseOnboardingRequest` with reason `context_sufficient` — the system overrides the LLM's judgment that it knows enough, because the gap evidence says otherwise.
4. The probe does NOT bypass `PauseOnboardingRequest` with reason `user_seems_busy` — if the user is clearly in deep focus, the probe defers to the next interaction.
5. After the probe fires (whether answered or dismissed), the dormancy timer resets.

**Normative rule:** The LLM MUST NOT suppress ALL probing indefinitely. If `PauseOnboardingRequest` with reason `context_sufficient` has been active for longer than `dormancy_threshold_days` AND open gaps exist, the system SHALL override the pause for exactly one contextual probe. This is not a bug in the LLM's judgment — it's a structural guardrail against the LLM not knowing what it doesn't know.

---

## 3. §12.4E — Onboarding Trigger Table: Per-Context Cooldowns

**Current row:**

| Trigger | Lane | Cooldown | Budget cost |
|---|---|---|---|
| Gap detection finds ≥ 3 open gaps | Conversational | 7 days | -1 |

**Replace with:**

| Trigger | Lane | Cooldown | Budget cost |
|---|---|---|---|
| Gap detection finds ≥ 3 open gaps for a SINGLE work context | Conversational | 7 days per work context | -1 (or -0.5 if contextual) |
| Gap detection finds open gaps across 3+ DIFFERENT work contexts | Conversational | 7 days global | -1 |

**Add after the trigger table:**

### 12.4E.1 Per-Context Cooldown — Detail

The gap detection cooldown SHALL be scoped per work context, not global. Finding gaps in Henderson SHALL NOT suppress probing about Paramount.

```ts
type GapCooldownTracker = {
  cooldowns: Record<string, string>;   // work_context_id → last_probed_at ISO datetime
  global_last_probed_at?: string;
  schema_version: 1;
};

function isGapProbingAllowed(ctx: {
  work_context_id: string;
  cooldown_tracker: GapCooldownTracker;
  per_context_cooldown_days: number;   // default 7
  global_cooldown_days: number;        // default 7
}): boolean {
  // Check per-context cooldown
  const contextLastProbed = ctx.cooldown_tracker.cooldowns[ctx.work_context_id];
  if (contextLastProbed) {
    const daysSince = daysBetween(contextLastProbed, now());
    if (daysSince < ctx.per_context_cooldown_days) return false;
  }
  
  // Check global cooldown (prevents rapid-fire probing across many contexts)
  if (ctx.cooldown_tracker.global_last_probed_at) {
    const daysSinceGlobal = daysBetween(ctx.cooldown_tracker.global_last_probed_at, now());
    if (daysSinceGlobal < 1) return false;  // Max 1 gap-triggered probe per day globally
  }
  
  return true;
}
```

**Effect:** If the user works on 5 different matters in a month, the system can probe about each one independently on its own 7-day cycle. The global rate limit (max 1 gap-triggered probe per day) prevents the system from asking about all 5 matters on the same day when cooldowns happen to align.

**Storage:** `ELNOR_MEMORY/system/state/gap_cooldown_tracker.json` (atomic JSON, EC-written).

---

## 4. §12.4E — Onboarding Trigger Table: Add New Trigger Row

**Add row to trigger table:**

| Trigger | Lane | Cooldown | Budget cost |
|---|---|---|---|
| New work context created or first referenced | Conversational | None (fires once per context) | -0.5 (contextual) |

**Detail:**

When a new work context entity is created (new matter, new project) or when a `world_entity` with `entity_type: "matter"` or `entity_type: "project"` is referenced for the first time in a conversation, the system SHOULD proactively ask one contextual question to seed the knowledge graph for that context:

- "I see you're working on [context name] — can you tell me a bit about it so I can be more helpful?"
- "Is [context name] a new case? Who are the key players?"

This fires ONCE per work context (tracked by `work_context_id` in a first-contact set). It does not repeat. It costs -0.5 (contextual probe rate) because it's directly relevant to what the user is doing right now.

```ts
type WorkContextFirstContactTracker = {
  contacted_context_ids: Set<string>;
  schema_version: 1;
};
```

**Storage:** `ELNOR_MEMORY/system/state/work_context_first_contact.json` (atomic JSON, EC-written).

---

## 5. Cross-Doc Obligation: DOC72 §27.7 PauseOnboardingRequest Guardrails

**DOC72 §27.7 currently defines:**

```ts
type PauseOnboardingRequest = {
  reason: "question_debt_high" | "user_seems_busy" | "context_sufficient" | "session_ending";
  resume_after_turns?: number;
};
```

**DOC72 SHALL add the following constraints (cross-doc obligation from this amendment):**

1. `context_sufficient` pauses SHALL be limited to the CURRENT SESSION. The pause does not carry across sessions. Each new session starts with a fresh pause state.

2. `context_sufficient` pauses SHALL be overridable by the dormant curiosity reactivation rule (DOC24 §12.4D.2). When the system has been dormant for 14+ days AND open gaps exist, the system overrides `context_sufficient` for exactly one contextual probe.

3. `context_sufficient` pauses SHALL NOT suppress gap-triggered probing for work contexts the LLM has never been asked about. The LLM cannot judge context as "sufficient" for a work context it has zero knowledge about.

4. A new `max_dormancy_days` field SHALL be added to `PauseOnboardingRequest`:

```ts
type PauseOnboardingRequest = {
  reason: "question_debt_high" | "user_seems_busy" | "context_sufficient" | "session_ending";
  resume_after_turns?: number;
  max_dormancy_days?: number;          // default 14; system overrides pause after this many days
                                        // if open gaps exist. Prevents indefinite self-suppression.
};
```

5. DOC8 friction adaptation (§11.2) SHALL NOT reduce contextual probe frequency below a floor of 1 contextual probe per 14 days when open gaps exist. DOC8 MAY reduce topic-shift question frequency to zero — those are genuinely interruptive. But contextual probes about the thing the user is already working on have a protected minimum frequency.

---

## 6. Summary of Changes

| Section | Change | Effect |
|---|---|---|
| §11.5 | Add `contextual_probe` cost tier at -0.5 | Contextually relevant questions are half the budget cost of generic clarifying questions. System can ask ~10 contextual probes per session instead of 5. |
| §12.4D | Add knowledge density gap rule | System detects when a work context has thin knowledge relative to user engagement and proactively asks. |
| §12.4D | Add dormant curiosity reactivation rule | System cannot go silent for more than 14 days when it has open gaps. Forces one gentle probe. |
| §12.4E | Per-context cooldowns instead of global | Probing about Henderson doesn't suppress probing about Paramount. |
| §12.4E | Add new-work-context first-contact trigger | System asks one question when it encounters a new matter or project for the first time. |
| DOC72 §27.7 | PauseOnboardingRequest guardrails | LLM self-suppression is session-scoped and overridable by dormancy reactivation. Cannot claim "context sufficient" for contexts it knows nothing about. |

**Invariant preserved:** The system still respects the user's focus. `user_seems_busy` pauses are never overridden. Topic-shift questions remain expensive. The changes only affect CONTEXTUAL probing about things the user is already working on, and dormancy reactivation for situations where compound suppression has silenced the system entirely.

---

*End of DOC24 Amendment — Persistent Onboarding Curiosity*