{
  "id": "workflows",
  "name": "workflows",
  "version": "0.1.0",
  "defaultEnabled": true,
  "behaviorStatus": "active",
  "ownershipStatus": "locus-specific",
  "sourceMode": "locus-owned",
  "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",
    "saved workflow names resolve project .pi/workflows first (canonical), then .claude/workflows and .agents/workflows interop sources, then ~/.pi/workflows, then the explicit curated Package registry; files under extensions/workflows/examples are not registered merely because they exist",
    "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, records operator-command launch status, and starts runWorkflowScript without passing through model-tool approval or opening a second Locus-owned prompt",
    "/workflows run checks the real Pi command context ctx.isIdle before target resolution, transcript creation, or execution; because Pi 0.80.3 invokes 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",
    "/workflows status normal view owns one-screen truncation with newest 10 evidenced runs plus total/shown/older and older-run summary; run discovery requires journal.ndjson or result.json plus a canonical UTC id prefix or a persisted journal timestamp, orders legacy ids by that timestamp, and ignores artifact-only directories",
    "static workflow help, passive list/info fallbacks, status/detail, launch errors, and headless settled receipts use typed operator blocks; interactive TUI /workflows list and info use workflow-owned focused custom components, while the existing live progress panel remains domain-owned",
    "workflow history uses [R] independently from persisted source tags [P]/[U]/[PKG]; TUI bounds status/detail to 10/20 rows, RPC to 4/1 rows, and all variants point to result.json for complete 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",
    "agents catalog discovery + task/createAgentSession host required to spawn sub-agents",
    "DSL agent/parallel/pipeline/phase/log routes every sub-agent through executeAgentRunBoundary with createAgentSdkSessionExecutor",
    "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 transient key pinned while running; after completion, the next input/registered command cleanup or Pi turn_end removes the large widget/status/transient 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: command and tool runs buffer lifecycle with zero active-run sendMessage calls; after completion a command updates its UI, awaits ctx.waitForIdle, rechecks ctx.isIdle, then immediately appends one visible locus-workflow-event digest with triggerTurn:false and no deliverAs, while the 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 first-wins resolver and persisted run index; Project/User directories are scanned on every call while Package rows are filtered by CURATED_PACKAGE_WORKFLOW_NAMES, so list, info, bare-name run, and unknown-name recovery use one explicit Package registry; each selectable row is a two-line block with badge/source/name/description first and an indented exact origin path second, middle truncation preserves path beginning and basename, and very low terminals use a compact one-line fallback; current and run-specific history rows are inspectable, the custom viewer uses all terminal rows except Pi's three footer/status rows with no fixed 24-row 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 path or history run id/path/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 prompt handoffs, not execution: a ready current source offers Back, Start, Edit, and Review; history offers only Back and Review; Tab 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 ctx.ui.custom completes may the command prefill the restored main editor through setEditorText with Request, Skill: $pi-workflow-authoring, and Additional instructions; the skill owns Start/Edit/Review/diagnosis procedure while no browser action submits the prompt, 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, filesystem precedence, static metadata, trust, DSL primitives, agent catalog selection, model-role metadata, and actual per-call/session model routing; named info resolves through the same first-wins catalog, reports exact source/path and bounded literal meta.description, and fails explicitly for unknown names; 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, invents an execution graph, mutates the editor, or writes files, and this owner-local view does not change generic Ctrl-O, cat, or terminal scrollback",
    "workflow catalog descriptions come only from a bounded static AST scan of top-level literal export const meta, accepting unquoted or quoted literal description keys; listing never executes modules or accepts comments, unrelated description fields, computed metadata, or interpolated descriptions",
    "workflow log journal lines carry optional source script/runtime provenance; dsl.log emits script, internal enter/exit and resume metadata emit runtime, and missing source remains neutral legacy data",
    "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: null persists as a valid success; undefined, BigInt, circular, and throwing-toJSON values become an explicit diagnostic sentinel and force the outer persisted envelope to ok:false; a JSON-safe top-level result with ok:false or partial:true is semantic non-success even when a deliberate partial has no error string; 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",
    "after JSON detach, a script result object with boolean ok:false propagates to the run envelope/status and cannot render as successful; missing or non-boolean ok retains legacy execution-success semantics unless top-level partial:true",
    "semantic failure formatting is owned by workflow-result and 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",
    "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; it is not Pi session continuation",
    "bare DSL agent(prompt) defaults to catalog agent default; explicit opts.agent still selects quick_task or any other catalog agent",
    "model routing keeps per-call opts.model precedence, preserves agent frontmatter model in the request capsule through model-role resolution, and otherwise falls back to the current session model for the child executor",
    "parallel()/pipeline() run through a bounded scheduler seam; 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 or direct return with ok:false or status failed/blocked/cancelled rejects one WorkflowGroupFailureError with stable WORKFLOW_GROUP_FAILURE code, ordered discriminated slots/partialResults, indexed branch and optional stage evidence, and accurate total/completed/failed counts; 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 without adding dynamic fanout, acceptance, async detach, or control semantics",
    "fails closed with an honest reason when the host cannot spawn a child agent session",
    "schema-bearing agent()/llm() results expose typed schemaValidation {status:valid|mismatch, attempts, errors}; successful recovery and exhausted parser/validator mismatch mirror to terminal journal lines, while raw child/model failure is never relabelled as schema outcome",
    "DSL llm(prompt, opts?) makes ONE direct model completion via pi-ai completeSimple (streamSimple when opts.stream; no child session, no tools); model resolved via the same per-call getModel/ctx.model routing as agent(), then ctx.modelRegistry.getApiKeyAndHeaders(model) supplies the API key, provider/model headers, and provider environment required by the direct call; opts.schema parses+validates result.text as JSON with retry; journals llm_start/llm_end (+ usage/model and a bounded first diagnostic on failure) and llm_delta on stream; status/detail and the final workflow receipt retain that failure evidence, run-level token/cost budget is summed from llm_end usage, and missing model/auth or call errors fail closed to ok:false without fabricated text"
  ],
  "stateUsed": [
    ".locus/runtime/workflows/<runId>/journal.ndjson",
    ".locus/runtime/workflows/<runId>/script-<sha256>.workflow.mjs",
    ".locus/runtime/workflows/<runId>/result.json",
    "in-memory workflow journal + intermediate JS results",
    "process-shared agentLiveStore.rows parent/child live-row state for workflow progress rendering and agents-entrypoint drill/control, with 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",
    "one post-idle Pi session custom message with customType locus-workflow-event for the bounded command digest (participates in LLM context), plus the native workflow toolResult for one bounded tool lifecycle digest",
    ".locus/runtime/reports/agent-sdk-*.jsonl (per-child evidence via the SDK host)"
  ],
  "tier": "core-owned",
  "provides": {
    "tools": ["workflow"],
    "commands": ["workflows"],
    "hooks": ["input", "turn_end"]
  },
  "uiLifecycle": {
    "commands": [
      {
        "name": "workflows",
        "taxonomy": ["transient-widget", "status", "overlay-selector", "artifact-write", "no-ui"],
        "transient": ["widget:workflows", "status:locus contribution workflow.run"],
        "persistent": [".locus/runtime/workflows/**"]
      }
    ]
  },
  "permissions": {
    "filesystem": {
      "read": ["*"],
      "write": ["*"]
    },
    "subprocess": ["*"],
    "network": ["*"],
    "browser": true,
    "models": true,
    "ui": ["setWidget", "setStatus", "notify", "custom", "setEditorText"]
  },
  "risk": "critical",
  "docsPath": "docs/extensions/active/workflows.md",
  "sourceAuditPath": "https://github.com/kroffske/locus-pi/blob/main/docs/source-audit/workflows.md",
  "tests": [
    "tests/shared/workflows/workflow-script-identity.test.ts",
    "tests/shared/workflows/workflow-run-snapshot.test.ts",
    "tests/shared/workflows/workflow-group-failure.test.ts",
    "tests/extensions/workflows/workflow-identity-projection.test.ts",
    "tests/extensions/workflows/workflow-catalog.test.ts",
    "tests/extensions/workflows/workflow-catalog-viewer.test.ts",
    "tests/extensions/workflows/requirements-grill-workflow.test.ts",
    "tests/extensions/workflows/workflow-transcript.test.ts",
    "tests/shared/workflows/workflow-llm-bridge.test.ts",
    "tests/extensions/workflows/workflows-launch-gate.test.ts",
    "tests/extensions/workflows/workflows-progress.test.ts",
    "tests/integration/public-registration.test.ts"
  ],
  "review": {
    "status": "reviewed",
    "source": "write-from-scratch",
    "reviewedBy": "locus-pi",
    "reviewedAt": "2026-07-10"
  }
}
