# Keyed Slot

> For agents: start with the [agent guide](https://pi-tui.ratstack.sh/llms.txt). Every page here has a Markdown twin, and every pattern has a JSON record.

Structural / Status and widgets · `keyed-slot` · [HTML](https://pi-tui.ratstack.sh/patterns/keyed-slot/) · [JSON](https://pi-tui.ratstack.sh/patterns/keyed-slot.json) · [all patterns](https://pi-tui.ratstack.sh/patterns.md)

Also known as Keyed status slot.

## Intent

Publish and clear compact state under one stable footer key.

## Motivation

Nico's status helpers need to clear one operation without clearing concurrent footer signals.

## Applicability

- Use this when an operation's state fits in one short label.

## Structure

```text
owner -> setStatus(key, text)
end -> setStatus(key, undefined)
```

## Participants

- [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Writes or clears one named footer slot.
- `Slot owner`: Chooses a stable key and clears it when its state ends.

Pi screen APIs: [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user)

## Consequences

- Stable keys isolate compact status ownership.
- Every lifecycle exit must clear its own key.

## Implementation

- Clear the same key when its state ends.
- Clearing one key does not clear other operations.
- Reject updates from stale contexts.

## States

Each state was rendered at 40, 60, 80 and 120 columns in Pi's dark and light themes. Each frame links one WebP. An animated frame links its first image, then the animation.

### Owned status

`active`: Show two independent synthetic operation labels.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Keyed Slot, Owned status, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/keyed-slot/active-40-dark.af1dd3ad52b9.webp) ![Keyed Slot, Owned status, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/keyed-slot/active-40-light.88bb4a6eb7d8.webp)
- 60 columns: ![Keyed Slot, Owned status, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/keyed-slot/active-60-dark.1af2e5deade2.webp) ![Keyed Slot, Owned status, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/keyed-slot/active-60-light.4c87dbdc754a.webp)
- 80 columns: ![Keyed Slot, Owned status, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/keyed-slot/active-80-dark.be59cade8c8d.webp) ![Keyed Slot, Owned status, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/keyed-slot/active-80-light.61b13fa2646f.webp)
- 120 columns: ![Keyed Slot, Owned status, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/keyed-slot/active-120-dark.e2c016440b01.webp) ![Keyed Slot, Owned status, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/keyed-slot/active-120-light.3c75f32564c2.webp)

### One slot cleared

`cleared`: Remove one owned key while the other remains.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Keyed Slot, One slot cleared, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/keyed-slot/cleared-40-dark.f17d649dc2d2.webp) ![Keyed Slot, One slot cleared, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/keyed-slot/cleared-40-light.d032968a6bc5.webp)
- 60 columns: ![Keyed Slot, One slot cleared, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/keyed-slot/cleared-60-dark.ba58bc287194.webp) ![Keyed Slot, One slot cleared, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/keyed-slot/cleared-60-light.67654098c67b.webp)
- 80 columns: ![Keyed Slot, One slot cleared, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/keyed-slot/cleared-80-dark.2ef742545372.webp) ![Keyed Slot, One slot cleared, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/keyed-slot/cleared-80-light.fef45355610f.webp)
- 120 columns: ![Keyed Slot, One slot cleared, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/keyed-slot/cleared-120-dark.b952dd837fe4.webp) ![Keyed Slot, One slot cleared, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/keyed-slot/cleared-120-light.3360985e3f36.webp)

## Sample code

`stories/patterns/structural/keyed-slot.ts`, the story the frames above were rendered from.

```ts
// Keyed Slot: clear one stable footer key without clearing another operation.
import type { PatternStory } from "../../../src/pattern.ts";

export const story: PatternStory = {
  id: "keyed-slot", title: "Keyed Slot", kind: "screen",
  apis: ["ctx.ui.setStatus"],
  states: [
    { id: "active", label: "Owned status" },
    { id: "cleared", label: "One slot cleared", steps: [{ type: "action", name: "clear-lint" }] },
  ],
  setup({ ui, theme, action }) {
    const lintKey = "lint";
    const runKey = "run";
    // Two owners can publish to the footer at once.
    const showLint = () => ui.setStatus(lintKey, theme.fg("warning", "2/3"));
    const showRun = () => ui.setStatus(runKey, theme.fg("success", "ready"));
    const clearLint = () => ui.setStatus(lintKey, undefined);
    const clearRun = () => ui.setStatus(runKey, undefined);

    ui.setEditorText("Review src/tasks.ts");
    ui.setWorkingMessage("Checking sample code");
    showLint();
    showRun();
    action("clear-lint", () => {
      clearLint();
      // The independent run slot remains ready.
      ui.setWorkingMessage("");
      ui.notify("Sample-code check complete");
    });

    // All lifecycle exits clear precisely the keys this example owns.
    return () => {
      clearLint();
      clearRun();
      ui.setWorkingMessage("");
    };
  },
};
```

## Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Own and clear a named status slot
  - [extensions/rp-native-tools-lock/index.ts:105-118](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/rp-native-tools-lock/index.ts#L105-L118) @17cce138
  - [extensions/ephemeral-mode.ts:34-70](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/ephemeral-mode.ts#L34-L70) @17cce138
  - [extensions/pi-codex-goal/src/goal-runtime-status.ts:1-15](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/pi-codex-goal/src/goal-runtime-status.ts#L1-L15) @17cce138
  - [extensions/stash/index.ts:34-72](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/stash/index.ts#L34-L72) @17cce138
  - [extensions/poly-notify/index.ts:315-335](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/poly-notify/index.ts#L315-L335) @17cce138
- [**pi-annotate**](https://github.com/nicobailon/pi-annotate): Own and clear a named status slot
  - [index.ts:54-57](https://github.com/nicobailon/pi-annotate/blob/cebb68deb28dcc6422d2846a2a36e5e20ca31725/index.ts#L54-L57) @cebb68de
- [**pi-boomerang**](https://github.com/nicobailon/pi-boomerang): Own and clear a named status slot
  - [index.ts:1422-1445](https://github.com/nicobailon/pi-boomerang/blob/1a5985b2d92cfa84ce1f470d100d02b368711a91/index.ts#L1422-L1445) @1a5985b2
  - [index.ts:1155-1170](https://github.com/nicobailon/pi-boomerang/blob/1a5985b2d92cfa84ce1f470d100d02b368711a91/index.ts#L1155-L1170) @1a5985b2
- [**pi-model-switch**](https://github.com/nicobailon/pi-model-switch): Own and clear a named status slot
  - [index.ts:202-205](https://github.com/nicobailon/pi-model-switch/blob/b254ece4fe90d34938fdd69c975379041dce9c53/index.ts#L202-L205) @b254ece4
- [**pi-prompt-template-model**](https://github.com/nicobailon/pi-prompt-template-model): Own and clear a named status slot
  - [index.ts:784-797](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/index.ts#L784-L797) @6da20591
  - [index.ts:1578-1590](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/index.ts#L1578-L1590) @6da20591
  - [index.ts:1757-1765](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/index.ts#L1757-L1765) @6da20591
- [**pi-review-loop**](https://github.com/nicobailon/pi-review-loop): Own and clear a named status slot
  - [index.ts:16-22](https://github.com/nicobailon/pi-review-loop/blob/878d1a50ceff4ae6d76b99ea0a715a4b2ba28a11/index.ts#L16-L22) @878d1a50
- [**pi-rewind-hook**](https://github.com/nicobailon/pi-rewind-hook): Own and clear a named status slot
  - [index.ts:440-450](https://github.com/nicobailon/pi-rewind-hook/blob/a62e7c2c89d130b3a02f7f799c15de62743d3fa8/index.ts#L440-L450) @a62e7c2c
- [**dot314**](https://github.com/nicobailon/dot314): Upstream-derived preset picker and status
  - [extensions/preset.ts:1-18](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/preset.ts#L1-L18) @17cce138

## For agents: choose and check

Choose Keyed Slot when your job matches its intent and applicability above. Its neighbours in Status and widgets are listed below. Read the one whose intent fits your job more closely before you commit.

- [Signal Pair](https://pi-tui.ratstack.sh/patterns/signal-pair.md): Pair a compact footer signal with a structured near-editor widget.
- [Widget Dock](https://pi-tui.ratstack.sh/patterns/widget-dock.md): Mount and replace auxiliary content under a stable widget key.
- [Result Relay](https://pi-tui.ratstack.sh/patterns/result-relay.md): Replace pending feedback with a persistent non-modal result widget.
- [Notice Fuse](https://pi-tui.ratstack.sh/patterns/notice-fuse.md): Clear a one-off keyed notice after its display interval.
- [Work Caption](https://pi-tui.ratstack.sh/patterns/work-caption.md): Set task-specific text in Pi's active working indicator.

Check your version:

- Render your version at 40, 60, 80 and 120 columns in the dark and light themes.
- Check width: no rendered line is wider than the terminal.
- Check style-leak: no line ends with colour, bold or a link still switched on.
- Check hard-coded-colour: every colour on screen comes from the active theme.
- Check height: the output fits in the rows the terminal has.
- Read the Pi 1.0.3 docs for [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user) before you use them.
- Every reference frame gets the verdict its state expects.

Next actions: `related({ id: "keyed-slot" })` lists what to read next, and `states({ id: "keyed-slot", width: 40 })` returns every frame and verdict at one width. The [build-pi-tui-component skill](https://pi-tui.ratstack.sh/skills/build-pi-tui-component.md) walks through the whole loop.

## Related

- [Notice Fuse](https://pi-tui.ratstack.sh/patterns/notice-fuse.md): adds a deadline to the slot

## Linked from

- [Status Ribbon](https://pi-tui.ratstack.sh/patterns/status-ribbon.md): supplies keyed footer signals
