{
  "id": "workflows",
  "runtimeRequirements": [
    "Default-loaded through package.json#pi.extensions",
    "saved workflow JavaScript is reviewed trusted input executed by Node's main-context default loader; static node: imports are available under default self-contained-static identity, while local/package/dynamic/source-anchored module behavior requires literal meta.identityCoverage entry-only; either mode can retain host-process filesystem, subprocess, network, and other capabilities, so identity policy, approval metadata, the intended dsl-only authoring convention, and worktrees are not a sandbox",
    "workflow tool accepts name, scriptPath, or legacy script target fields; exactly one must be supplied",
    "workflow semantic input is only optional text of any length, with no runtime character ceiling; the closed programmatic tool may separately carry unrestricted items:string[] whose exact order, bytes, whitespace, empty strings and duplicates are preserved through a frozen dsl.items() snapshot with no automatic child metadata or item-list count/size/content policy, while non-arrays, non-string members, nested arrays, null and unknown top-level fields fail before child execution; the tool may also carry closed continuation metadata with one originRunId and at least one complete artifact ref, all from that origin, while duplicate identities, malformed refs, object-valued input, and continuation combined with replay-only resumeFromRunId fail before child execution",
    "saved workflow names resolve the nearest project .locus-pi/workflows namespace first, then ~/.locus-pi/workflows, then the packaged examples directory examples/workflows; .pi/workflows, .claude/workflows, and .agents/workflows are not saved-name sources; every accepted directory uses only the pi-native <name>.workflow.mjs filename, so a foreign host's <name>.js is never resolved and a script authored against another workflow DSL is not portable here; a Package workflow is registered by the existence of its entry file, and a package-boundary test fails when package.json#files does not ship what the directory resolves",
    "standard authoring is one continuous Design -> review -> Build sequence shared by the packaged ordinary locus-pi-workflow-create and explicitly selected locus-pi-workflow-create-detailed lessons (selection/invocations owned by docs/workflows/create.md): a raw requirement first writes and reviews .locus-pi/workflows/<name>/<name>.design.md with an explicit `runnable root` or `group-only` namespace and exact Entries table (group-only omits the root), selected pattern, numbered algorithm, explicit node/input/output/capability/consumer graph, concurrency, bounds, handoffs, mechanisms and failure exits, then writes exactly the declared direct sources, including the same-named root only when declared, validates every logical identity, Node syntax and orchestration-only shape without importing unchecked source; create-only stops at checked source while authorized create-and-run hands it to the existing run skill without repeat approval; only explicit Design only wording pauses after design; Build design: <path> and Build approved design: <path> remain compatible Build-only requests; material graph changes return to Design, and both lessons teach a complete Pi example and dependency reasoning before task-fit patterns, reading only selected shared references",
    "workflow_check_source reads one confined project-relative workflow source up to 512 KiB, bounds and redacts model-facing errors, and applies Node syntax validation plus the same machine-enforced standard source grammar used by the package gate without importing or executing the target; source-check and workflow error returns use Pi's tool_result hook to preserve the machine error flag alongside structured diagnostics; the packaged authoring skill and task/plan use this Pi-native tool in both source checkouts and installed consumer projects",
    "each run owns one canonical .locus-pi/runs/<runId>/ evidence root split into outputs for the human projection and runtime for machine state; deliberate agent files are written with ordinary tools at exact caller-assigned destinations in prompts, independent of native workspace coordination; --run-name <name> selects .locus-pi/workspaces/<name> for new workspaces while an existing legacy-only .locus-pi/plans/<name> remains bound in place; runtime/artifacts/index.json records runId, artifactId, name, sha256, media type, size, relative path, stage, provenance, and optional source lineage; every agent attempt persists its exact answer, while a fresh child must also contribute its confined Pi transcript and result envelope; replay persists a new answer with replay provenance but fabricates no child evidence; index ownership, path/symlink confinement, duplicate identities, bounded text, transcript headers, and digest reads fail closed; only explicit publishArtifact/publishPrimaryArtifact records enter outputs and the newest 20 published/primary refs enter runtime/result.json plus the native workflow tool result, so call evidence stays out of the reader folder while a later run receives source-truth refs instead of guessing ids; the full index remains canonical",
    "DSL publishArtifact(name,text) persists and exposes a supporting workflow-authored document, while publishPrimaryArtifact(name,text) declares the one semantic result and a second declaration fails closed; both return the complete {runId,artifactId,name,sha256} reference; consumeTextArtifact(ref) accepts only a complete prior-run text reference from an ok source run, verifies identity, media type, size, path and digest, then copies bytes into the current run with source lineage plus verified source target/artifact metadata and its validated terminal string result/ref projection; agent({artifact}) gives the automatic runtime-only answer record a stable logical name",
    "repository_check is available to full-tool workflow children; the workflow bridge freezes exact package script commands before any child can write, and the tool accepts exactly one still-matching baseline script name with no arguments or shell text, while the host fixes package-manager argv, timeout, output bound, cwd and cleanup and runs the trusted operator-owned script in a disposable external Git worktree containing current tracked/untracked source bytes; writer-added or modified commands are refused, the operator checkout is never its cwd, and package script execution is not an OS or network sandbox",
    "project-local planning and testing lifecycle examples stay ignored under .locus-pi/workflows; they exercise split-run plan consumption and independent testcase design, implementation/execution, and failure attribution, but are not tracked examples, Package workflows, or public npm package files",
    "at resolution time, existing explicit scriptPath and project saved-name candidates are checked both lexically and by canonical realpath against the canonical project root; an external symlink observed by that check fails before module evaluation, internal symlinks remain allowed after their physical target verifies in-project while the lexical path stays stable as recorded sourcePath, and personal/package sources retain their separate trusted roots; validation and source capture are point-in-time filesystem operations, not a sandbox; legacy script strings normalize to saved names or project-relative paths",
    "each run persists version-2 exact-entry identity with read-only script-<sha256>.workflow.mjs snapshot, scriptSha256, identity policy/coverage, execution source, Node version/platform/arch, sorted builtin imports and unbound dependency descriptions; default self-contained-static rejects recognized direct dependency syntax except literal static node: imports/re-exports and executes the snapshot, while literal top-level meta.identityCoverage entry-only permits local/package/dynamic/require/import.meta behavior through a hash- and run-qualified source URL but labels dependency bytes unbound; createRequire aliases, eval-generated imports and arbitrary host code are outside static analysis and require an honest author downgrade; only old unversioned identity reads as entry-only-legacy, unknown/inconsistent schemas fail closed, structured operator target/identity metadata omits internal paths while runtime error text may contain loader paths, and final snapshot verification follows JSON result detachment before synchronous persistence; hash checks are point-in-time evidence, not an atomic same-owner race guarantee",
    "/workflows run is an explicit operator command: it resolves the launch target, claims one stable session/project background identity with a generation-scoped callback lease, returns the editor while runWorkflowScript continues, and does not pass through model-tool approval or open a second Locus-owned prompt; the programmatic workflow tool remains awaited but registers non-exclusive control with the same owner without occupying the slash-command slot",
    "/workflows run checks the real Pi command context ctx.isIdle before target resolution, transcript creation, or execution; because Pi can invoke extension commands during streaming and would route sendMessage without deliverAs to agent.steer, a busy or unreadable idle state fails closed with a warning, zero sendMessage calls, and zero workflow execution; a second slash run under the same stable session/project identity conflicts until its predecessor settles, while canonical /workflows stop [runId|last] and flat /workflow-stop compatibility alias are the sole operator cancellation paths for slash- and tool-launched workflows and report stopping until settlement",
    "the installed locus-pi-workflow-run skill runs, starts, resumes, or monitors the current run of an existing workflow: it uses the native workflow tool when that structured capability exists; outside Pi it invokes the registered /workflows run surface directly through pi --mode json -p with the slash command as one argv element and refuses native-only items or continuation instead of dropping them; every external target, native-workspace, and resume value uses the same simple-token or JSON string-token encoding rule for whitespace, quotes, backslashes, and controls, while a leading-hyphen command value is rejected because quoting does not make a reserved option token valid; semantic input remains unchanged after --; the caller applies a 30-second gate for typed workflow_start or workflow_rejected, keeps Pi attached until workflow_end, and reads the canonical runDir, journalPath, resultPath, workflowStatus, and resultPersisted fields; Pi exit status is transport evidence rather than workflow success, awaiting_operator remains unanswered, --approve is the existing broad Pi project trust boundary rather than workflow-only approval, and neither Pi nor the workflow runtime is a sandbox",
    "bare /workflows is the canonical visible command and opens a TUI command menu with descriptions beside all nine exact verbs dashboard, list, info, status, result, run, continue, stop, and skills; RPC, print/no-UI, and TUI hosts without select/interactive UI receive the same typed help fallback; direct /workflows <subcommand> forms remain available, and only /workflow-stop remains as an emergency compatibility alias; command completion defers persisted handoff scanning until a continue argument is actually requested, so ordinary command typing does not synchronously traverse run evidence",
    "/workflows skills sync|status|remove manages only the four packaged workflow skills (ordinary create, detailed create, run and external Pi session) for Codex .agents/skills and Claude Code .claude/skills at user or project scope; the package directories remain canonical, an adjacent .locus-pi-workflow-skills.v1.json provenance file is the sole ownership record, sync uses per-link atomic symlinks and removes provenance-backed retired-name links, package upgrades refresh stale managed targets, status is read-only, remove touches only provenance-backed links, every real directory or foreign symlink conflict fails closed before mutation, and an unexpected later filesystem error rolls the selected managed-link transaction back to its captured state",
    "Pi 0.84.3 is the minimum supported workflow host floor: all four Pi peer ranges stay open above that floor, the exact development dependencies and lockfile entries move together to the Pi CLI version exercised by maintainers, and tool-origin question projection waits for agent_settled rather than the intermediate turn_end event",
    "actionable awaiting_operator results carry one strict runner-enriched generic handoff envelope over select/text questions, exact terminal artifact refs, origin, and self-contained-static target/script identity; continuation resolution preserves the persisted target kind so folder-qualified saved names stay names instead of passing through the legacy script alias and becoming filesystem paths; a question may bind one unchanged published continuation ref as detailArtifactRef, which the service re-reads with digest verification, redacts, bounds to 4096 bytes and 12 lines, and renders above the choices without workflow-side text parsing; legacy reason-only waits remain readable but not actionable, malformed or forged envelopes fail closed, continuation re-verifies current identity and artifacts, and result.json remains immutable",
    "one source-adjacent atomic claim sidecar excludes parallel continuation across Pi processes, binds to the child run inside the runner, stores no raw answer, releases launch failure, recovers bounded stale pre-start claims, and projects successful/newly-awaiting children as resolved while failed/cancelled children remain retryable",
    "the session-scoped direct-question queue reads durable actionable handoffs oldest-first, shows Question 1 of N, and launches at most one continuation; the /workflows menu provides contextual workflow/run selection for info, result, run, continue, and stop rather than one combined picker; Escape is an answer rather than a postponement \u2014 the workflow's continuation receives the question list with an explicit operator-declined note, and each question carries either the answer given before the refusal or a declined marker; nothing reopens automatically except questions published by runs the current Pi session launched, so a new session never raises an older run's question at start or on its first settled turn, while the menu's continue entry opens the oldest pending item project-wide, /workflows continue <runId> opens a named one, and canonical /workflows stop is the cancellation path",
    "TUI handoffs use one non-overlay inline select/text component shared narrowly with ask, RPC uses native bidirectional extension UI requests, JSON/print requires /workflows continue with explicit --answer, and embedded child observation remains session.subscribe; Locus serializes its own inline components but Pi exposes no global third-party UI focus lock",
    "/workflows dashboard plus interactive TUI /workflows status and /workflows status <runId> open a persisted disk-only run viewer with run to stage to evidence to content navigation; it groups indexed answers, transcripts, result envelopes, published/consumed text, and journal logs by stage, re-reads and digest-verifies artifacts before rendering, refuses changed, malformed, missing, oversized, or tampered evidence, and owns no live session state",
    "static workflow help, passive list/info/status/detail fallbacks, launch errors, and headless settled receipts use typed operator blocks; interactive TUI /workflows list, info, dashboard, and status use workflow-owned focused custom components, while RPC, print/no-UI, and TUI without custom UI keep bounded static status/detail and the existing live progress panel remains domain-owned",
    "workflow history uses [R] independently from persisted source tags [P]/[U]/[PKG]; the focused catalog presents Project, User, Package, and History as source tabs, shows active catalog and selected source paths, wraps descriptions at word boundaries, and copies complete folder-owned namespaces to Project/User without merging or overwriting; passive TUI bounds status/detail to 10/20 rows, RPC to 4/1 rows, and all variants point to persisted run evidence",
    "programmatic workflow tool declares exec approval metadata to the Pi host; approval details warn that the reviewed file has full host Node.js/module access and that approval is consent, not capability isolation",
    "programmatic workflow tool stays headless and does not add a launch prompt",
    "the direct fusion tool is registered by the default workflows extension but removed from pi.getActiveTools by default; /fusion configures one required homogeneous tool-free or agent mode, 2-10 unique concrete member models and a separate judge from modelRegistry.getAvailable, persists version-2 project-local configuration under .locus-pi/fusion/config.json, and uses pi.setActiveTools to enable or disable the tool immediately without changing unrelated active tools; status, enable, run and tool execution reject version 1, while explicit configure/set atomically replace it with reviewed enabled:false version 2; every invocation revalidates enabled state and current model availability before creating a run, forwards no ambient history, and executes through the same mode-specific Fusion runtime, child bridge, journal, artifacts, result envelope, and report as dsl.fusion; tool-free requires exact empty host active-tool readback before prompt, fresh terminal evidence separates declared mode from readback, and replay retains the declaration without claiming live readback",
    "an agent catalog plus the createAgentSession host is required to spawn sub-agents; workflow-local behavior is supplied through rendered prompts",
    "DSL agent/items/workspaceDir/invokeWorkflow/publishArtifact/publishPrimaryArtifact/consumeTextArtifact/continuationArtifacts/parallel/pipeline/phase/log routes model calls through executeAgentRunBoundary with createAgentSdkSessionExecutor; items() returns immutable exact caller strings; workspaceDir() returns native coordination only; outputDir, publishPrimaryFile, root/child meta.outputDir and the publishPrimaryArtifact source-file overload are removed and rejected by both source-check modes, with narrow runtime migration traps; invokeWorkflow inherits native workspace and receives user-file paths in whole input; string-only text snapshots and verified continuation remain optional native evidence; agents save assigned files through ordinary tools, and consumers reopen those exact paths after edits/replay before use; file placement never changes cwd, tool resolution, context or worktree choice; ordinary agent answers remain opaque and choices are the only exact route form",
    "invokeWorkflow accepts exactly one child target: child binds a direct sibling to the running root's exact folder namespace and source, name resolves a root or qualified root/child with normal Project to User to Package precedence, scriptPath binds one project-relative source, and packageName remains exact installed-Package compatibility",
    "DSL promptFile() renders source-relative .prompt.md resources with strict variables; workflow-local agent files are not supported",
    "DSL workspace() allocates one retained runtime-owned linked worktree at an exact ref and returns an opaque handle that can be reused across agents",
    "workflow agent() live rows compute a workflow parent row id, pass it as live.parentRowId to the SDK executor, and collapse the visible workflow parent row once its real SDK child row exists so the live progress panel and text fallback do not show duplicate Working parent/child rows",
    "workflow agent_end completed, failed, cancelled, and blocked states project to terminal live rows; the shared terminal invariant clears currentTools/currentToolArgs/currentToolStartMs and freezes elapsedMs, while cancelled has its own marker/status, leaves active counts, stops the spinner, and removes the x stop affordance; live-row settlement alone does not decide the script outcome, but a failed/blocked/cancelled result returned directly from parallel() or pipeline() is a typed group failure",
    "workflow live progress uses a pinned passive belowEditor widget while the background lease is active; it projects normalized, stable-deduplicated declared/reached/current stages and one active child through the shared AgentLivePanel, whose rows and latest-message sub-lines measure visible terminal columns and truncate wide graphemes before Pi renders them; an operator continuation names its source run, keeps current-run counters independent, and shows that run's process-retained settled agents as one atomic previous-run block, or an explicit history-unavailable status pointer when those rows have expired; it keeps parallel groups as non-selectable headings and round retries in the same slot with rN, then /ps expands the shared fleet for leaf Enter/readable output and Esc/q navigation without abort; explicit workflowRunId provenance removes both the x stop hint and handler for labeled and unlabeled workflow rows, while standalone agent rows retain confirmed x cancellation; session_shutdown immediately disposes the session-owned progress listener and timer even when a runner ignores abort, while terminal/failure cleanup remains idempotent; after completion, input/registered command cleanup or Pi turn_end removes the large presentation while store-owned cleanup retains the newest five workflow run subtrees whose rows are all terminal for /ps viewer, /ps last, and direct guidance; older fully terminal subtrees are pruned, while active, queued, and zero-row runs are not counted or pruned",
    "transcript persistence has an explicit surface mode: a background command run publishes one idle-checked locus-workflow-run boundary banner at launch and then buffers lifecycle with no further active-run sendMessage calls, carrying its originating session generation; after completion a still-current command awaits ctx.waitForIdle, rechecks ctx.isIdle, then immediately appends the exact untruncated prose result as a separate visible locus-workflow-result message followed by one visible bounded terminal locus-workflow-run digest, both with triggerTurn:false and no deliverAs, while session_shutdown/reload revokes stale callbacks and the programmatic tool puts one digest in its native final toolResult",
    "command and tool digests include only bounded workflow/agent lifecycle: lines cap at 160 characters, each run keeps at most 20 agent transitions plus start/final (at most 22 lifecycle lines and 4096 digest characters), raw result/journal data is excluded, journal errors are retained only as fallback text for exactly one final failure with eventKind workflow_end, and workflow agent lines use stable catalog agent plus label rather than a non-canonical parent-row petname",
    "command evidence and digest-persistence warnings use the real Pi warning level; result.json persistence failure is shown once inside the final live/typed result instead of a duplicate toast; if waitForIdle, the final idle check, or sendMessage is unavailable or fails, command digest persistence reports a warning and makes no sendMessage fallback, so it never steers a streaming parent",
    "owned [agent] ->/[agent] <- transport markers are detail-only in /workflows status <runId>; the main status surface suppresses the duplicate markers without suppressing failures or evidence warnings",
    "/workflows list [query] builds one current/history model from the shared namespace-first resolver and persisted run index; Project, User, and Package directories are scanned on every call, so list, info, named run, and unknown-name recovery use one resolver; one canonical folder owns an optional same-named root plus direct children, and a folder without its root is an honest non-runnable group header; the winning source owns that whole namespace, root or group headers precede indented short-name children, and no catalog or identity surface exposes an absolute origin path; each selectable row keeps a compact [P]/[U]/[PKG] source badge and source-relative locator, while very low terminals use a compact one-line fallback; current and run-specific history rows are inspectable, with history leading ref then run id then source badge; the custom viewer uses all terminal rows except Pi's three footer/status rows with no fixed ceiling, Pi-native JavaScript highlighting and a persistent top/bottom code frame keep metadata separate while internal scroll reaches the final line, i opens an independently scrollable identity screen where current locator or history run id/locator/SHA remain reachable with Up/Down, PageUp/PageDown, Home, and End even at terminal widths 8 and 1, Esc/Back returns to the preserved catalog cursor, width-bounded empty/no-match/history-only states have no phantom selection, and RPC/print/no-UI or TUI without custom UI retain an honest passive projection",
    "workflow source actions are editor handoffs: a ready current source offers Back, Start, Edit, and Review; history offers only Back and Review; Tab or Left/Right moves action focus with a caret plus warning color while other actions and metadata labels use semantic success color, Enter resolves the focused typed intent, and only after the shared inline ctx.ui.custom surface completes may the command prefill the restored main editor through setEditorText; Start writes direct /workflows run <resolved-name>, while Edit/Review/diagnosis write Request plus Skill: locus-pi-workflow-create and Additional instructions; no browser action submits editor text, imports or runs a workflow, sends a message, writes a file, or mutates history",
    "history inspection reads only the exact immutable script snapshot persisted for one run; simple run id, lexical run containment, non-symlink directory chain, exact script-<sha256>.workflow.mjs basename, regular non-symlink file type, direct-child realpath containment, readable bytes, valid target, and matching SHA-256 are all required; ready, legacy, missing, unreadable, invalid, and tampered persisted states are explicit, and the browser returns stale if target, path, SHA, or identity coverage changes after catalog selection; no state falls back to current or alternative source",
    "/workflows info [name] builds one source-backed OperatorBlock explaining commands, namespace precedence, static metadata, composition, trust, DSL primitives, agent catalog selection, model-role metadata, and actual per-call/session model routing; named info resolves through the same catalog, reports exact source plus a source-relative locator, bounded literal meta.description and any declared literal meta.phases, and fails explicitly for unknown refs; interactive TUI with custom UI opens a workflow-owned read-only scroll view over that same block, with Up/Down, PgUp/PgDn, Home/End, and Esc/q making the complete semantic contract reachable at 146, 80, and 48 columns; RPC, print/no-UI, and TUI without custom UI retain a bounded passive projection with an honest custom-UI limitation; neither path imports or runs a workflow, mutates the editor, or writes files",
    "workflow catalog descriptions and authoring profiles come only from a bounded static AST scan of top-level literal export const meta, accepting unquoted or quoted literal keys; profile is standard, legacy, integration, or unclassified when absent/invalid; listing never executes modules or accepts comments, unrelated fields, computed metadata, or interpolated values; repository source-shape tests reject parser/renderer/recovery/wrapper machinery from standard entries without imposing a domain linter at runtime",
    "workflow journal rows use per-kind required and allowed fields before projection, so malformed JSON, empty agent_end, cross-kind fields, invalid counts, and invalid terminal statuses remain explicit diagnostics; a continuation run begins with the exact closed runtime log binding originRunId and sourceRef/consumedRef pairs, whose nested unknown fields, wrong origins/current run ids, duplicate identities, or malformed refs are rejected by readers; dsl.log emits script, internal enter/exit and resume metadata emit runtime, and missing source remains neutral legacy data; child timeout/cancellation waits only a bounded interval for SDK abort acknowledgement before preserving evidence and disposing",
    "workflow live completion prefers result.summary, then scalar result.verdict, then a string result, otherwise reports completed; main TUI, tool, RPC, and headless outputs never dump raw JSON, which stays in status detail and result.json",
    "workflow results cross one JSON-safety boundary owned by prepareWorkflowResult: null persists as a valid success; undefined, unsupported BigInt, circular, and throwing-toJSON values become an explicit diagnostic sentinel and force the outer persisted envelope to ok:false; at a direct parallel()/pipeline() boundary the same preparation diagnostic becomes a typed group failure before semantic classification, remains separate from the exact three returned-outcome kinds, and preserves the original raw branch or stage value in group evidence; mandatory result.json envelope/write failure forces the returned run to ok:false, retains typed resultPersistence evidence, and appends a runtime error journal line so read-side status remains failed",
    "the runner persists one closed disposition beside ok: completed, awaiting_operator, cancelled with operator_stop/session_shutdown/aborted reason, or failed; finish precedence is controlling abort, failed evidence, dsl.awaitOperator declaration, then completed, so a trusted script catch cannot turn an aborted run green; awaitOperator records one bounded last-call-wins declaration with optional generic operatorHandoff without changing the returned payload; all human/model projections consume one strict projector, legacy files without disposition fall back to ok, and malformed/future dispositions or unreadable result envelopes remain unknown",
    "after JSON detach, the returned-outcome classifier treats exact ok:false, partial:true, and status failed/blocked/cancelled as semantic non-success; at the root, failure statuses previously completed and now fail, while ok:false and partial:true remain failure; missing or non-boolean ok/partial, other statuses, nested fields, and non-object values retain legacy execution-success semantics",
    "semantic failure classification and formatting are owned by workflow-outcome, the fs-free rules module, while workflow-result owns only persistence of what those rules produce; the formatting is shared by transcript, tool/command result blocks, and live progress: technical error wins, otherwise non-empty result summary plus stable unresolvedRows is preserved, with a generic fallback only when neither exists",
    "the packaged benchmarks/weak-model-workflow-v2 evidence surface freezes an exact-baseline seed and identity inputs plus a self-contained adaptive executor/evaluator with embedded oracle/waivers; execute mode validates one approval before any child call, requires exact baseline/seed/red preflight, enforces owner-local sequential writes, fresh focused checker/reviewer/fixer sessions, mandatory replan and fail-closed no-progress/caps, rejects any HEAD change, hashes actual child JSONL, requires matched successful bash receipts including exact npm run check, runs host-owned focused/owner/docs/package checks, and diffs git snapshots around every child before creating independent-harness evidence; caller/model-provided criteria, evidence kinds, refs and digests are ignored, while the AST launch gate permits only audited Node built-ins and rejects local/package imports, re-exports, dynamic imports, and require; it is not a package.json#pi.extensions entry and cannot widen the default runtime surface",
    "workflow run identity is announced before the first journal event so zero-event runs install the same semantic completion surface as eventful runs",
    "workflow live progress and headless liveAgents text fallback render compact rows through the shared local AgentLivePanel contract with source-backed optional metadata only; the generic session viewer labels recorded terminal, cancellation, and failure text without assuming every writer produced an answer, while the workflow adapter alone verifies replay answer artifacts before projection",
    "workflow live rows use the same process-shared store as the agents entrypoint, so active/completed workflow children resolve through petname, last, ids, label, and the focused fleet menu",
    "resumeFromRunId links a fresh workflow run to a prior persisted run summary and replays that run's recorded agent calls; it is not Pi session continuation, and it never reuses the child's side effects, only its recorded answer text. A replayed call also does not re-read: replay is keyed on the request, never on the state the child observed, so a replayed stage answers about the tree as it was when it ran",
    "replay is keyed on each agent attempt's ordinal position plus its fully resolved request (prompt, agent, capabilityMode, maxToolCalls, model, modelRole, requireModelRole, timeoutMs, maxTurns, label, phase, workspaceMode, workspaceHandle); a choice call additionally records its return-contract version (now v3, one exact declared string), so a record written under an earlier contract is reported as return-contract-changed rather than blamed on the script as a key-mismatch, is named as a release boundary in the journal, and runs fresh; a resumed source that still declares a removed shaped-result option fails before the replay lookup with the same named error as a fresh run, so an old shaped receipt is never reinterpreted; each same-session correction is part of one recorded attempt. Replay is a strict prefix: the first key mismatch invalidates that call and every later call, including calls whose own prompt is unchanged. A recorded failure keeps its ordinal and always re-runs, and a call resolving to a non-project workspaceMode or carrying a workspaceHandle is never replayed even on a clean key match. The latch fires on key mismatch only, not on a changed outcome: a call that re-runs at a matching key does not break the prefix, so its successors keep replaying answers recorded in a run where that call had a different outcome; because timeoutMs and maxTurns carry no package default and enter the key only when declared, every record written while the former defaults were in force fails the key comparison and re-runs rather than serving text a child produced under a now-deleted implicit ceiling as though the declared budget had been in force",
    "replay-safety is scanned by the same AST scan that classifies import edges, never asserted by a meta field: the nondeterministic roots are Date.now, new Date, Math.random, performance.now/timeOrigin, crypto.randomUUID/getRandomValues and process.hrtime/uptime, and the scan folds direct access, computed access, access through globalThis/global/self, a computed global member, a parenthesised constructor, a rebinding of the root (const d = Date, const { random } = Math), and a named, default or namespace import of a nondeterministic node:crypto/node:perf_hooks/node:process export. entry-only coverage is unproven by construction because imported bytes are outside the scan; an unproven script still runs normally but is neither recorded nor replayed. dsl.now() and dsl.random() are the recorded, replayable alternatives",
    "the replay-safety scan is a filter, not a proof: it reads syntax, so access assembled at runtime (globalThis[k] with a computed k, Reflect.get, a root threaded through a parameter), a value smuggled in through process.env or argv, and anything inside an imported module stay invisible to it. The gate that actually prevents a wrong replay is the per-call request key plus the strict prefix latch, which fails closed on any nondeterminism that reaches a prompt or changes call order or count",
    "resume continues a repaired workflow from its stop point: completed nodes return recorded answers, the repaired node and its tail run fresh, and replay refuses whole with a named reason written to the journal and result.json \u2014 source-run-unusable, target-changed, identity-coverage-unproven, replay-unsafe-script, no-recorded-calls \u2014 while every refused or unresolvable call runs for real; no code path fabricates a recorded answer",
    "a run that reused recorded calls is marked as replayed on every surface: replayed:true on agent_start/agent_end journal lines, a typed replay envelope in result.json, replayed=<n> plus a not-fresh-evidence line in /workflows status, [replayed] markers in the status timeline, replayed=<n> in the live progress panel header, and [replayed] plus a replayed count in the bounded command/tool lifecycle digest that enters LLM context; a replayed call reports no usage, so the run budget counts only work that actually happened",
    "bare DSL agent(prompt) starts a clean child using the current parent model and full host tool surface; explicit opts.agent selects an exact project or user profile; ordinary agent calls and Fusion agent mode receive allowedTools [\"*\"] and remain writable, while tool-free Fusion is the explicit exception with empty allowedTools plus host noTools enforcement; legacy per-call capability fields are ignored",
    "model routing resolves opts.model then opts.modelRole then the agent's frontmatter tier then ctx.model through the host model registry, and the resolved model is what the child session is created with; an unresolvable concrete provider/id selector fails the call by name with no child spawned, an unassigned role normally degrades to ctx.model and records modelRoleFallback, while opt-in requireModelRole requires an explicit role, refuses a simultaneous concrete model, and refuses an unassigned role before fresh child creation; agent_start records that strict declaration and agent_end carries executedModel read back from the child session, while replay may reuse original recorded evidence because it creates no child; an agent frontmatter tier written in the pre-tier pi/<role> namespace is read as that role so a catalog copied from an older release still resolves, while pi/<not-a-role> and per-call selectors stay fail-closed",
    "parallel()/pipeline() run through a bounded scheduler seam whose width bounds one call rather than the whole run, so nested fan-out is held instead by the run-wide leaf-agent concurrency gate the budget contract feeds; workspaceMode worktree/temporary-worktree agents receive serialized human approvals and retained per-call git worktrees before child execution",
    "parallel()/pipeline() are fail-closed full barriers for ordinary branch failures: scheduled siblings settle, then a thrown branch/stage, a direct value whose prepareWorkflowResult JSON detachment reports a diagnostic, or a successfully detached direct value with ok:false, partial:true, or status failed/blocked/cancelled rejects one WorkflowGroupFailureError with stable WORKFLOW_GROUP_FAILURE code, ordered discriminated slots/partialResults that preserve original returned values, indexed branch and optional stage evidence, and accurate total/completed/failed counts; preparation diagnostic is a separate group-boundary failure rather than a fourth returned-outcome kind; direct non-JSON-safe values and direct group partial:true could previously pass the barrier and now fail closed, while direct ok:false and failure statuses remain failure; the human message groups identical branch messages and previews at most three distinct causes while the structured envelope retains every branch failure; pipeline stops later stages for a failed item, all-success arrays preserve ordering, fulfilled null remains a successful value distinct from a failed slot, and WorkflowInvocationCapError remains a separate hard run-level failure",
    "an uncaught WorkflowGroupFailureError is projected by the runner into a JSON-safe workflow_group_failure result envelope without branch values and with both inner and outer ok:false; deliberate partial continuation must catch only WORKFLOW_GROUP_FAILURE, rethrow other errors, and return JSON-safe partial:true evidence, which remains semantic non-success; trusted JavaScript can broad-catch, so this is an authoring contract rather than an enforcement boundary",
    "parallel()/pipeline() emit local group_start/group_end journal lines and group live rows with accurate completed/failed barrier counts; author-known arrays and caller-supplied dsl.items() may feed the same visible group body; durable fixed items may instead invoke one saved child level through invokeWorkflow(), with the complete unique safe key list validated before the first child, source-bound atomic terminal-success checkpoints, cycle and saved-grandchild rejection before model work, root/child lineage and independent run evidence, one fenced root lease per workflow workspace, and live project-source policy journaled at the run boundary; no group or child receives acceptance semantics, async detach, recursive delegation, a workflow-side ledger, domain parsing/rendering, or custom recovery",
    "fails closed with an honest reason when the host cannot spawn a child agent session",
    "standard workflow source supports exact text, exact choice and bounded literal-schema JSON: plain agent(prompt) returns the exact full child text (result:\"report\" keeps whole execution reports), agent({choice}) returns one exact declared string, and agent({schema}) returns immutable structured JSON; handoffs, validate, output, repair, outputTransport, returnVia, maxAnswerChars and schemaMaxLength are refused by name before any child starts, before the replay lookup, with exact text, choice, schema constraints or caller-assigned files as appropriate; fusion schema and validate are refused the same way and every Fusion leg returns text; every choice attempt exposes typed schemaValidation {status:valid|mismatch, attempts, errors} on agent_end and is carried ONLY by same-session workflow_return acceptance, which accepts exactly one declared string and refuses arrays, objects and stringified JSON; a plain child is never given workflow_return; a host that cannot register the return tool and read its active tool set back refuses the choice call by name before the child starts; choice refuses non-arrays, fewer than 2 values, blank and duplicate members before any child, and bounds neither the number of options nor the length of one; the prompt and journal state the contract and its one package-owned same-session clarification turn, attempts count against maxTotalAgentInvocations, choiceFallback applies only when that clarification is exhausted, and a mismatch otherwise throws SchemaValidationError; a child run failure stays WorkflowAgentExecutionError and consumes no value retry, and plain agent(prompt) keeps the exact-text contract with no parser or prompt augmentation; both standard and orchestration-only source checks admit structured v4 with a direct explicit options object, a literal label, and literal schema data or one unshadowed top-level literal const; await before reading required declared fields or using proven array length, synchronous JSON map projections and for-of; pending Promise projections remain refused, and function projections are consumed only as owned parallel branches; optional or unknown fields, unchecked indexing, dynamic schemas and unproven array items remain outside checked consumption, and plain model text stays opaque; structured v4 uses the same workflow_return tool carrying the actual caller schema; Pi >=1.0.0 and the verified openai-codex raw/admission capabilities are required, strict:prefer is requested only for closed/all-required objects, typed arrays and no grapheme length bounds, local schema/raw-terminal validation remains authoritative, and the package owns two submissions total; native v5 and removed custom-validation evidence remain readable but cannot replay as current acceptance",
    "agent() is the DSL's ONLY model-calling primitive; there is no direct one-shot completion node, no pi-ai completeSimple/streamSimple path, and no llm_start/llm_end/llm_delta journal kind. The run-level token/cost budget shown by /workflows status sums usage from agent_end and from the sole post-child error terminal used when script validation or artifact adoption throws after execution; transport errors that never produced an answer carry no usage",
    "a model-called workflow owns one native Locus card with no default duplicate shell: a single header names the workflow and RUNNING, COMPLETED, AWAITING OPERATOR, CANCELLED, or FAILED state; theme-colored rail and explicit child-agent rows keep runtime annotations separate from unrailed workflow-produced prose, preserve semantic status colors, and bound compact/narrow layouts without hardcoded ANSI colors",
    "a finished run's terminal text is readable in one step: a prose result is persisted verbatim as outputs/workflow-result.md, the native tool's operator card renders it whole outside the technical rail while model-facing tool content remains bounded, the chat digest and live panel name the file plus the exact command, and /workflows result [runId|last] opens the whole untruncated text in a scrollable read-only screen while a host without custom UI gets a bounded preview plus the exact path; structured results stay in runtime/result.json, and both /workflows result and /workflows status accept the short run suffix every surface prints, last, or a full run id, and an ambiguous suffix is refused with listed candidates",
    "workflow run evidence has one canonical project-local root at .locus-pi/runs/<runId>/; one centralized path module owns .locus-pi, runs, plans, workflow-state, workflow-output-state, and retired .pi/locus-pi runs/workflows lookups; the runner creates only non-symlink outputs and runtime directories and writes the first runtime/journal.ndjson line before onRunStart, so an announced RunID always has visible storage and initialization failure starts no child; old .pi/locus-pi/runs/<runId>/ and .pi/locus-pi/workflows/<runId>/ evidence is left untouched and receives a named migration message with no fallback read",
    "each native runtime workspace has a project-local fenced lease recording its run id, pid, acquisition time and opaque token; ordinary launch reclaims a lease only after same-host liveness proves its owner dead, while --force additionally requires the referenced run's complete internally consistent terminal result envelope; dead/forced replacement is serialized by an exclusive reclaim guard, a persistent guard fails closed with its exact project-relative path and is never auto-removed, the guard creator removes only its own token on every exit, and a normal acquirer that wins the stale-rename/create gap is never displaced; diagnostics keep project-local paths relative and explain Ctrl+Z/fg plus explicit OS-specific stop and exact-state-file recovery; arbitrary caller-assigned destinations are not locked, so operators serialize roots sharing fixed domain files",
    "task is a group-only Package namespace with three manual children: task/draft publishes an editable orchestration brief, and task/plan or task/plan-light consumes the complete accepted text to publish one checked workflow.mjs; the root is not runnable, the children never invoke one another, and none runs generated source",
    "task/draft is a standard two-agent Package workflow: one reconnaissance agent inspects only the smallest live project surface that can change the graph, then one drafting agent publishes draft.md with literal Task, Draft goal, Context, Workflow direction, and Draft direction sections covering semantic input, one primary output, selected pattern, agents, exact handoffs, bounded reflection or review, concurrency, and fail-closed exits; the operator may copy and edit the complete text before task/plan",
    "task/plan receives the complete accepted draft and exact caller-assigned source/review/decision-log paths as one opaque input, writes workflow.mjs through ordinary agent tools and performs node --check plus project-confined workflow_check_source orchestration-only; at most three independent reviews and two revisions use the same actual file, preserve task constraints in every helper/router and retain checked/reviewed-byte SHA-256 evidence; acceptance returns the whole review report, while exhaustion returns explicit review_exhausted; native completion does not attest file delivery, and the caller reopens and rechecks the assigned regular nonempty source before execution",
    "task/plan-light designs and reviews a node-and-edge ledger from the complete accepted draft with exact source/design/queue/log/check/review/correction paths assigned through whole input, then grows that same source through at most six runnable graph-node slices; seed and slice mechanical checks plus independent design reviews retain source-free reports and bounded correction/recheck; required deliverables, execution constraints, decision-log history and named pending-edge replacement remain binding; final whole-file checks and successful review are required, but no DSL file publication occurs; all failure and exhaustion routes preserve assigned files as non-success; replay reuses answers, not writes, and the actual reader refuses absent or drifted source before execution",
    "post-code-review is a read-only seven-child Package workflow that freezes one review target, runs independent boundary/simplicity/contract/style lanes, challenges necessity, and synthesizes one code-shape decision; every fresh child requires an assigned smol role, replay stays marked as not-fresh evidence, lane question ids survive necessity and synthesis with exactly one closed disposition, simplicity records delete-first contraction and runtime caller evidence, and synthesis makes current-change dead surface, fake configurability, duplicated invariant ownership, stale derived documentation, misleading behavior descriptions, unearned seams, and open delete/rewrite/owner moves REQUIRED while keeping impact separate and final QA outside this workflow",
    "Workflow launch defaults are mode-scoped: every run defaults to `concurrency = 4`; headless Pi `print`/`json` root launches additionally default to `totalAgents = 10_000`, shared across fresh physical child attempts made by the root, saved children and Fusion; `totalAgents` is unbounded in TUI/RPC. Every other undeclared workflow budget axis is unbounded. An explicit number replaces the applicable default; saved children inherit the root's resolved budget instead of resolving defaults again, and a parallel()/pipeline() group with no local width uses the resolved run concurrency; all six axes print in the run header, journal, result.json and run report, with an unbounded axis shown as the literal unbounded (no timer armed, no counter refusing a child); none of the axes bounds the SIZE of an answer, and the removed answerChars axis and the removed agent maxAnswerChars option are both refused by name with a message naming the consumer contract to declare instead; the run-level axes concurrency/totalAgents/runtimeMs are declarable only through host-side RunWorkflowScriptOptions.budget, while maxToolCalls, timeoutMs and maxTurns are per-call agent() options; an explicit axis is checked before the next spend and reaching it ends the run as stopped by budget <axis> with every answer already received kept and readable, never as an invalid answer; a declared timeoutMs reaches the SDK host unchanged as one deadline, is wall clock including operator ask waits, and a value above Node maximum timer delay runs as a chain of representable waits instead of being refused or clamped; a narrowing is silent and a raise above an applied value emits a runtime-source journal line; every run journals the applied budget, and .locus-pi/runs/<runId>/outputs/README.md prints each axis against gate-owned peak concurrency, fresh and replayed agent attempts counted separately, run wall clock, longest child and observed tokens, printing unobserved axes honestly; replayed calls start no child and are not charged against totalAgents; cost is reported as unavailable rather than zero"
  ],
  "stateUsed": [
    ".locus-pi/workspaces/<run-name>/ for named native runtime coordination; existing legacy-only .locus-pi/plans/<run-name>/ workspaces stay physically bound; agent files use exact prompt destinations",
    ".locus-pi/runs/<runId>/outputs/README.md plus semantic documents and workflow-result.md",
    ".locus-pi/runs/<runId>/runtime/journal.ndjson",
    ".locus-pi/runs/<runId>/runtime/replay.ndjson",
    ".locus-pi/runs/<runId>/runtime/script-<sha256>.workflow.mjs",
    ".locus-pi/runs/<runId>/runtime/resources/<sha256>-<resource>.md",
    ".locus-pi/runs/<runId>/runtime/result.json",
    ".locus-pi/workflow-state/v1/<workspace-hash>/ for active workspace lease.json ownership, temporary reclaim.json serialization guards, and atomic source-bound saved-child checkpoints; the namespace may be empty after temporary ownership state is released",
    ".locus-pi/workflow-output-state/ historical lease records remain readable; fresh execution allocates no final-output roots or leases",
    ".locus-pi/workspaces/<generated-run-name>/ for default native runtime coordination, or named/confined workspaceDir selections; workflow-state/v1 owns the surviving native lease and checkpoints/v3, launch-binding.v3 is output-free, every fresh root has distinct lineage, and old output-format execution is inspectable but requires a fresh migrated run before resume/recovery/checkpoint reuse; user-file destinations are explicit prompt instructions, never native storage defaults",
    ".locus-pi/runs/<runId>/runtime/operator-handoff-claim.json when an actionable handoff is claimed; mutable claim metadata never becomes an artifact and never stores the answer",
    ".locus-pi/runs/<runId>/runtime/artifacts/index.json plus answers, fresh child transcripts/result envelopes, workflow-published text, and verified prior-run input copies",
    ".locus-pi/logs/errors.jsonl for append-only cross-run workflow failure summaries; canonical per-run evidence remains under .locus-pi/runs/<runId>/",
    "in-memory workflow journal + intermediate JS results",
    "process-local background-run registry keyed by stable session/project identity, with one exclusive active slash run plus non-exclusive tool controls, generation-scoped callback leases, typed abort reasons, controllers, and terminal promises",
    "process-shared agentLiveStore.rows parent/child live-row state for workflow progress rendering and agents-entrypoint drill/control, with explicit workflowRunId provenance, command-only workflow stop eligibility, display-time collapse of workflow parent rows after SDK child rows exist, and store-owned retention/pruning of workflow subtrees: newest five fully terminal runs remain available while older terminal runs are removed; active, queued, and zero-row runs are untouched",
    "AgentLiveRow optional metadata and group rows for source-backed live progress details",
    "up to three Pi session custom messages per prose command run: one idle-checked locus-workflow-run boundary banner at launch, then after idle one locus-workflow-result containing the exact untruncated terminal prose, then one bounded terminal locus-workflow-run digest (all participate in LLM context); non-prose command runs omit the middle result message, and the native workflow toolResult keeps one bounded tool lifecycle digest",
    ".locus-pi/runs/<runId>/runtime/artifacts/transcripts/<callId>/*.jsonl (fresh per-child SDK session evidence)",
    ".locus-pi/fusion/config.json version 2 for project-local enabled state, required homogeneous tool-free or agent mode, concrete member selectors, and the separate judge selector",
    "user or project .agents/skills/.locus-pi-workflow-skills.v1.json and .claude/skills/.locus-pi-workflow-skills.v1.json for managed external-agent skill-link provenance"
  ],
  "provides": {
    "tools": ["workflow", "workflow_check_source", "fusion"],
    "commands": ["workflows", "workflow-stop", "fusion"],
    "hooks": ["session_start", "session_shutdown", "input", "turn_end", "agent_settled", "tool_result"]
  },
  "uiLifecycle": {
    "commands": [
      {
        "name": "workflows",
        "taxonomy": ["transient-widget", "status", "overlay-selector", "artifact-write", "no-ui"],
        "transient": ["widget:workflows", "status:locus contribution workflow.run"],
        "persistent": [".locus-pi/runs/**"]
      },
      {
        "name": "workflow-stop",
        "taxonomy": ["transient-widget", "status", "artifact-write", "no-ui"],
        "transient": ["widget:workflows", "status:locus contribution workflow.run"],
        "persistent": [".locus-pi/runs/**"]
      },
      {
        "name": "fusion",
        "taxonomy": ["transient-widget", "persistent-state", "artifact-write", "no-ui"],
        "transient": ["widget:fusion", "widget:fusion-live"],
        "persistent": [".locus-pi/fusion/config.json", ".locus-pi/runs/**"]
      }
    ]
  },
  "permissions": {
    "filesystem": {
      "read": ["*"],
      "write": ["*"]
    },
    "subprocess": ["*"],
    "network": ["*"],
    "browser": true,
    "models": true,
    "ui": ["setWidget", "setStatus", "notify", "custom", "select", "input", "editor", "setEditorText"]
  },
  "risk": "critical",
  "docsPath": "extensions/workflows/README.md",
  "tests": [
    "tests/extensions/workflows/authoring/workflow-authoring-contract.test.ts",
    "tests/extensions/workflows/tool/workflow-source-check-tool.test.ts",
    "tests/contracts/host/workflow-error-results.test.ts",
    "tests/extensions/workflows/examples/task/publication/task-source-placement.test.ts",
    "tests/extensions/workflows/runtime/workflow-agent-choice.test.ts",
    "tests/extensions/workflows/runtime/workflow-agent-output.test.ts",
    "tests/extensions/workflows/runtime/workflow-artifacts.test.ts",
    "tests/extensions/workflows/runtime/workflow-budget.test.ts",
    "tests/extensions/workflows/runtime/workflow-continuation.test.ts",
    "tests/shared/agent-runtime/agent-read-only-policy.test.ts",
    "tests/extensions/workflows/runtime/workflow-replay.test.ts",
    "tests/extensions/workflows/runtime/workflow-script-identity.test.ts",
    "tests/extensions/workflows/runtime/workflow-run-snapshot.test.ts",
    "tests/extensions/workflows/runtime/workflow-group-failure.test.ts",
    "tests/extensions/workflows/runtime/workflow-handoff.test.ts",
    "tests/extensions/workflows/runtime/workflow-input.test.ts",
    "tests/extensions/workflows/runtime/workflow-result.test.ts",
    "tests/extensions/workflows/runtime/workflow-run-report.test.ts",
    "tests/extensions/workflows/run/workflow-identity-projection.test.ts",
    "tests/extensions/workflows/run/workflow-run-viewer.test.ts",
    "tests/extensions/workflows/catalog/workflow-catalog.test.ts",
    "tests/extensions/workflows/catalog/workflow-catalog-viewer.test.ts",
    "tests/extensions/workflows/catalog/workflow-copy.test.ts",
    "tests/extensions/workflows/command/skills.test.ts",
    "tests/extensions/workflows/command/menu.test.ts",
    "tests/extensions/workflows/examples/task/task-draft-workflow.test.ts",
    "tests/extensions/workflows/examples/task/task-plan-workflow.test.ts",
    "tests/extensions/workflows/examples/post-code-review/review-prompt-config.test.ts",
    "tests/extensions/workflows/tool/workflow-diagram-artifacts.test.ts",
    "tests/extensions/workflows/transcript/workflow-transcript.test.ts",
    "tests/extensions/workflows/run/workflow-background-runs.test.ts",
    "tests/extensions/workflows/launch/workflow-command-completions.test.ts",
    "tests/extensions/workflows/launch/workflow-command-launcher.test.ts",
    "tests/extensions/workflows/command/run-receipts.test.ts",
    "tests/extensions/workflows/operator/workflow-handoff-integration.test.ts",
    "tests/extensions/workflows/operator/workflow-operator-handoff-controller.test.ts",
    "tests/extensions/workflows/operator/workflow-operator-handoff-service.test.ts",
    "tests/extensions/workflows/launch/workflows-launch-gate.test.ts",
    "tests/extensions/workflows/tool/workflow-tool-card.test.ts",
    "tests/extensions/workflows/operator/workflow-progress-install.test.ts",
    "tests/extensions/workflows/operator/workflow-progress-render.test.ts",
    "tests/extensions/workflows/operator/workflow-progress-state.test.ts",
    "tests/extensions/workflows/operator/workflow-progress-surfaces.test.ts",
    "tests/contracts/extensions/runtime-registration.test.ts",
    "tests/integration/workflow-run-skill.test.ts",
    "tests/extensions/workflows/run/workflow-result-text.test.ts",
    "tests/extensions/workflows/runtime/workflow-fusion.test.ts",
    "tests/extensions/workflows/fusion/fusion-tool.test.ts"
  ]
}
