Documentation: 0.5.0 (source: v0.5.0).
# Plans

> Prepare, authorize, checkpoint, pause, and safely resume a non-atomic journey.

Markdown source of https://docs-v0-5-0.atlas-kit-docs.pages.dev/guides/plan-sequences/ — see https://docs-v0-5-0.atlas-kit-docs.pages.dev/ai/ for the full machine-readable surface.

A `PlanSequence` coordinates a journey that needs more than one confirmed
transaction. It is deliberately non-atomic: every step is one transaction, the
chain may advance between steps, and every freshly prepared Plan requires fresh
authorization. The sequence itinerary and each reached Plan description are
distinct authorization moments.

> **Runnable example — Read a fleet.** Walk from a profile to its character, then to the fleets that character owns. Run it in the browser at https://docs-v0-5-0.atlas-kit-docs.pages.dev/guides/plan-sequences/.

The embedded playground remains read-only. It supplies live Fleet context for
the guide without acquiring a signer or executing a sequence; the snippets
below exercise execution only against the repository's deterministic stub.

## Build only the itinerary

Construction records stable step identities but invokes no capability planner.
The `prepare` callback owns capability readiness and runs only when its step is
reached. It returns either a freshly prepared Plan or `waiting`; the planning
core does not interpret the reason or poll an advisory `notBefore` time.

```ts
import type { PlanAuthorization } from '@aephia/atlas-kit/fleets/actions';
import {
  planFleetDock,
  planFleetUndock,
} from '@aephia/atlas-kit/fleets/actions';
import { createPlanSequence } from '@aephia/atlas-kit/planning';

const authorization = {
  profile: fleet.ownerProfile.address,
  authority: authoritySigner.address,
  keyIndex: 0,
} satisfies PlanAuthorization;

const sequence = await createPlanSequence({
  cluster: 'zink-ptr',
  sequenceId: 'fleet-round-trip',
  revision: 0,
  steps: [
    {
      id: 'undock',
      prepare: async () => ({
        status: 'ready',
        plan: await planFleetUndock(ctx, fleet, { authorization }),
      }),
    },
    {
      id: 'dock',
      prepare: async ({ confirmed }) => {
        const current = await sage.fleets.get(fleet.address);
        if (confirmed.length !== 1 || current.state.kind !== 'idle') {
          return {
            status: 'waiting',
            reason: 'Wait until the confirmed undock is visible as idle.',
            notBefore: '2026-08-28T12:05:00.000Z',
          };
        }
        return {
          status: 'ready',
          plan: await planFleetDock(ctx, current, { authorization }),
        };
      },
    },
  ],
});

console.table(sequence.describe());
```

Show the itinerary before starting. Later, show each fresh Plan and its own
`describe()` output immediately before that Plan's signer boundary. Approval of
the itinerary is not blanket approval of later transactions.

## Persist waiting progress

Create the first checkpoint yourself and keep its JSON in caller-owned durable
storage. This deterministic example uses an in-memory string; a real
application must await its database or filesystem durability boundary.

```ts
import {
  createPlanSequence,
  createPlanSequenceCheckpoint,
  executePlanSequence,
  stringifyPlanSequenceCheckpoint,
} from '@aephia/atlas-kit/planning';

const waitingSequence = await createPlanSequence({
  cluster: 'zink-ptr',
  sequenceId: 'wait-for-capability',
  revision: 0,
  steps: [
    {
      id: 'ready-later',
      prepare: async () => ({
        status: 'waiting',
        reason: 'The capability is not ready yet.',
        notBefore: '2026-08-28T12:05:00.000Z',
      }),
    },
  ],
});

let checkpointJson = stringifyPlanSequenceCheckpoint(
  createPlanSequenceCheckpoint(waitingSequence, '2026-08-28T12:00:00.000Z'),
);
const waiting = await executePlanSequence(
  ctx,
  waitingSequence,
  checkpointJson,
  {
    feePayer: authoritySigner,
    store: {
      save: async (checkpoint) => {
        checkpointJson = stringifyPlanSequenceCheckpoint(checkpoint);
      },
    },
  },
);
console.log(waiting.status, waiting.status === 'waiting' && waiting.reason);
```

Capability readiness is not transaction confirmation. `notBefore` is advisory
capability context, not a timer the SDK follows. A checkpoint's `confirmed`
entries are chain confirmation evidence: each records a public signature, slot,
and `confirmed` or `finalized` commitment. `observedAt` is only the caller's
local observation time; its age never proves confirmation or permission to
continue.

## Pause, resume, and complete

An `AbortSignal` pauses only between transactions. The first call below is
already aborted, so it returns `paused` before preparing or signing. The second
call resumes from the persisted checkpoint, presents the fresh Plan, and
completes after one confirmed transaction.

```ts
import type { PlanAuthorization } from '@aephia/atlas-kit/fleets/actions';
import { planFleetUndock } from '@aephia/atlas-kit/fleets/actions';
import {
  createPlanSequence,
  createPlanSequenceCheckpoint,
  executePlanSequence,
  stringifyPlanSequenceCheckpoint,
} from '@aephia/atlas-kit/planning';

const authorization = {
  profile: fleet.ownerProfile.address,
  authority: authoritySigner.address,
  keyIndex: 0,
} satisfies PlanAuthorization;
const resumable = await createPlanSequence({
  cluster: 'zink-ptr',
  sequenceId: 'safe-undock',
  revision: 0,
  steps: [
    {
      id: 'undock',
      prepare: async () => ({
        status: 'ready',
        plan: await planFleetUndock(ctx, fleet, { authorization }),
      }),
    },
  ],
});

let checkpointJson = stringifyPlanSequenceCheckpoint(
  createPlanSequenceCheckpoint(resumable, '2026-08-28T12:00:00.000Z'),
);
const store = {
  save: async (
    checkpoint: Parameters<typeof stringifyPlanSequenceCheckpoint>[0],
  ) => {
    checkpointJson = stringifyPlanSequenceCheckpoint(checkpoint);
  },
};
const controller = new AbortController();
controller.abort();
const paused = await executePlanSequence(ctx, resumable, checkpointJson, {
  feePayer: authoritySigner,
  signal: controller.signal,
  store,
});
checkpointJson = stringifyPlanSequenceCheckpoint(paused.checkpoint);

const result = await executePlanSequence(ctx, resumable, checkpointJson, {
  feePayer: authoritySigner,
  store,
  onBeforeSign: async ({ plan, description }) => {
    console.log(plan.summary);
    console.table(description);
  },
});
if (result.status === 'completed') console.log(result.signature);
```

## Resume safety

Persist checkpoints, never signers or secrets. Do not persist a signer, wallet
callback, transaction, private key, or seed phrase. Never blindly retry, and do
not automatically resume after an unknown outcome, an identity mismatch, or
confirmation evidence that cannot be verified. Stop and reconcile the intended
game action and chain history first.

With an observable signer, the SDK can persist the public signature before
submission and reconcile it only after matching chain confirmation. An opaque
`TransactionSendingSigner` combines signing and sending; if that boundary is
interrupted, its durable `invoking` attempt has no public signature and remains
unresolved. The SDK cannot overstate recoverability where no observable
signature exists.

## Reference

- [`planning`](/reference/planning/) — exact sequence, checkpoint, and result types
- [Warp & subwarp](/guides/moving-a-fleet/) — one-transaction movement Plans
