# How the i‑have‑adhd Plugin Stores Conversation State: Session Manager Persistence Explained

> Discover how the i-have-adhd plugin stores conversation state. Learn about session manager persistence and how the ADHD mode survives restarts.

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

---

**The i‑have‑adhd plugin stores conversation state by persisting an ADHD‑mode flag as a custom entry type `"i-have-adhd-state"` in the session manager, allowing the mode to survive session restarts and tree reconstructions.**

The `ayghri/i-have-adhd` extension for Pi (the AI platform) needs to remember whether a user has enabled ADHD‑friendly output across multiple turns of a conversation. Rather than keeping this state only in memory, the plugin implements a robust persistence mechanism that writes directly to the session manager's branch data structure.

## Where State Is Stored: The Session Manager Branch

The core storage mechanism relies on `pi.appendEntry`, a session manager API that appends custom metadata entries to the conversation branch. In [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts), the extension defines a dedicated custom entry type for this purpose.

When a user toggles ADHD mode, the plugin calls `appendEntry` with:

- Entry type: `"i-have-adhd-state"`
- Payload: an object containing the `enabled` boolean

```ts
// Persist the new state
function setEnabled(nextEnabled: boolean, ctx: ExtensionContext) {
  enabled = nextEnabled;
  // Store in session manager – this is the persistent state
  pi.appendEntry("i-have-adhd-state", { enabled } satisfies AdhdModeState);
  updateStatus(ctx);
  syncContext(ctx);
}

```

This write operation occurs at lines 55–58 of the main extension file, ensuring the flag becomes part of the durable conversation record.

## Retrieving State on Session Start or Rebuild

The plugin must recover this state whenever a conversation resumes or the tree structure changes. The `getSavedState` function in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) handles this by scanning the entire session branch:

```ts
// Retrieve the last saved state when a session starts
function getSavedState(ctx: ExtensionContext): boolean | undefined {
  let savedState: boolean | undefined;
  for (const entry of ctx.sessionManager.getBranch()) {
    if (entry.type !== "custom" || entry.customType !== "i-have-adhd-state")
      continue;
    const data = entry.data as Partial<AdhdModeState> | undefined;
    if (typeof data?.enabled === "boolean") savedState = data.enabled;
  }
  return savedState;
}

```

This iteration strategy (lines 66–80) intentionally processes the entire branch and keeps the **last** valid entry, ensuring that more recent toggles override earlier ones. The function returns `undefined` when no state entry exists, triggering fallback behavior.

## Fallback Behavior and "Always-On" Configuration

When `getSavedState` returns `undefined`, the plugin checks for an alternative signal: the presence of an "always-on" marker file in the conversation context (lines 45–49). This allows administrators or template creators to pre-enable ADHD mode without requiring an explicit user toggle.

The fallback chain operates as:

1. Last explicit `"i-have-adhd-state"` entry in the branch
2. Always-on marker detection via `latestMarkerIsActive`
3. Default disabled state

## Syncing Rules Based on Retrieved State

Once the persisted state is determined, `syncContext` executes the actual behavioral change. This helper either injects the ADHD‑friendly rule set from [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) into the model context or removes it, depending on the `enabled` flag value.

The separation between **persistence** (`setEnabled`/`getSavedState`) and **application** (`syncContext`) creates clean architectural boundaries:
- State storage knows nothing about model prompts
- Rule injection knows nothing about where the flag came from

## User-Facing Toggle Command

State changes originate from the slash command handler registered at lines 22–24:

```ts
// Toggle the mode (e.g. via the slash command)
pi.registerCommand("i-have-adhd", {
  description: "Toggle ADHD‑friendly output for this session",
  handler: async (args, ctx) => {
    const arg = args.trim().toLowerCase();
    if (arg === "" ) setEnabled(!enabled, ctx);      // flip
    else if (arg === "on") setEnabled(true, ctx);   // enable
    else if (arg === "off") setEnabled(false, ctx); // disable
  },
});

```

Each execution path ultimately triggers `setEnabled`, which pers persists the new value before updating the status bar and syncing context rules.

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) | Defines custom entry type `"i-have-adhd-state"`; implements `setEnabled`, `getSavedState`, and `syncContext` |
| [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) | Provides `contextMessages` and `latestMarkerIsActive` utilities for rule presence detection |
| [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) | Contains the actual ADHD‑friendly rule set injected when mode is enabled |

## Summary

- **Storage mechanism**: Custom session manager entries via `pi.appendEntry("i-have-adhd-state", { enabled })`
- **Retrieval method**: Full branch scan in `getSavedState` extracting the last matching entry
- **Durability**: Survives session restarts, tree reconstructions, and conversation compactions
- **Fallbacks**: Supports always-on marker files and defaults to disabled
- **Architecture**: Clean separation between state persistence and rule application

## Frequently Asked Questions

### Does the i‑have‑adhd plugin store state in local files or browser storage?

No. According to the `ayghri/i-have-adhd` source code, state is stored in the session manager's branch structure, not local files or browser storage. The `pi.appendEntry` API writes to the conversation's persistent metadata, which is synchronized across devices through the platform's existing session infrastructure.

### What happens if multiple ADHD state entries exist in the same conversation branch?

The `getSavedState` function intentionally iterates through the entire branch and keeps the last valid entry it finds. More recent toggles automatically override earlier ones, creating a natural history trail while ensuring current preference takes precedence.

### Can ADHD mode be enabled by default without user interaction?

Yes. The plugin checks for an "always-on" marker via `latestMarkerIsActive` in [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) when no explicit state entry exists. This allows conversation templates or system configurations to pre-enable the mode before any user command.

### Why scan the entire branch instead of reading the most recent entry directly?

The session manager API exposes `getBranch()` as an iterable sequence without indexed access to custom entry types. The linear scan approach (lines 66–80 of [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts)) handles edge cases like entry compaction or metadata interleaving while maintaining predictable O(n) performance for typical conversation lengths.