# Agent guide - authoring, signals, and lifecycle

## Task and reading order

Use this topic when mounting Pibbl, integrating a host framework, debugging state resets, or managing imperative resources. First read [installation and authoring](/start/), [state and lifecycle](/guides/state-and-lifecycle/), and the exact [pibbl contract](/reference/functions/pibbl/). For an HTML-owned application, also read the [native-button integration](/guides/native-button/).

Do not infer compatibility from syntax resembling React. Pibbl components are synchronous functions returning Pibbl nodes. Pibbl and React have different elements, hooks, and JSX runtimes. Use one JSX runtime per file or explicit `createElement` at a mixed-runtime boundary. The [React host guide](/guides/react-integration/) shows the lifecycle split.

## Ownership and identity

The host owns the canvas DOM node, CSS, and the returned controller. Pibbl owns the live mount and its component/hook resources. Retain the controller and dispose it before the host is removed. Repeated `pibbl` calls on the same live canvas replace the root on the scheduler and return the same controller; they do not replace mount-time viewport, fit, density, or AbortSignal configuration.

Component identity depends on parent scope, function identity, key, and same-type occurrence. Define stable component functions outside the rendering function that uses them. Keys preserve same-parent reorder; they cannot preserve a component across reparenting. A changed function or key is a lifetime change, not a styling update. Call hooks in the same order and count every successful render.

## Choose the state primitive

| Requirement                    | Use                         | Avoid                                                |
| ------------------------------ | --------------------------- | ---------------------------------------------------- |
| Host-owned writable state      | `signal`                    | Recreating it on every host update                   |
| Component-owned writable state | `useSignal`                 | Writing while rendering                              |
| Derived state                  | `computed` or `useComputed` | A reaction that writes another signal                |
| Imperative synchronization     | `useReaction` with cleanup  | Treating it as a React effect that may write signals |
| Mount-lifetime construction    | `useConst`                  | Assuming it owns cleanup or tracks reads             |
| Mutable session bookkeeping    | `useRef`                    | Expecting mutation to schedule rendering             |

Reads are explicit. Passing a signal to a declared reactive input makes the receiver its consumer. Calling `.get()` in your component makes that component the consumer. There is no recursive unwrapping of arbitrary application objects. `set` treats a function as data; use `update` for functional updates. `batch` groups synchronous notification waves.

## Scheduling and failure

The shared scheduler runs Begin, Advance, Commit, Plan, Render, and Complete. Reactions run in Complete after successful rendering. Signal reads in a reaction belong to it; its cleanup runs before replacement or release. Setup and cleanup cannot write signals. Component, primitive, and computed evaluation also forbid writes, including equality no-ops. `untracked` does not bypass these guards.

Failed evaluation preserves the previous successful dependency generation and releases provisional resources. This does not promise pixel rollback: immediate Canvas operations already performed may remain visible. Do not use a thrown render as an application transaction.

## Complete example and adaptation

The source below is the existing Signals lesson. Compile it with `jsx: "react-jsx"` and `jsxImportSource: "@pibbl/core"`, attach a canvas with width 720 and height 420, then call its default `mount(canvas)`. Keep its returned controller. The lesson's module-level signals deliberately outlive one mount; move state into `useSignal` when each instance should start independently.

[Open the Signals lesson](/playground/#/workbench/signals) in the playground. For the underlying API, use [signal](/reference/functions/signal/), [useSignal](/reference/hooks/use-signal/), [computed](/reference/functions/computed/), and [useReaction](/reference/hooks/use-reaction/).

## Verification

1. Mount once, activate the example, and assert its displayed state changes after a scheduled frame.
2. Rerender with the same function/key and confirm state is retained. Change the key and confirm a new lifetime.
3. Reorder keyed siblings within the same parent and confirm state follows keys. Treat reparenting as a remount.
4. Mount, remove, and remount repeatedly. Check owned listeners/observers are released and the old controller cannot affect the replacement.
5. Test a failed setup or render and confirm external resources are released. Inspect semantic state before pixels.

When reporting results, distinguish a code typecheck from a browser run, and include package version, source revision, action, and observed state. Do not claim teardown from a screenshot or from heap timing alone.

## Complete source

Host setup for this source: use a canvas with width 720 and height 420, compile with `jsx: "react-jsx"` and `jsxImportSource: "@pibbl/core"`, import its default `mount`, and call `const controller = mount(canvas)` after attaching the canvas. Call `controller.dispose()` before removing it.

### signals.example.tsx

```tsx
import {
  computed,
  pibbl,
  useSignal,
  Rectangle,
  Text,
  signal,
  type PibblController,
} from "@pibbl/core";

const x = signal(144, { debugName: "learning x" });
const doubledX = computed(() => x.get() * 2, { debugName: "learning doubled x" });
const signalSummary = computed(
  () => `x ${x.get()} · computed ${doubledX.get()}`,
  { debugName: "learning signal summary" },
);

function SignalCard() {
  const color = useSignal("#2563eb", { debugName: "learning color" });

  return [
    <Rectangle style={{ width: 720, height: 420, fill: "#eef2ff" }} />,
    <Rectangle
      style={{
        left: x,
        top: 126,
        width: 432,
        height: 174,
        fill: color,
        stroke: "#17211d",
        strokeWidth: 6,
        cursor: "pointer",
      }}
      onClick={() => {
        x.update((value) => (value === 144 ? 196 : 144));
      }}
    />,
    <Text
      pointerEvents="none"
      style={{
        left: 360,
        top: 180,
        fill: "#ffffff",
        font: "900 28px sans-serif",
        textAlign: "center",
      }}
    >
      SIGNALS FLOW TO RECEIVERS
    </Text>,
    <Text
      pointerEvents="none"
      style={{
        left: 360,
        top: 222,
        fill: "#f7c948",
        font: "700 15px monospace",
        textAlign: "center",
      }}
    >
      {signalSummary}
    </Text>,
    <Text
      pointerEvents="none"
      style={{
        left: 360,
        top: 264,
        fill: "#ffffff",
        font: "600 13px monospace",
        textAlign: "center",
      }}
    >
      CLICK TO UPDATE THE WRITABLE SIGNAL
    </Text>,
  ];
}

export default function mount(canvas: HTMLCanvasElement): PibblController {
  return pibbl(canvas, <SignalCard />);
}

```

## Documentation version

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.
