# How the Pre-Send Check Works in the i‑have‑ADHD Extension

> Understand the i-have-ADHD extension's pre-send check. Learn how this validation step ensures ADHD-friendly rules are correctly applied to conversation context for better interaction.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: internals
- Published: 2026-09-02

---

**The pre-send check is a validation step that runs before every model response to determine whether ADHD-friendly rules must be injected into or removed from the conversation context.**

The i‑have‑ADHD extension for Claude Code uses this mechanism to ensure the model follows ADHD-optimized communication rules only when explicitly enabled. The check prevents duplicate injections and guarantees clean removal when the mode is disabled, all without requiring manual context management by the user.

## Where the Pre-Send Check Lives in the Codebase

The pre-send check spans two core files in the `extensions/` directory:

| Component | File Path | Purpose |
|-----------|-----------|---------|
| `rulesAreInContext` | [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) | Examines session markers to determine if rules are currently active |
| `syncContext` | [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) | Executes injection or removal based on the check's result |

This architecture separates **detection logic** from **action logic**, making the pre-send check both testable and context-agnostic.

## The Detection Logic: `rulesAreInContext`

The `rulesAreInContext` function in [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) performs the actual pre-send validation. It uses a helper function called `latestMarkerIsActive` to compare the order and types of custom markers in the conversation.

The function returns `true` only when the most recent active marker is `i-have-adhd-rules` — not `i-have-adhd-disabled` or any other state. This comparison accounts for marker timestamps and handles both OpenAI Message Protocol (OMP) and Pi context formats.

```typescript
// Conceptual flow of rulesAreInContext
function rulesAreInContext(sessionMarkers: Marker[]): boolean {
  // Find latest occurrence of each marker type
  const rulesIndex = findLatestIndex(sessionMarkers, "i-have-adhd-rules");
  const disabledIndex = findLatestIndex(sessionMarkers, "i-have-adhd-disabled");
  
  // Rules are active only if they appear after any disable marker
  return latestMarkerIsActive(rulesIndex, disabledIndex);
}

```

The `latestMarkerIsActive` helper is unit-tested in [`scripts/check_context_compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/scripts/check_context_compat.ts) to ensure correct behavior across different context implementations.

## The Action Logic: `syncContext` Pre-Send Handler

Before each turn, event handlers in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) call `syncContext`. This function uses the pre-send check result to maintain context integrity:

- **Mode enabled + rules missing**: Inject the full rule set via hidden `pi.sendMessage()`
- **Mode disabled + rules present**: Send `i-have-adhd-disabled` marker to cancel previous injection
- **No state change**: Skip modification, avoiding unnecessary context bloat

```typescript
// Simplified syncContext implementation pattern
async function syncContext(pi: PiAPI, enabled: boolean): Promise<void> {
  const rulesPresent = await rulesAreInContext(pi.getSessionMarkers());
  
  if (enabled && !rulesPresent) {
    // Pre-send injection: rules needed but not found
    await pi.sendMessage({
      role: "system",
      content: loadRulesFrom("skills/i-have-adhd/SKILL.md"),
      hidden: true
    });
  } else if (!enabled && rulesPresent) {
    // Pre-send cleanup: disable marker supersedes rules
    await pi.sendMessage({
      role: "system",
      content: "i-have-adhd-disabled",
      hidden: true
    });
  }
  // Else: state matches desired mode, no action needed
}

```

## What the Pre-Send Check Guards Against

| Failure Mode | Pre-Send Defense |
|-------------|------------------|
| **Duplicate rule injection** | `syncContext` only calls `sendMessage` when `rulesAreInContext` returns `false`, preventing multiple identical rule blocks from accumulating |
| **Stale rules after disable** | The disabled marker takes precedence in `latestMarkerIsActive` comparison, ensuring rules stop applying immediately upon user request |
| **Context pollution** | Hidden messages keep the rule management invisible to users while maintaining clear marker history for the check |

## Complete Pre-Send Flow Example

```typescript
// 1️⃣ User enables ADHD mode
await pi.sendCommand("i-have-adhd");   // toggles enabled = true

// Pre-send check runs: rulesAreInContext() → false
// Action: Inject full rules from skills/i-have-adhd/SKILL.md

// 2️⃣ Model responds with ADHD-optimized formatting
// Pre-send check runs: rulesAreInContext() → true  
// Action: No injection needed, response proceeds normally

// 3️⃣ User disables with stop phrase
await pi.processInput("stop adhd mode");  // toggles enabled = false

// Pre-send check runs: rulesAreInContext() → true (rules still present)
// Action: Send i-have-adhd-disabled marker

// 4️⃣ Next model response follows default style
// Pre-send check runs: latest marker is disabled → rulesAreInContext() → false
// No rules applied, standard formatting restored

```

## Rule Source and Customization

The actual rule content injected during the pre-send check resides in [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md). This markdown file contains the complete ADHD-friendly communication guidelines that the model follows when the pre-send check determines rules should be active. Modifying this file changes the behavior without altering the pre-send check logic itself.

## Summary

- The **pre-send check** is implemented through `rulesAreInContext` in [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) and executed via `syncContext` in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts)
- It validates marker state using `latestMarkerIsActive` to determine if ADHD rules are currently in effect
- The check prevents duplicate injections by comparing desired state against actual context markers
- Clean disable is achieved through marker precedence — `i-have-adhd-disabled` always wins over `i-have-adhd-rules` when newer
- Rule content is sourced from [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) and injected as hidden system messages only when needed

## Frequently Asked Questions

### What triggers the pre-send check to run?

The pre-send check executes before every model turn through event handlers in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts). Any user message, command, or state change calls `syncContext`, which invokes `rulesAreInContext` to validate the current marker state.

### Why use hidden messages instead of modifying the prompt directly?

Hidden messages preserve the conversation history structure while keeping rule management invisible to users. This approach maintains compatibility with different Claude Code context formats (OMP and Pi) without requiring platform-specific prompt injection methods.

### How does the extension handle rapid enable/disable toggles?

Each toggle updates the timestamp of its respective marker. The `latestMarkerIsActive` helper always selects the most recent marker by time, so rapid state changes resolve correctly based on final toggle order regardless of intermediate states.

### Can the pre-send check logic be tested independently?

Yes. The [`scripts/check_context_compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/scripts/check_context_compat.ts) file contains unit tests specifically for `latestMarkerIsActive` and `rulesAreInContext`. These tests verify correct marker comparison without requiring full Claude Code environment integration.