# Signal Pair

> 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 · `signal-pair` · [HTML](https://pi-tui.ratstack.sh/patterns/signal-pair/) · [JSON](https://pi-tui.ratstack.sh/patterns/signal-pair.json) · [all patterns](https://pi-tui.ratstack.sh/patterns.md)

Also known as Status-widget pair.

## Intent

Pair a compact footer signal with a structured near-editor widget.

## Motivation

Memory Workbench and other extensions need a short status plus a larger task display.

## Applicability

- Use this when progress needs both a short signal and a task display.

## Structure

```text
operation -> status key
operation -> widget key
end -> clear both
```

## 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.
- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- `Paired keys`: Identify the compact signal and its structured detail.

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), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `Text`

## Consequences

- A compact signal can coexist with structured detail.
- Two owned keys require paired cleanup.

## Implementation

- Clear both owned keys when the operation ends.
- Guard terminal widget factories with TUI mode.

## 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.

### Signal and detail

`active`: Show a short running status and a small task widget.

Checks at every width and theme:

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

Frames:

- 40 columns: ![Signal Pair, Signal and detail, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/signal-pair/active-40-dark.9e4b62c2f9fc.webp) ![Signal Pair, Signal and detail, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/signal-pair/active-40-light.35ab3ebcdc36.webp)
- 60 columns: ![Signal Pair, Signal and detail, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/signal-pair/active-60-dark.235d6eb2290f.webp) ![Signal Pair, Signal and detail, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/signal-pair/active-60-light.0aa55c3466f3.webp)
- 80 columns: ![Signal Pair, Signal and detail, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/signal-pair/active-80-dark.0bd9f41f291a.webp) ![Signal Pair, Signal and detail, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/signal-pair/active-80-light.7e1d64bcd73c.webp)
- 120 columns: ![Signal Pair, Signal and detail, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/signal-pair/active-120-dark.e616c1b4f409.webp) ![Signal Pair, Signal and detail, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/signal-pair/active-120-light.d43b72eebe40.webp)

### Paired cleanup

`finished`: Clear both surfaces when the synthetic task ends.

Checks at every width and theme:

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

Frames:

- 40 columns: ![Signal Pair, Paired cleanup, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/signal-pair/finished-40-dark.3e2c10b04190.webp) ![Signal Pair, Paired cleanup, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/signal-pair/finished-40-light.cf5a7d62e15b.webp)
- 60 columns: ![Signal Pair, Paired cleanup, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/signal-pair/finished-60-dark.c08c0b2366da.webp) ![Signal Pair, Paired cleanup, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/signal-pair/finished-60-light.769ece0eea99.webp)
- 80 columns: ![Signal Pair, Paired cleanup, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/signal-pair/finished-80-dark.9ab1e2bbc7c6.webp) ![Signal Pair, Paired cleanup, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/signal-pair/finished-80-light.94c46febc1e4.webp)
- 120 columns: ![Signal Pair, Paired cleanup, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/signal-pair/finished-120-dark.c3d80129b1b7.webp) ![Signal Pair, Paired cleanup, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/signal-pair/finished-120-light.0a0661633e58.webp)

## Sample code

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

```ts
// Signal Pair: publish compact progress and structured detail, then clear both owned keys.
import { Text } from "@earendil-works/pi-tui";
import type { PatternStory } from "../../../src/pattern.ts";

export const story: PatternStory = {
  id: "signal-pair", title: "Signal Pair", kind: "screen",
  apis: ["ctx.ui.setStatus", "ctx.ui.setWidget", "Text"],
  states: [
    { id: "active", label: "Signal and detail" },
    { id: "finished", label: "Paired cleanup", steps: [{ type: "action", name: "finish" }] },
  ],
  setup({ ui, theme, action }) {
    const statusKey = "preview";
    const widgetKey = "preview-tasks";
    const showProgress = () => {
      ui.setStatus(statusKey, theme.fg("warning", "2/3"));
      ui.setWidget(widgetKey, (_tui, widgetTheme) => new Text([
        widgetTheme.fg("accent", "Task preview · 2/3"),
        widgetTheme.fg("success", "✓ Measure display columns"),
        widgetTheme.fg("success", "✓ Render dark frame"),
        widgetTheme.fg("warning", "> Render light frame"),
      ].join("\n"), 0, 0));
    };
    const clearProgress = () => {
      ui.setStatus(statusKey, undefined);
      ui.setWidget(widgetKey, undefined);
    };

    ui.setEditorText("Review the completed preview");
    showProgress();
    action("finish", () => {
      clearProgress();
      ui.notify("Preview ready · all 3 tasks complete");
    });
    return clearProgress;
  },
};
```

## Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Separate compact status from structured detail
  - [extensions/plan-mode.ts:575-590](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/plan-mode.ts#L575-L590) @17cce138
  - [extensions/pi-codex-goal/src/goal-runtime-status.ts:42-63](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/pi-codex-goal/src/goal-runtime-status.ts#L42-L63) @17cce138
  - [extensions/plan-mode.ts:1-18](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/plan-mode.ts#L1-L18) @17cce138
- [**pi-custom-compaction**](https://github.com/nicobailon/pi-custom-compaction): Separate compact status from structured detail
  - [runtime/session-state.ts:49-78](https://github.com/nicobailon/pi-custom-compaction/blob/a0e4700badb1c5c1c2dd12eeb250ff067fa67b7e/runtime/session-state.ts#L49-L78) @a0e4700b
  - [runtime/session-state.ts:105-183](https://github.com/nicobailon/pi-custom-compaction/blob/a0e4700badb1c5c1c2dd12eeb250ff067fa67b7e/runtime/session-state.ts#L105-L183) @a0e4700b
- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Separate compact status from structured detail
  - [ralph-wiggum/index.ts:190-215](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/ralph-wiggum/index.ts#L190-L215) @bca5070b
- [**pi-memory-workbench**](https://github.com/nicobailon/pi-memory-workbench): Separate compact status from structured detail
  - [index.ts:72-82](https://github.com/nicobailon/pi-memory-workbench/blob/92b4c9c3ad07841418d77118bf8bd02ad204f7c4/index.ts#L72-L82) @92b4c9c3
- [**pi-messenger**](https://github.com/nicobailon/pi-messenger): Separate compact status from structured detail
  - [index.ts:295-305](https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/index.ts#L295-L305) @09937ed6

## For agents: choose and check

Choose Signal Pair 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.

- [Keyed Slot](https://pi-tui.ratstack.sh/patterns/keyed-slot.md): Publish and clear compact state under one stable footer key.
- [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), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `Text` before you use them.
- Every reference frame gets the verdict its state expects.

Next actions: `related({ id: "signal-pair" })` lists what to read next, and `states({ id: "signal-pair", 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

- [Widget Dock](https://pi-tui.ratstack.sh/patterns/widget-dock.md): provides the detail surface

## Linked from

- [Result Relay](https://pi-tui.ratstack.sh/patterns/result-relay.md): pairs compact and detailed signals
- [Snapshot Lens](https://pi-tui.ratstack.sh/patterns/snapshot-lens.md): provides the two display surfaces
