Dynamic TUI Sequence Design
Summary
pi-agenticoding adds a transient-looking but session-backed sequence recorder. Users define a sequence interactively in the TUI by entering steps through a prompt-like editor. The extension captures raw user-authored steps, validates them immediately, previews them exactly as typed, and runs the sequence for a fixed number of iterations.
Sequences are not config resources and are not project/global workflows. They are persisted as extension-owned custom entries on pi's session tree so the latest sequence can be restored after reload, resume, and session replacement. Restore is branch-aware: the current branch is authoritative.
Between iterations, the runner uses ctx.newSession() so notebook, readonly, handoff, child sessions, topic, and watchdog state reset normally. Only serialized sequence run progress crosses the reset boundary.
Scope
Supported
- TUI sequence recording mode.
- Record, validate, preview, edit, and run a sequence.
- Restore the latest sequence snapshot from session history.
- Fixed run count, asked at run start.
- Free-text prompt steps.
- pi-agenticoding commands:
/readonly, /handoff, /notebook.
- Prompt templates and skills when they can be resolved from
pi.getCommands().
/compact through ctx.compact().
- Full reset between iterations through
ctx.newSession().
Not supported
- Durable project/global sequence storage.
- Third-party extension command execution.
- UI-only built-ins such as
/settings, /tree, /model.
- Literal
/new as a user-authored step. Reset is runner-owned.
- Replaying pre-expanded template or skill text from stored sequence state.
Persistence model
Sequences use pi's session tree as the durable source of truth.
- Persist sequence snapshots as extension-owned
custom entries.
- Rehydrate in-memory sequence state on
session_start by scanning the current branch newest-to-oldest.
- The latest snapshot on the current branch wins.
- Sequence persistence is session-scoped, not process-scoped, and not config-scoped.
- Sequence entries do not enter LLM context.
Suggested custom entry shape:
interface SequenceSnapshotEntry {
version: 1;
sequenceId: string;
steps: string[];
updatedAt: number;
}
Restore is branch-only in v1: always use the latest snapshot on the current branch.
User experience
Start:
If a previous sequence snapshot exists on the current branch:
Restore last sequence?
- Run
- Edit
- Start new
- Cancel
Recording mode replaces the editor with a sequence recorder that preserves prompt-like editing and autocomplete where possible.
Footer/status:
● recording sequence · 3 steps · Enter adds step · Ctrl+Enter finishes · Esc cancels
Preview widget:
Sequence
1. /readonly
2. Plan the implementation for $@
3. /handoff execution
Preview always shows the raw authored step text. Slash commands remain visible and unexpanded so the sequence stays readable and understandable in the TUI.
On finish:
Run this sequence how many times? [1]
During execution:
⟳ sequence · run 2/5 · step 3/4 · /handoff execution
Unsupported entries fail visibly during recording:
Unsupported step: /publish is an external extension command.
pi-agenticoding cannot execute third-party extension command handlers.
Data model
Canonical persisted model
Persist only raw user-authored steps.
interface RecordedSequence {
version: 1;
id: string;
steps: string[]; // exact authored input, one step per entry
updatedAt: number;
}
Raw step text is the only sequence source of truth because command/template/skill expansion can change between runs, reloads, and replacement sessions.
Derived resolved model
Resolve raw steps into typed runtime steps during validation and execution.
type ResolvedStep =
| { kind: "prompt"; raw: string; text: string }
| { kind: "agenticodingCommand"; raw: string; name: "readonly" | "handoff" | "notebook"; args: string }
| { kind: "template"; raw: string; name: string; args: string; sourcePath: string }
| { kind: "skill"; raw: string; name: string; args: string; sourcePath: string }
| { kind: "compact"; raw: string; instructions?: string };
Rules:
- Preview uses
raw.
- Execution uses freshly resolved data.
- Templates and skills are never persisted in expanded form.
- Unsupported classes are rejected during recording and not stored unless rewritten as plain prompts.
Transient run progress
Only serialized run progress crosses ctx.newSession() boundaries.
interface SequenceRun {
id: string;
sequenceId: string;
steps: string[];
args: string[];
totalRuns: number;
runIndex: number;
stepIndex: number;
}
State design
Sequence state must be centralized inside the extension state, not spread across ad hoc module singletons.
interface SequenceState {
lastRecorded: RecordedSequence | null; // rehydrated from custom entries
activeRun: SequenceRun | null; // transient, cleared when run finishes
}
Guidelines:
- Session history is the durable source of truth.
- In-memory state is a cache of the latest rehydrated snapshot plus active transient execution state.
- Avoid storing
livePi, liveState, or similar mutable runtime mirrors as primary architecture.
- If execution needs fresh session-bound capabilities after
ctx.newSession(), reacquire them from the replacement-session context instead of caching old ones.
Rehydration
On session_start:
- Read the current branch through
ctx.sessionManager.getBranch().
- Scan newest-to-oldest for sequence snapshot custom entries.
- Rebuild
SequenceState.lastRecorded from the newest valid snapshot.
- Clear stale in-memory sequence state when no persisted snapshot exists on the branch.
This follows the same event-sourced persistence pattern already used for notebook state.
Validation
Validation is two-phase.
Recording-time validation
Each entered step is validated immediately.
Allowed:
- Plain prompt text.
/readonly, /handoff, /notebook.
/compact.
- Prompt templates from
pi.getCommands() where source === "prompt".
- Skills from
pi.getCommands() where source === "skill".
Rejected:
- Third-party extension commands.
- Interactive-only built-ins.
/new.
- Any unresolved or malformed slash command.
Runtime validation
Revalidate before execution, and re-resolve before each iteration.
Why:
- templates/skills may change on disk
- available commands may change after reload or session replacement
- command provenance may differ between runs
pi.getCommands() is the canonical source of command metadata. Use source and sourceInfo instead of command-name heuristics or path guessing.
Execution rules
- Validate and resolve the complete sequence before starting a run.
- Execute steps sequentially.
- Before each iteration, resolve raw steps again from current command metadata.
- After prompt-like steps, wait for idle.
/readonly, /handoff, and /notebook call shared pi-agenticoding command functions.
/compact wraps ctx.compact() callbacks in a Promise.
- After the final step of an iteration, call
ctx.newSession({ withSession }) if more iterations remain.
- Continue only through the fresh
ReplacedSessionContext passed to withSession.
- Carry only
SequenceRun data across reset.
- Abort visibly if a previously valid step can no longer be resolved.
Session replacement rules
ctx.newSession() is a hard session-boundary.
- The old command
ctx, old pi, old sessionManager, and any previously captured session-bound objects are stale after replacement.
withSession runs only after the replacement session is active and the new extension instance has already started.
- Code after replacement must use only the fresh
ctx passed to withSession.
- Capture only plain serialized data across the boundary, such as
SequenceRun.
This avoids stale-context bugs and keeps sequence iteration reset semantics aligned with normal pi behavior.
Command refactor
Slash handlers and sequences must share behavior:
/readonly -> runReadonlyToggle(ctx, state)
/handoff -> requestHandoff(ctx, state, direction)
/notebook -> runNotebookCommand(ctx, state, args)
/sequence -> runSequence(ctx, args)
Sequence execution must call the same shared command functions used by manual slash invocation. Do not synthesize command execution by sending /cmd text back through the prompt path.
Audit checklist
- No pi internals imported or patched.
- No fake command execution via
pi.sendUserMessage("/cmd").
- Sequence snapshots persist through custom session entries only.
- Rehydration restores the latest sequence snapshot from the current branch.
- Preview shows raw authored steps, not expanded template/skill content.
- Runtime always revalidates dynamic commands before execution.
- No stale
ctx, pi, sessionManager, or cached session-bound objects survive ctx.newSession().
- Unsupported commands fail before execution.
- Notebook reset semantics remain untouched.
- Failures are visible through recorder UI/status notifications.
Sequence snapshots remain invisible implementation details in session history and are not labeled for /tree inspection.
Dynamic TUI Sequence Design
Summary
pi-agenticoding adds a transient-looking but session-backed sequence recorder. Users define a sequence interactively in the TUI by entering steps through a prompt-like editor. The extension captures raw user-authored steps, validates them immediately, previews them exactly as typed, and runs the sequence for a fixed number of iterations.
Sequences are not config resources and are not project/global workflows. They are persisted as extension-owned custom entries on pi's session tree so the latest sequence can be restored after reload, resume, and session replacement. Restore is branch-aware: the current branch is authoritative.
Between iterations, the runner uses
ctx.newSession()so notebook, readonly, handoff, child sessions, topic, and watchdog state reset normally. Only serialized sequence run progress crosses the reset boundary.Scope
Supported
/readonly,/handoff,/notebook.pi.getCommands()./compactthroughctx.compact().ctx.newSession().Not supported
/settings,/tree,/model./newas a user-authored step. Reset is runner-owned.Persistence model
Sequences use pi's session tree as the durable source of truth.
customentries.session_startby scanning the current branch newest-to-oldest.Suggested custom entry shape:
Restore is branch-only in v1: always use the latest snapshot on the current branch.
User experience
Start:
If a previous sequence snapshot exists on the current branch:
Recording mode replaces the editor with a sequence recorder that preserves prompt-like editing and autocomplete where possible.
Footer/status:
Preview widget:
Preview always shows the raw authored step text. Slash commands remain visible and unexpanded so the sequence stays readable and understandable in the TUI.
On finish:
During execution:
Unsupported entries fail visibly during recording:
Data model
Canonical persisted model
Persist only raw user-authored steps.
Raw step text is the only sequence source of truth because command/template/skill expansion can change between runs, reloads, and replacement sessions.
Derived resolved model
Resolve raw steps into typed runtime steps during validation and execution.
Rules:
raw.Transient run progress
Only serialized run progress crosses
ctx.newSession()boundaries.State design
Sequence state must be centralized inside the extension state, not spread across ad hoc module singletons.
Guidelines:
livePi,liveState, or similar mutable runtime mirrors as primary architecture.ctx.newSession(), reacquire them from the replacement-session context instead of caching old ones.Rehydration
On
session_start:ctx.sessionManager.getBranch().SequenceState.lastRecordedfrom the newest valid snapshot.This follows the same event-sourced persistence pattern already used for notebook state.
Validation
Validation is two-phase.
Recording-time validation
Each entered step is validated immediately.
Allowed:
/readonly,/handoff,/notebook./compact.pi.getCommands()wheresource === "prompt".pi.getCommands()wheresource === "skill".Rejected:
/new.Runtime validation
Revalidate before execution, and re-resolve before each iteration.
Why:
pi.getCommands()is the canonical source of command metadata. UsesourceandsourceInfoinstead of command-name heuristics or path guessing.Execution rules
/readonly,/handoff, and/notebookcall shared pi-agenticoding command functions./compactwrapsctx.compact()callbacks in a Promise.ctx.newSession({ withSession })if more iterations remain.ReplacedSessionContextpassed towithSession.SequenceRundata across reset.Session replacement rules
ctx.newSession()is a hard session-boundary.ctx, oldpi, oldsessionManager, and any previously captured session-bound objects are stale after replacement.withSessionruns only after the replacement session is active and the new extension instance has already started.ctxpassed towithSession.SequenceRun.This avoids stale-context bugs and keeps sequence iteration reset semantics aligned with normal pi behavior.
Command refactor
Slash handlers and sequences must share behavior:
Sequence execution must call the same shared command functions used by manual slash invocation. Do not synthesize command execution by sending
/cmdtext back through the prompt path.Audit checklist
pi.sendUserMessage("/cmd").ctx,pi,sessionManager, or cached session-bound objects survivectx.newSession().Sequence snapshots remain invisible implementation details in session history and are not labeled for
/treeinspection.