Files
harness-tools/docs/pi-workflow/design.md
T

42 KiB

pi-workflow Design

Status: Accepted
Date: 24AUG2026

Purpose

pi-workflow coordinates feature planning and implementation through selected Markdown-defined agents, persisted human gates, isolated Pi child sessions, and mandatory independent review.

The extension is based on extensions/_workflows, but it is a new extension rather than a rename. The existing extension remains an inactive reference until pi-workflow is verified and explicitly exposed.

Goals

  • Load bundled Agent Profiles and trusted project overrides from .pi/agents/*.md.
  • Select agents automatically using deterministic eligibility followed by planner selection with recorded rationale.
  • Pause after discovery for unresolved questions and after design for user approval.
  • Require at least three prototype candidates and explicit user acceptance for non-trivial UI or UX work.
  • Prevent an implementation agent from reviewing its own work.
  • Support configurable concurrency with a user-controlled safety ceiling.
  • Retain a restricted JavaScript DSL for custom feature and read-only analysis workflows.
  • Run every feature in one dedicated Git worktree.
  • Let the user choose a fresh plan-seeded session, smart compaction with normal-compaction fallback, or unchanged current context before implementation.
  • Recover safely after process interruption without replaying potentially mutating work automatically.

Non-goals

  • Replacing the outer nono sandbox or granting capabilities beyond it.
  • Allowing child agents to ask the user directly.
  • Supporting arbitrary workflow graphs that can bypass required gates.
  • Automatically merging a completed worktree into its source branch.
  • Adding a general plugin framework for custom stages, policies, or persistence backends.
  • Supporting global Agent Profiles in the first version.

Language

Agent Profile: A versioned Markdown definition containing an agent's purpose, instructions, model preferences, tools, roles, and Eligibility Criteria.

Eligibility Criteria: Machine-readable conditions that determine whether an Agent Profile may be considered for an Assignment.

Assignment: One stage-specific task executed by one Agent Profile in a fresh child session.

Workflow Run: The persisted execution of one feature or analysis request.

Stage: A named unit of workflow progress controlled by the engine.

Gate: A persisted point where the Workflow Run waits for a valid Decision.

Decision: A revision-checked user or policy response that satisfies or rejects a Gate.

Prototype Candidate: One throwaway design option produced for user comparison.

Review Assignment: A fresh-session Assignment whose Agent Profile differs from every implementation Assignment it reviews.

Implementation Context Choice: The user's persisted selection of a fresh plan-seeded session, compacted current session, or unchanged current session before implementation starts. The compacted strategy prefers smart_compact and falls back to normal Pi compaction only when that tool is unavailable.

External Interface

The extension exposes one state-changing tool and one workflow command.

type PiWorkflowInput =
  | {
      action: "start";
      request: string;
      featureName?: string;
      workflow?:
        | { mode: "feature" }
        | {
            mode: "feature-script";
            insertionPoint: "before-design" | "after-design" | "before-review";
            source: string;
            args?: string;
          }
        | { mode: "analysis-script"; source: string; args?: string };
      concurrency?: number;
      background?: boolean;
      idempotencyKey: string;
    }
  | {
      action: "decide";
      runId: string;
      gateId: string;
      expectedRevision: number;
      decision: GateDecision;
      idempotencyKey: string;
    }
  | { action: "inspect"; runId: string }
  | {
      action: "abort";
      runId: string;
      expectedRevision: number;
      idempotencyKey: string;
    };

Gate Decisions use closed, gate-specific schemas:

type GateDecision =
  | {
      kind: "answer-questions";
      answers: Record<string, string>;
    }
  | {
      kind: "approve-plan";
      approved: true;
      acceptedUiImpact: "none" | "minor" | "non-trivial";
      comment?: string;
    }
  | {
      kind: "revise-plan";
      feedback: string;
    }
  | {
      kind: "accept-prototype";
      candidateId: string;
      candidateRevision: number;
      browserReviewed: true;
      feedback?: string;
    }
  | {
      kind: "refine-prototypes";
      feedback: string;
    }
  | {
      kind: "select-implementation-context";
      strategy: "fresh-session" | "smart-compact" | "current-session";
    }
  | {
      kind: "report-context-preparation";
      preparationId: string;
      planRevision: number;
      method: "smart_compact";
      outcome: "unavailable" | "succeeded" | "no-op";
    }
  | {
      kind: "report-context-preparation";
      preparationId: string;
      planRevision: number;
      method: "smart_compact";
      outcome: "failed";
      error: string;
    }
  | {
      kind: "recover";
      resolution: "retry" | "inspect" | "abort";
    };

Feature and feature-script starts require featureName as a concise lowercase kebab-case name that describes what the feature does, such as searchable-workflow-dashboard. Analysis starts omit it. The host validates the name before persisting launch intent.

The pi_workflow tool calls a single internal execute(command) interface. /workflow opens the dashboard and may accept a run ID for direct inspection. /workflow continue <run-id> performs a pending fresh-session handoff or normal-compaction fallback after the run is safely waiting; it cannot approve plans, select prototypes, or make other Decisions.

When a run reaches a Gate, the tool returns a structured waiting view. The parent agent asks the user through its normal interaction tools, then calls pi_workflow with action: "decide". Child agents never receive ask_user.

Background runs return a run ID immediately. When they reach a Gate or settle, the extension sends one custom follow-up message with a stable delivery key to the active parent session.

After start creates the run lease, every subsequent state-changing parent operation is authenticated against its active parent-session ID. Session IDs retained in parentSessionIds are historical discovery links and permit inspection only.

Agent Profiles

Discovery and precedence

Profiles are loaded in this order:

  1. Bundled profiles from extensions/pi-workflow/agents/*.md.
  2. Project profiles from the nearest .pi/agents/*.md, only when ctx.isProjectTrusted() is true.

Files are read in lexical order. The first duplicate within one source wins and emits a diagnostic. A project profile replaces a bundled profile with the same normalized name. Provenance and a content digest are retained. Regular files and symlinks are accepted only when their resolved paths remain inside the selected profile root.

Profile contents are pinned into the Workflow Run before selection. Editing a Markdown file does not change an active run.

Markdown schema

---
name: frontend-prototyper
description: Produces structurally distinct browser-reviewable UI prototypes.
roles: [prototype]
model: anthropic/claude-opus-4-6
effort: high
tools: [read, grep, find, ls, edit, write]
selection:
  stages: [prototype]
  anyTags: [frontend, ui, ux]
  allTags: []
  pathGlobs: ["src/**/*.svelte", "src/**/*.tsx"]
  keywords: [dashboard, page, component, responsive]
---

Agent instructions go here.

Required fields:

  • name: lowercase kebab case, unique after precedence is applied.
  • description: concise purpose used by selection and the planner.
  • roles: one or more of discover, design, prototype, implement, verify, or review.
  • selection: structured Eligibility Criteria with at least one stage; additional filters may be empty for a deliberate fallback profile.
  • Markdown body: non-empty agent instructions.

Optional fields:

  • model: bare model ID or provider/model-id.
  • provider: accepted only with a bare model field.
  • effort: off, minimal, low, medium, high, xhigh, or max.
  • tools: CSV or YAML string array.

Unknown fields produce diagnostics and are ignored. Malformed files are skipped independently so one profile cannot break the catalog.

Eligibility and planner selection

The engine normalizes request and repository evidence into facts:

interface WorkflowFacts {
  stage: StageKind;
  taskKind: "feature" | "analysis";
  requestTerms: string[];
  candidatePaths: string[];
  languages: string[];
  tags: string[];
  riskTags: string[];
}

Eligibility is deterministic:

  • The profile must support the requested role and stage.
  • Every allTags entry must be present.
  • At least one anyTags entry must match when the list is non-empty.
  • At least one pathGlobs entry must match when the list is non-empty.
  • At least one normalized keywords entry must match when the list is non-empty.

Pre-discovery selection uses only facts available without a model call: workflow mode, requested stage, normalized request terms, repository language markers, and paths named by the request. Criteria that require unavailable discovery facts do not match. A bundled general discovery profile provides an explicit fallback.

The engine selects the initial discovery profile deterministically from eligible profiles using specificity and profile name as the stable tie-breaker. Empty and ambiguous selections fail clearly. After discovery, the selected planner receives only eligible profiles and chooses Assignments with a structured rationale. The host validates every choice and persists the eligible set, rejected profiles with reasons, selected profiles, and rationale.

Configuration

Configuration sources are:

  1. Bundled defaults.
  2. ~/.pi/agent/pi-workflow.json.
  3. Trusted .pi/pi-workflow.json.
  4. Per-run invocation values.

Invocation values may change requested concurrency but never the safety ceiling. A project may lower the global ceiling but cannot raise it.

{
  "concurrency": {
    "default": 8,
    "ceiling": 32
  },
  "maxAssignmentsPerRun": 128,
  "maxReviewCycles": 3,
  "analysisTools": ["read", "grep", "find", "ls"],
  "worktrees": {
    "root": ".worktrees"
  }
}

32 is an immutable process-wide maximum, not merely a default. Global and trusted project configuration may lower it but cannot raise it. The scheduler uses one extension-wide semaphore so concurrent Workflow Runs cannot exceed the effective process ceiling in aggregate. Each run also has its requested or configured limit.

The first-version analysis and review tool sets are audited built-in allowlists. Configuration may remove tools but cannot add arbitrary extension tools by name.

Effective per-run concurrency is:

min(requested or configured default, global ceiling, trusted project ceiling, 32)

Invalid, zero, non-integral, or out-of-range values fail before the run starts.

Workflow State

type RunStatus =
  | "running"
  | "waiting"
  | "completed"
  | "failed"
  | "aborted";

type AssignmentStatus =
  | "queued"
  | "running"
  | "succeeded"
  | "failed"
  | "interrupted";

interface WorkflowRun {
  runId: string;
  revision: number;
  mode: "feature" | "analysis";
  status: RunStatus;
  request: string;
  featureName?: string;
  parentSessionIds: string[];
  planArtifact?: PlanArtifact;
  implementationContext?: ImplementationContextState;
  lease?: RunLease;
  workspace: WorkflowWorkspace;
  effectiveConfig: EffectiveWorkflowConfig;
  pinnedProfiles: PinnedAgentProfile[];
  facts: WorkflowFacts;
  stages: Stage[];
  gates: Gate[];
  decisions: Decision[];
  assignments: Assignment[];
  prototypeCandidates: PrototypeCandidate[];
  deliveries: DeliveryRecord[];
  uiImpact?: UIImpact;
  error?: WorkflowError;
}

Only the workflow engine may transition Stage, Gate, Assignment, context-preparation, or run status. Each accepted state-changing command increments revision and atomically replaces the persisted snapshot. Read-only inspection does not increment the revision. ImplementationContextState persists the Gate ID, plan revision, preparation ID, selected strategy, observed method/outcome, active parent-session ID, and any fresh-session or normal-compaction intent.

Built-in Feature Pipeline

Initialize
  -> Discover
  -> Questions Gate, when unresolved questions exist
  -> Design
  -> Plan Approval Gate
  -> Prototype, only for non-trivial UI impact
  -> Prototype Acceptance Gate
  -> Implementation Context Gate
  -> Implement
  -> Verify
  -> Review
      -> Remediate -> Verify -> Review, when changes are requested
  -> Complete

1. Initialize

  • Resolve effective configuration, validate the feature name, and validate the repository without mutation.
  • Load, validate, and pin Agent Profiles from the source checkout.
  • Verify that eligible discovery, design, implementation, verification, and independent review profiles exist before mutating the repository.
  • For feature mode, verify that the configured worktree root is ignored by Git and that the source checkout is clean.
  • Persist a run snapshot with workspace state creating before invoking Git.
  • Create a distinct worktree transactionally, then persist workspace state ready.
  • On creation failure, roll back what can be removed safely, persist the failure, and leave enough intent for startup reconciliation.
  • Persist Assignment intent before starting every child.

2. Discover

Discovery Assignments inspect the repository and return structured facts, relevant files, risks, precedents, unresolved questions, and preliminary UI impact evidence.

If unresolved questions exist, the engine persists a Questions Gate and waits. Answers become a Decision and are included in every later planning prompt.

3. Design

The planner selects eligible Agent Profiles with recorded rationale and produces:

  • Scope and acceptance criteria.
  • Stage Assignments.
  • Explicit file ownership for every mutating Assignment.
  • Verification commands and expected outcomes.
  • A structured UI impact classification: none, minor, or non-trivial, with affected surfaces and evidence.
  • The independent Review Assignment target.

The engine combines planner output with objective UI signals from request terms, affected paths, routes, layout/navigation changes, accessibility, responsiveness, and user-flow changes. Objective signals of non-trivial UI impact cannot be downgraded by the planner. The approved design is written atomically to a bounded, versioned plan.md containing requirements, Decisions, selected Assignments, file ownership, verification commands, worktree details, and UI impact evidence. The Plan Approval view shows the classification and evidence, and the Decision explicitly accepts that classification. Implementation and prototyping cannot begin before approval.

4. Prototype

This Stage is required when UI impact is non-trivial.

  • Produce at least three structurally distinct Prototype Candidates.
  • Use separate fresh Assignments and candidate-specific paths. Profiles may repeat when only one prototyper is eligible.
  • Prefer an existing host page and a ?variant= switcher.
  • Use realistic data and responsive layouts.
  • Keep prototypes out of production behavior and mark them as throwaway.
  • Return launch instructions, routes, candidate summaries, and artifact paths.

The engine opens a Prototype Acceptance Gate only after at least three candidates have unique IDs, revisions, paths, routes, structural rationales, responsive checks, and browser-launch evidence. The user reviews them in the browser or application and may accept one successful candidate revision, request refinement, or abort. The accepted candidate and feedback produce a new plan.md revision before implementation-context selection. Accepted prototype code is evidence, not production code; implementation rewrites the accepted direction to production standards. A post-acceptance cleanup Assignment removes prototype-only routes, losing candidates, and switchers before verification while retaining bounded artifacts outside the production tree.

5. Choose Implementation Context

Immediately before implementation, the engine persists an Implementation Context Gate referencing the final approved plan.md revision. For work without non-trivial UI impact this follows Plan Approval; for non-trivial UI work it follows Prototype Acceptance and the resulting plan revision.

The user chooses one strategy:

  • fresh-session: the engine atomically persists a handoff intent bound to the Gate ID, plan revision, run revision, source parent-session ID, and unique preparation ID before /workflow continue <run-id> calls ExtensionCommandContext.newSession(). During the expected session_shutdown, the matching source session and reason: "new" atomically claim the event's targetSessionFile onto that intent and enter claimed-target-awaiting-withSession. The target's immediate session_start with reason: "new" and matching previousSessionFile suppresses recovery for that startup lifecycle only. Only the following replacement withSession callback for the claimed target may consume the intent: it attaches the new active parent-session ID, seeds the bounded plan.md, run ID, revision, worktree path, and continuation instruction, and then satisfies the Gate. Cancellation or failure before consumption keeps the Gate waiting with the original session active when possible.
  • smart-compact: the engine persists a preparation ID bound to the Gate and final plan revision. When pi.getActiveTools() contains smart_compact, the parent invokes it with plan.md as the focus and reports succeeded, no-op, or failed through a report-context-preparation Decision carrying that preparation ID and plan revision. When the parent reports unavailable, the host accepts it only if smart_compact is absent from the active tool inventory, persists a normal-compaction intent, and directs /workflow continue <run-id> to invoke ctx.compact({ customInstructions, onComplete, onError }) with equivalent plan-focused instructions. The invocation's intent-bound onComplete or onError callback consumes that exact intent and records the terminal method and outcome. A successful or no-op preparation satisfies the Gate; a failure remains visible and waiting. An available-but-failed smart_compact attempt never silently falls back.
  • current-session: the Gate is satisfied immediately and the current context is retained unchanged.

Session replacement or compaction occurs only while the Workflow Run is waiting with no active child Assignments. Every callback, command, and event must match the active parent-session ID, Gate ID, preparation ID, and plan revision. A stale value, failed transition, or mismatched strategy leaves the Gate waiting and cannot start implementation.

6. Implement

Mutating Assignments run in the Workflow Run's worktree.

  • Every Assignment has declared file ownership.
  • All mutating Assignments are serialized in the first version. Concurrency remains available for read-only Assignments.
  • Mutating child sessions do not receive bash or other process-execution tools. Assignment-scoped file operations reject symlink traversal and require canonical containment beneath both the worktree root and the Assignment's owned path roots.
  • Plan-approved verification and generation commands run separately through a host executor using fixed executable/argument arrays, never agent-authored shell strings.
  • The engine records branch, HEAD, worktree identity, and Git diff state before and after every mutating Assignment and host command.
  • Out-of-scope changes, branch movement, commits, or worktree metadata changes invalidate the Assignment and open a Recovery Gate before more mutation starts.
  • The scheduler reserves capacity synchronously before launching children.
  • Assignment intent is persisted before child execution.
  • The extension does not commit, merge, reset, or remove work automatically in the first version.

7. Verify

Verification runs the focused tests, type checks, linting, builds, and browser validation required by the approved plan. Changed behavior must have focused tests. UI changes must be exercised in the application or browser with realistic data.

A failed verification returns to remediation or opens a Recovery Gate when automatic remediation is unsafe.

8. Review

The engine maintains a cumulative set of every Agent Profile that mutated the reviewed diff, including implementation, prototype cleanup, and remediation. Review selection excludes that complete set.

Each Review Assignment:

  • Uses a fresh child session.
  • Receives only an audited read-only tool allowlist plus an internal read-only diff tool.
  • Records a zero-diff postcondition; any repository change invalidates the verdict.
  • Receives the approved plan, accepted prototype Decision when applicable, verification evidence, and complete diff scope.
  • Reports specification conformance and repository standards as distinct structured axes.
  • Returns a structured verdict and findings.

changes_requested creates remediation Assignments whose profiles join the cumulative mutator set, then repeats Verify and Review in another fresh session. A reviewer profile may never remediate the work it reviews. If exclusions leave no eligible reviewer, the run fails closed before further mutation. After three unsuccessful review cycles by default, the engine opens a Recovery Gate for user direction. Completion requires an approved review verdict.

Custom Script Modes

The existing restricted JavaScript runtime remains as defense in depth inside nono.

Feature script

A feature script is bound by its closed insertionPoint input to run before Design, after Design, or before Review. It may request only custom read-only analysis Assignments at that point. Every pre-implementation custom Assignment uses the audited analysis tool allowlist. The host still injects and enforces Questions, Plan Approval, Prototype Acceptance, implementation verification, and independent review policy. Scripts cannot choose another insertion point at runtime, create mutating Assignments before approval, satisfy Gates, reorder mandatory Stages, or create child sessions directly.

Analysis script

An analysis script may orchestrate read-only Assignments without feature Gates. Its effective tool set is the intersection of:

  • The configured analysisTools allowlist.
  • The selected Agent Profile's tools.
  • Tools active in the parent session.

The fixed first-version allowlist excludes bash, mutation tools, recursive orchestration, user interaction, process execution, and unaudited extension tools. Configuration may only remove entries from that allowlist.

Script-visible interface

stage("Audit");
const results = await parallel([
  () => assign("review", "Inspect persistence behavior"),
  () => assign("review", "Inspect cancellation behavior"),
]);
return results;

stage, assign, parallel, and immutable args communicate through authenticated, bounded IPC. The script has no filesystem, network, process, import, timer, eval, or dynamic code-generation access.

Worktree Policy

Every feature Workflow Run owns a distinct, exclusive worktree under:

<repository>/.worktrees/pi-workflow-<feature-name>-<run-id>/

The corresponding branch is:

feature/<feature-name>

<feature-name> comes from the validated featureName start input and describes the feature rather than the workflow mechanism. Any existing branch with that name rejects a new start. If persisted state associates the branch with a Workflow Run, the error returns its run ID and directs the caller to continue it through inspect or decide; otherwise it directs the caller to choose another descriptive name. The extension never adds a numeric or run-ID suffix silently.

The configured worktree root must already be ignored by Git. The extension verifies this with Git before persisting creation intent and fails with setup guidance rather than modifying .gitignore automatically. A dirty source checkout blocks creation so uncommitted work is never silently omitted.

The engine records an ownership lease for the run and refuses to adopt or share another run's worktree. The completed worktree remains available for inspection and manual integration. Removal is explicit and never part of normal completion.

Read-only analysis runs use the parent cwd and do not create worktrees. All child sessions inherit the parent nono sandbox. The extension neither invokes nono nor widens filesystem or network permissions.

Persistence and Recovery

Artifacts live under:

~/.pi/agent/pi-workflows/<run-id>/

Files include:

workflow.json
request.md
plan.md
config.json
profiles/
handoffs/
transcripts.json
result.json
artifacts/

Writes are atomic and mode 0600. Serialization, transcript, source, argument, result, and IPC bounds from _workflows are retained.

The snapshot contains Decisions and prior Assignment outcomes, so a separate event store is unnecessary. Every start, decide, and abort command requires an idempotency key. The launch key and initial result are persisted before workspace creation. State-changing commands use revisions and idempotency keys to reject stale or duplicate transitions. Repeating a completed key returns its recorded result without another transition.

An active run owns a persisted lease containing process identity, active parent-session identity, heartbeat time, and worktree identity. The run retains every parent session ID attached through a handoff so /workflow can discover it from either session, but only the active ID may mutate the run. Startup and session recovery reconcile stale leases, child processes, workspace-creation intent, Git identity, context handoffs, compaction intents, and assignments left running.

Fresh-session handoff intent is durable before newSession(). A session_shutdown with reason new bypasses ordinary interruption recovery only when its source session matches the pending intent; that handler atomically claims the event's targetSessionFile and marks the intent claimed-target-awaiting-withSession. The claimed target's immediately following session_start is exempt from recovery only when its reason is new and previousSessionFile matches the source. The replacement withSession callback must then match the claimed target before it atomically consumes the intent, rotates the active parent-session ID, records both session IDs, and resumes delivery. Any later session_start caused by startup, reload, resume, or fork, or a bounded claim expiry before consumption, follows normal recovery and leaves implementation blocked.

Every Gate and terminal background notification has a deterministic delivery key and persisted pending/sent state. Delivery uses a custom parent-session message containing that key in structured details. The parent session is the receiver-side receipt: recovery scans durable session entries for the key before retrying, then marks the outbox item sent. This closes the crash window between message persistence and sender-side state updates.

On process or session interruption:

  • Active Assignments become interrupted idempotently.
  • Potentially mutating Assignments are never replayed automatically.
  • The run opens a Recovery Gate offering retry, inspect, or abort.
  • Completed Assignments and accepted Decisions remain intact.

Child Execution

Every Assignment creates a fresh in-memory Pi session using the parent model registry, Assignment cwd, resolved trust decision, settings, skills, extensions, and AGENTS.md context. Worktree trust is resolved with resolveStandaloneChildProjectTrust; an alternate cwd never inherits a session-only trust decision implicitly.

The effective model is resolved in this order:

  1. Assignment override allowed by the workflow definition.
  2. Agent Profile model/provider/effort.
  3. Parent session model and thinking level.

Unknown or ambiguous models fail the Assignment before execution. The effective provider, model, effort, profile digest, child session ID, and context capacity are persisted.

Children cannot access recursive workflow or subagent tools or ask_user. pi_workflow is added to both the shared child-tool exclusion policy and the separate Pi-subagent child exclusion list alongside the legacy workflow name; implementation should consolidate the duplicate list onto the shared policy when possible. Each child tool call retains the shared timeout guard, including tools registered after startup. The first-response watchdog and bounded shutdown behavior from _workflows are retained.

Internal Modules

pi-workflow should remain a small set of deep modules:

  • index.ts: Pi tool, command, lifecycle, and composition root.
  • engine.ts: command handling, state transitions, gates, policies, and scheduling.
  • profiles.ts: Markdown discovery, validation, eligibility, pinning, and diagnostics.
  • execution.ts: fresh child sessions, assignment-scoped file operations, approved host commands, model resolution, structured output, transcripts, and timeouts.
  • script-runtime.ts plus sandbox-child.cjs: restricted custom DSL and IPC.
  • store.ts: atomic revisioned snapshots and bounded artifacts.
  • workspace.ts: worktree creation, ownership conflict checks, and lifecycle.
  • context-transition.ts: plan handoffs, new-session replacement, smart/default compaction callbacks, and session attachment.
  • dashboard.ts: run inspection, continuation handoff, and prototype/report links.
  • agents/*.md: bundled default profiles.

Pure transition and eligibility logic stay inside the engine and profile modules rather than behind speculative adapter interfaces. Tests may inject filesystem, executor, decision, and script-runtime adapters at the composition root.

Exposure and Migration

  • Do not rename or activate _workflows during initial development.
  • Add extensions/pi-workflow tests to package.json explicitly.
  • Typecheck through the existing extensions/**/*.ts configuration.
  • Add pi-workflow to config/pi-links.json only after focused tests, full tests, independent review, and manual TUI validation pass.
  • Run the link reconciler in check mode before sync.
  • Preserve unrelated managed links and require /reload after exposure.

Implementation Phases

Phase 1: Domain and profile catalog

Implement the Workflow Run model, configuration resolution, Markdown profile loader, deterministic Eligibility Criteria, and bundled profiles.

Phase 1 Success Criteria

  • Trusted project profiles override bundled profiles deterministically, while untrusted project profiles are never read.
  • Profile files and symlinks resolving outside the selected bundled or trusted .pi/agents root are rejected.
  • Malformed profiles produce isolated diagnostics and cannot suppress valid profiles.
  • Profile contents, provenance, and effective model settings are pinned by digest.
  • Every profile has stage Eligibility Criteria, pre-discovery fallback is deterministic, and irrelevant profiles are excluded with recorded reasons.
  • Configuration resolves default concurrency 8 and an immutable process-wide maximum of 32; project and run settings cannot raise that maximum.

Phase 2: Engine, gates, and persistence

Implement the command interface, legal transitions, closed Gate Decisions, atomic revisioned snapshots, idempotency, leases, delivery state, recovery, transactional worktree creation scaffold, and the built-in feature state machine through Plan Approval using an injected scripted Assignment executor for tests.

Phase 2 Success Criteria

  • An exhaustive legal and illegal Stage/Gate transition matrix passes through execute(command); invalid transitions fail closed.
  • start, decide, and abort require idempotency keys; launch intent is persisted before worktree creation, and a repeated completed key returns its prior result without a second transition.
  • Stale revisions are rejected for decide and abort.
  • Using the injected scripted Assignment executor, Questions and Plan Approval pause and resume across process restarts with the accepted UI classification intact and no dependency on live Pi child execution.
  • Startup reconciliation detects stale leases and transitions abandoned running Assignments to interrupted exactly once.
  • Potentially mutating interrupted Assignments require an explicit Recovery Decision; completed Decisions and Assignments are not replayed.
  • Feature names are validated before launch persistence; worktree branches use exactly feature/<feature-name>, and every existing branch collision rejects new start with recorded-run inspect/decide guidance or descriptive-name rename guidance, never a silent suffix.
  • Workspace creation intent and lease are persisted before Git mutation; creation succeeds transactionally or leaves a reconciled failure that startup can recover.
  • Interrupted atomic writes leave a complete prior or next snapshot, never partial JSON.

Phase 3: Child execution and selection

Implement the production Pi Assignment executor and eligible planner-selection path, then integrate child sessions, structured output, model selection, process-wide scheduling, transcript capture, trust resolution, and timeout behavior with the Phase 2 engine.

Phase 3 Success Criteria

  • Live Discover and Design Assignments drive the built-in pipeline through Questions and Plan Approval using the production executor.
  • Every Assignment uses a fresh child session and resolves worktree trust through resolveStandaloneChildProjectTrust.
  • Planner choices are restricted to eligible profiles and persist selected, rejected, and rationale data.
  • Aggregate running Assignments across all Workflow Runs never exceed 32 or a lower configured ceiling.
  • Capacity is reserved synchronously; queued cancellation and bounded session shutdown release capacity exactly once.
  • pi_workflow, legacy workflow/subagent tools, and user interaction remain unavailable through both shared workflow-child and Pi-subagent child policies.
  • A tool registered after child startup is still guarded by extensions/shared/tool-call-timeout.ts.

Phase 4: Worktrees, implementation, verification, and review

Extend the Phase 2 worktree scaffold with the Implementation Context Gate, /workflow continue, and the production command/callback context-transition adapter, then add serialized mutation, canonical ownership and Git-state enforcement, verification evidence, independent review, and remediation cycles.

Phase 4 Success Criteria

  • A dirty source state or unignored worktree root blocks creation before Git mutation.
  • Workspace creation intent is persisted first; creation failure rolls back safely or remains reconcilable after restart.
  • Every feature run receives a distinct leased worktree and worktrees are retained until explicitly removed.
  • A versioned plan.md is persisted before the Implementation Context Gate and revised with an accepted prototype when applicable.
  • Phase 4 registers /workflow continue; its ExtensionCommandContext performs both fresh-session replacement and approved normal-compaction fallback rather than deferring either path to dashboard integration.
  • fresh-session persists a Gate-, revision-, plan-, preparation-, and source-session-bound handoff intent before newSession; only the replacement withSession callback consumes it, attaches the new active parent session, seeds the bounded plan handoff, and resumes.
  • smart-compact uses a Gate-bound report-context-preparation transition with distinct unavailable, succeeded, no-op, and failed outcomes; failed requires a non-empty actionable error. The host validates unavailable against pi.getActiveTools(); only that outcome permits plan-focused ctx.compact({ onComplete, onError }), whose intent-bound callback persists success or failure.
  • An available-but-failed smart_compact call does not silently fall back and keeps the Implementation Context Gate waiting with an actionable error; current-session preserves context unchanged.
  • Context transitions run only while no child Assignment is active. Every state-changing parent call must match the lease's active parent-session ID; historical IDs are inspection-only, and stale Gate, preparation, plan, or run revisions keep implementation blocked.
  • The pending fresh-session intent survives its matching old-session reason: "new" shutdown and atomically enters claimed-target-awaiting-withSession; only the target's immediate matching reason: "new" session_start suppresses recovery before withSession consumes it. Later startup, reload, resume, fork, or expiry recovers an unconsumed claim without starting implementation.
  • Mutating Assignments are serialized, receive no process-execution tools, reject symlink traversal, and require canonical write containment beneath both the worktree and owned path roots.
  • Plan-approved host commands use fixed executable/argument arrays; out-of-ownership writes or branch, HEAD, commit, reset, clean, or worktree changes fail before another mutation begins.
  • Review excludes the cumulative set of profiles that mutated the diff, including remediation and prototype cleanup, and fails closed when no independent reviewer remains.
  • Review uses a fresh session, an audited read-only tool set, and a zero-diff postcondition.
  • Review reports specification conformance and repository standards separately.
  • Completion is impossible without focused behavioral tests, successful verification, and approved independent review.

Phase 5: Prototype policy and custom scripts

Add objective UI-impact validation, three-candidate prototype handling, post-acceptance cleanup, Prototype Acceptance, and restricted feature/analysis script modes.

Phase 5 Success Criteria

  • Objective signals of non-trivial UI impact cannot be downgraded, and Plan Approval explicitly accepts the displayed classification.
  • Non-trivial UI work cannot reach implementation without three successful, structurally distinct candidate revisions and explicit acceptance of one.
  • Prototype candidates use realistic host context, are browser-reviewable and responsive, and produce launch and validation evidence.
  • Prototype-only routes, losing variants, and switchers are removed or isolated before verification while bounded artifacts remain inspectable.
  • Analysis and pre-implementation feature scripts receive only the audited read-only tool allowlist.
  • Feature scripts are bound to a validated closed insertion point and cannot reorder or bypass mandatory Gates, prototype policy, verification, or review.
  • Script IPC tests reject malformed, duplicate, oversized, unauthenticated, and over-budget requests and prove cancellation terminates active work.

Phase 6: Dashboard, integration, and exposure

Add the dashboard integration around the Phase 4 /workflow continue command, persisted notification outbox, reports, complete verification, documentation, and managed exposure.

Phase 6 Success Criteria

  • Dashboard state is derived from persisted snapshots and active runs, prior supported snapshot versions remain readable, and the existing /workflow continue command is presented only for a valid pending fresh-session or normal-compaction intent.
  • Stable delivery keys, durable custom parent-session message receipts, and outbox reconciliation ensure each background Gate or terminal result is delivered once across reload and recovery.
  • Focused tests, typecheck, full test suite, and manual Pi TUI validation of Gates, dashboard navigation, background delivery, and prototype handoff pass.
  • Exposure is performed only through config/pi-links.json; unmanaged extension links are rejected and link reconciliation check passes before sync.
  • _workflows remains available as an inactive reference until removal is separately approved.

Verification Notes

  • Test profile trust, precedence, malformed YAML isolation, and containment of regular files and symlinks.
  • Test the exhaustive legal and illegal Stage/Gate transition matrix through the engine interface.
  • Test stale revision, duplicate idempotency, lease reconciliation, and notification outbox handling.
  • Test process-wide semaphore reservation races, queued cancellation, bounded session shutdown, and interrupted recovery.
  • Test all three Implementation Context choices; Gate-bound smart_compact unavailable/succeeded/no-op/failed reports; required actionable failure errors; active-tool validation; intent-bound normal-compaction onComplete/onError callbacks; available-tool failure without fallback; and stale Gate, preparation, plan, run, and active-session identities.
  • Test fresh-session intent persistence before newSession, exact shutdown claim, immediate target session_start exemption, withSession consumption, cancelled replacement, active-session rotation, old-session mutation rejection, and recovery of unconsumed claims on later startup/reload/resume/fork or expiry, plus the no-active-child transition precondition.
  • Test cumulative reviewer profile/session exclusion, read-only postconditions, and failure when no independent reviewer exists.
  • Test non-trivial UI classification cannot bypass structural candidate evidence, user acceptance, or prototype cleanup.
  • Test analysis and pre-approval feature modes cannot activate mutation, recursive orchestration, interaction, process, or unaudited extension tools.
  • Test malformed, duplicate, oversized, unauthenticated, and over-budget script IPC plus cancellation escalation.
  • Test atomic persistence and backward-tolerant dashboard reads.
  • Manually validate Gate dialogs, background delivery, dashboard navigation, browser prototype handoff, and responsive candidates in Pi TUI and the application.

Precedents & Lessons

  • _workflows already proves bounded sandbox IPC, atomic artifacts, child-session execution, transcript capture, and dashboard rendering.
  • extensions/shared/child-session.ts is the source of truth for project trust inheritance, recursive-tool exclusion, and bounded child teardown.
  • extensions/shared/tool-call-timeout.ts must continue guarding tools registered after child startup.
  • extensions/subagents demonstrates synchronous capacity reservation, normalized lifecycle state, and duplicate-delivery hazards.
  • skills/design/prototype/UI.md requires structurally different variants, realistic host context, browser review, and throwaway cleanup.
  • skills/develop/code-review/SKILL.md establishes independent standards and specification review as separate concerns.
  • config/pi-links.json is the only approved exposure mechanism for repository-managed global extensions.