# How the i-have-adhd Skill Persists Rules Across a Session: A Technical Deep Dive

> Discover how the i-have-adhd skill maintains rule persistence across sessions. Learn about its use of runtime session managers and hidden custom messages for seamless continuity.

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

---

**The i-have-adhd skill persists its rules across a session by storing an `i-have-adhd-state` entry in the runtime's session manager and synchronizing hidden custom messages into the model's context on every session start, tree replay, or compaction event.**

The *i-have-adhd* skill is a conversational AI extension that enforces ADHD-friendly output formatting for the duration of a chat session. Rather than sending rules with every request, the skill implements a lightweight persistence layer that restores state automatically and injects rules only when necessary. This article examines the implementation in `ayghri/i-have-adhd`, focusing on how state survives across session boundaries and model context rebuilds.

## Session State Storage Mechanism

The skill's persistence begins with a dedicated state entry type. When a user toggles ADHD mode, the extension appends a custom entry to the session branch using `pi.appendEntry()`.

In [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) (lines 81-93):

```ts
pi.appendEntry(STATE_ENTRY_TYPE, { enabled } satisfies AdhdModeState);

```

The `STATE_ENTRY_TYPE` constant resolves to `i-have-adhd-state`. This entry contains a minimal payload: `{ enabled: true | false }`. The helper function `getSavedState()` traverses the session branch via `ctx.sessionManager.getBranch()` to retrieve the most recent value.

Key design decisions in this mechanism:

- **Branch-based storage** – State travels with the conversation tree, surviving rewinds and replays
- **Minimal payload** – Only the boolean flag is stored; rules text is loaded from [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) on demand
- **Latest-wins semantics** – Multiple entries are possible; the function returns the most recent

## Automatic State Restoration

The skill restores its state on three critical lifecycle events: `session_start`, tree replay, and `session_compact`. This ensures rules remain active even after context compaction or connection recovery.

In [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) (lines 61-70), the `restoreState()` function:

1. Reads any saved `i-have-adhd-state` entry from the session branch
2. Falls back through a priority chain: saved state → `alwaysOn` config → `.i-have-adhd-always` sentinel file → default `false`
3. Synchronizes the computed `enabled` flag to UI status indicators
4. Calls `syncContext()` to align the model's visible context

```ts
pi.on("session_start", async (_event, ctx) => restoreState(ctx));
pi.on("session_compact", async (_event, ctx) => syncContext(ctx));

```

The sentinel file check allows users to enable ADHD mode persistently across all sessions by creating `.i-have-adhd-always` in their workspace.

## Context Synchronization and Rule Injection

The `syncContext()` function (lines 34-52) manages the actual presence of rules in the model's incoming messages. It uses `latestMarkerIsActive()` from [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) to detect whether rules are currently injected.

**Injection logic:**

- When **enabled and rules missing**: Sends a hidden custom message of type `i-have-adhd-rules` containing `RULES_HEADER` and the full rule text
- When **disabled but rules present**: Sends a removal marker of type `i-have-adhd-disabled`

```ts
if (enabled && !injected) {
  pi.sendMessage(
    {
      customType: RULES_MESSAGE_TYPE,
      content: `${RULES_HEADER}\n\n${rules}`,
      display: false,      // Hidden from user UI
    },
    { triggerTurn: false }, // Does not trigger model response
  );
}

```

The `display: false` and `triggerTurn: false` options are critical: they make the message invisible to users and prevent it from consuming a turn in the conversation flow. Yet the message persists in context for all subsequent model calls.

## Session-Wide Guarantees

Because rules are injected during context construction events—not per request—they survive across multiple turns without re-transmission. The persistence model handles:

- **Multi-turn conversations** – Rules remain in context indefinitely
- **Context compaction** – `syncContext()` re-injects rules after the context window is rebuilt
- **Tree navigation** – State entries on the active branch determine current mode
- **Connection recovery** – `session_start` restoration ensures continuity

The skill's own documentation in [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) (lines 15-20) confirms this behavior: rules "apply to every response for the rest of the session" until explicitly disabled via "stop adhd mode" or "normal mode" commands.

## Command Interface for Manual Control

Users interact with persistence through a registered command that manipulates the state machine:

```ts
pi.registerCommand("i-have-adhd", {
  description: "Toggle ADHD-friendly output for this session",
  handler: async (args, ctx) => {
    const arg = args.trim().toLowerCase();
    if (arg === "on") setEnabled(true, ctx);
    else if (arg === "off" || arg === "stop") setEnabled(false, ctx);
    else setEnabled(!enabled, ctx); // Toggle on bare command
  },
});

```

The `setEnabled()` function updates both the session state entry and triggers immediate context synchronization, ensuring the change takes effect on the next model interaction.

## Configuration-Driven Defaults

Optional file-based configuration influences persistence behavior without code changes. The [`i-have-adhd.json`](https://github.com/ayghri/i-have-adhd/blob/main/i-have-adhd.json) config file supports:

- **`alwaysOn`** – Default enablement for new sessions
- **`hideStatus`** – Suppress UI status indicators

The sentinel file `.i-have-adhd-always` provides a zero-config alternative for permanent activation.

## Summary

- **State storage** uses `i-have-adhd-state` entries in the session manager branch, retrieved via `getSavedState()`
- **Restoration** occurs automatically on `session_start` and compaction through `restoreState()`
- **Rule injection** happens via hidden `i-have-adhd-rules` messages only when missing from context, managed by `syncContext()`
- **Persistence** survives tree replays, compaction, and reconnection without user-visible re-sending
- **Manual control** through the `i-have-adhd` command and optional `alwaysOn` configuration or sentinel file

## Frequently Asked Questions

### Where is the ADHD mode state physically stored?

The state is stored as a custom entry in the runtime's session manager branch, accessed through `ctx.sessionManager.getBranch()`. This branch-based storage travels with the conversation tree and survives rewinds, unlike ephemeral memory.

### Do the rules get sent with every model request?

No. The rules are injected once as a hidden custom message during context construction (session start, replay, or compaction) and persist in the model's context window. The `triggerTurn: false` option prevents this injection from triggering a model response.

### What happens if I close and reopen my editor?

On the next session start, the `restoreState()` function reads any saved `i-have-adhd-state` entry from the session branch. If no state exists, it checks the `alwaysOn` configuration or `.i-have-adhd-always` sentinel file before defaulting to disabled.

### How do I permanently enable ADHD mode for all sessions?

Create a `.i-have-adhd-always` file in your workspace, or set `"alwaysOn": true` in [`i-have-adhd.json`](https://github.com/ayghri/i-have-adhd/blob/main/i-have-adhd.json). Either approach causes `restoreState()` to default to enabled when no explicit session state exists.