# Agentic AI Studio

The Agentic AI Studio is V1's **governed autonomy plane** — the registry,
run-envelope, budget, kill-switch, grant-resolver, hand-off, mode, and
champion-challenger machinery that lets agents do real work (research, drafting,
fact-checking, narration, course generation, support triage) without ever
escaping the platform's tone, grounding, entitlement, and cost guardrails. It
serves operators (who arm kill switches and promote challengers), creators and
customers (who invoke entitlement-gated pipelines), and every domain that wants
to ship an autonomous workflow. It sits beside the platform substrates hubbed at
[../ARCHITECTURE.md](../ARCHITECTURE.md) and composes them: it grounds through
[Sophia](./substrate-sophia.md), enforces tone through
[Lilith](./substrate-lilith.md), generates through [Isis](./substrate-isis.md),
and remembers through [Iris](./substrate-iris.md).

> **Read this page for what is _shipping_ vs. _spec / provider-gated_.**
> Unusually for this doc set, the Studio is **overwhelmingly real**. The entire
> governance/orchestration brain lives in `libs/oshun/agentic-studio`
> (`@oshun/agentic-studio` v0.1.0), a pure-TypeScript package whose `index.ts`
> re-exports **12 subsystems** — `registry`, `runs`, `plans`, `dashboard`,
> `invocation`, `budgets`, `capabilities`, `feedback`, `modes`, `pipelines`,
> `grants`, `handoff` — backed by **271 passing tests across 16 files** that
> assert real computed values, not truthiness. Three things are genuinely
> thinner than the prose below might imply: (1) the **persistence is in-memory**
> — the `AgentRegistry` is a `Map`, and the BFF run-lifecycle store is
> in-process; the hub's old "durable jobs through `@oshun/queue`" line is
> **not** wired here (agentic-studio imports no `@oshun/queue`). (2) "Replay and
> time-travel" / "streaming progress" / "operator dashboard" are **data and
> query layers** (`dashboard/dashboard-query.ts`, `runs/replay.ts`,
> `runs/streaming-progress.ts`), not a rich admin React page — the admin surface
> is **API routes**, and the only agent _page_ is a single
> `apps/oshun/tenant-admin/src/app/agents/page.tsx`. (3) Tool **grants are
> declared and validated**, but actual tool **execution** (the real `web.fetch`,
> `sophia.ground`, …) is **injected at the app boundary and fail-closed by
> default**. So: the brain is real and tested; the "autonomous content gets
> produced end-to-end through live providers" story is **seam-and-fail-loud**,
> not wired to live vendors. Backlog: §18; product scope:
> [`V1/features.md` § Agentic AI Studio](../features.md#agentic-ai-studio).

## Where the prose used to be wrong

Three claims on the hub page are corrected here so readers aren't sent to the
wrong code:

- **No durable queue at this layer.** ARCHITECTURE's "Job orchestration —
  durable jobs through `@oshun/queue` with priority classes, replay, DLQ, SLA
  monitor" is **aspirational at the agentic-studio layer**. The queue library is
  `libs/shared/queue`; agentic-studio's `runs/dispatcher.ts`,
  `runs/orchestrator.ts`, `runs/checkpoint.ts`, and `runs/replay.ts` are
  in-process pure-function logic with **no** `@oshun/queue` binding, no DLQ, and
  no SLA monitor.
- **No `.../agents/` operator dashboard page in admin.** The hub points at
  `apps/oshun/admin/src/.../agents/`; that directory does not exist. The admin
  agentic surface is the API route group
  `apps/oshun/admin/src/app/api/admin/agentic-operations/` (`kill-switches`,
  `snapshot`, `gold-sets/promote`, `champion-challengers/rollout`). The lone
  agent _page_ is `apps/oshun/tenant-admin/src/app/agents/page.tsx`.
- **Budgets are self-contained, not "integrated with metis cost-tracking."**
  `libs/metis/cost-tracking` exists, but `src/budgets/budgets.ts`
  (`BUDGET_CATEGORIES` / `BUDGET_SCOPES` / `checkBudget` / `consumeBudget`) does
  not import it. "Integrated with" overstates a dependency that is not present.

## Agent registry and catalog

Every agent is a versioned `AgentRegistryEntry` registered into an
`AgentRegistry` (`src/registry/agent-registry.ts`). Registration is **validating
and fail-closed**: `register(entry)` returns a typed
`AgentRegistryValidationError[]` and only inserts into the backing `Map` when
the array is empty. The eight validation codes are real and individually tested:
`duplicate-agent-id`, `unknown-family`, `tool-grant-exceeds-family-ceiling`,
`invalid-semver` (for the agent, a capability, or the model binding),
`unknown-data-scope`, `lifecycle-experimental-mismatch`,
`duplicate-capability-id`, and `champion-without-disclosure`. Semver is checked
against a real regex (`/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/`), so a
champion-tier agent cannot ship with a placeholder version or without its
declared disclosure requirements.

### The 20 agent families and their tool ceilings

`AGENT_FAMILIES` (`src/registry/agent-families.ts`) is exactly **20** canonical
families. Each carries `AgentFamilyMeta` with a `toolGrantCeiling` the registry
enforces as a **hard ceiling** — a declared `toolGrant` outside its family's
ceiling is rejected with `tool-grant-exceeds-family-ceiling`, so an `editing`
agent cannot quietly request `web.fetch`.

| Family                       | Family               | Family                  | Family                 |
| ---------------------------- | -------------------- | ----------------------- | ---------------------- |
| `research`                   | `drafting`           | `editing`               | `fact-checking`        |
| `citation-verification`      | `illustration`       | `narration`             | `translation`          |
| `course-generation`          | `lesson-scaffolding` | `assessment-generation` | `study-plan-synthesis` |
| `recommendation-explanation` | `moderation-triage`  | `support-triage`        | `ritual-scriptwriting` |
| `sky-event-briefing`         | `claim-extraction`   | `source-ingestion`      | `kg-promotion`         |

For example, `research`'s ceiling is
`['web.fetch', 'source.fetch', 'sophia.ground', 'memory.read']` with a
`strict-cite` default grounding policy, while `editing`'s is
`['memory.read', 'sophia.fact_check', 'persona.invoke']` — narrower, and
read-only on sources. Each family also declares `primaryDomains`,
`defaultGroundingPolicy` (`strict-cite` / `cite-where-available` /
`no-citation`), and `defaultPersonaPolicy` (one of the Lilith voices
`lilith.teacher` / `lilith.guide` / `lilith.scribe` / `lilith.host`).

### Lifecycle, cost class, and data scope enumerations

| Enumeration              | Values                                                                                                                                              |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AGENT_LIFECYCLE_STATES` | `draft`, `rehearsal`, `shadow`, `champion`, `challenger`, `deprecated`, `retired`                                                                   |
| `AGENT_COST_CLASSES`     | `micro`, `small`, `medium`, `large`, `flagship`                                                                                                     |
| `DATA_SCOPES` (8)        | `tenant.public`, `tenant.private`, `user.public`, `user.private`, `editorial.draft`, `editorial.published`, `platform.metadata`, `platform.curated` |

An `AgentRegistryEntry` additionally pins a `modelBinding` (provider, model,
semver, default max tokens / temperature, and an explicit `fallbackProviderId` /
`fallbackModelId`), a `personaBinding`, a `tonePolicyId`, a `groundingPolicyId`,
an `auditPolicyId`, a `tenantScope` (`'platform'` or `{ tenantId }`), a
`visibility` set (`platform` / `tenant` / `creator` / `customer`), and
`disclosureRequirements`. `list(viewer)` filters the catalog by tenant and role
so a customer never sees a platform-only agent.

## The `AgentRun` envelope and its lifecycle

Every invocation produces an `AgentRunEnvelope` (`src/runs/agent-run.ts`) — the
durable, operator-visible shape of a run. It is the contract the hub gestures
at, named field by field:

| Field                                         | Meaning                                                                                                    |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `runId`, `rootAgentId`, `rootAgentSemver`     | identity of the run and its root agent                                                                     |
| `parentRunId`, `pipelineId`, `pipelineSemver` | attribution into a hand-off chain / pipeline                                                               |
| `status`                                      | one of `RUN_STATUSES` (below)                                                                              |
| `plan`                                        | `RunPlanStep[]` — DAG of steps with `dependsOnStepIds`                                                     |
| `toolCalls`                                   | `RunToolCallRecord[]` — each with `argsHash`, `outcome`, `costUnits`, `scopeCheckResultId`, `auditEventId` |
| `intermediateArtifacts`                       | checkpointable artifacts with a `persistedKey` for resume                                                  |
| `evidenceTrail`                               | per-stage `evidencePackId`, `groundingState`, and `citationCount`                                          |
| `costLedger`                                  | the `RunCostLedger` (below)                                                                                |
| `decisionRationale`                           | per-stage rationale text and a `decisionLabel` (e.g. `plan-replanned`, `gate-failed`, `abstained`)         |
| `outputs`                                     | `RunOutputBundle[]` (`final` / `intermediate` / `preview` / `partial-on-failure`)                          |
| `provenanceBundleId`                          | provenance handle carried through the hand-off chain                                                       |

### Run statuses — the real enum vs. the simplified diagram

The hub's mermaid state machine is a **simplified, aspirational** model: it uses
`pending`, `planning`, and `executing` states that **do not exist** in code. The
real `RUN_STATUSES` enum is:

```
queued · starting · running · paused · awaiting_approval ·
awaiting_tool · awaiting_branch_selection · cancelled · killed ·
failed · completed
```

`TERMINAL_STATUSES` is the set `{ completed, failed, cancelled, killed }`.
`killed` is deliberately distinct from `cancelled`: a `cancelled` run is an
operator's deliberate stop, a `failed` run errored, and a `killed` run was
terminated mid-flight by the governance executor (an armed kill switch or an
exhausted budget). The diagram also omits `paused`, `awaiting_tool`, and
`awaiting_branch_selection` — states that exist because runs really do suspend
on back-pressure, on a tool boundary, and on a branch decision.

### The cost ledger

`RunCostLedger` is a structured meter, not a single number — it tracks
`totalCostUnits`, per-stage and per-tool cost maps (`perStageCostUnits`,
`perToolCostUnits`), and physical resource counters: `inputTokenCount`,
`outputTokenCount`, `gpuMinutes`, `voiceSeconds`, `avatarSeconds`,
`retrievalCount`, and `externalCallCount`. These line up one-to-one with the
budget categories below, so a budget on `voice-seconds` meters against the same
quantity the ledger records.

## Run controls and the operator console

`src/runs/run-controls.ts` exposes the operator console as pure functions over
`(envelope, command) → ControlResult`. Each returns the next envelope plus a
typed `RunControlAuditEvent`, or a typed `ControlDenialReason` for an invalid
transition (`run-terminal`, `invalid-status-for-pause`, `no-such-branch`,
`fork-without-fresh-id`, …). The controls and their audit kinds:

| Control            | Behavior                                                                                           | Audit kind           |
| ------------------ | -------------------------------------------------------------------------------------------------- | -------------------- |
| `pause`            | suspend a pausable run (`queued`/`starting`/`running`/`awaiting_tool`/`awaiting_branch_selection`) | `pause`              |
| `resume`           | resume a `paused` run                                                                              | `resume`             |
| `cancel`           | operator-deliberate terminal stop                                                                  | `cancel`             |
| `kill`             | governance terminal stop                                                                           | `kill`               |
| `chooseBranch`     | pick a branch at `awaiting_branch_selection`                                                       | `branch-chosen`      |
| `fork`             | clone a run — **requires a fresh `runId`** (else `fork-without-fresh-id`)                          | `fork`               |
| `retryWithChanges` | rewind and apply overrides                                                                         | `retry-with-changes` |
| `operatorStep`     | single-step a run                                                                                  | `operator-step`      |

## The runtime governance seam (the real enforcement story)

This is the concrete enforcement the hub describes abstractly but never names.
Two pure modules and one orchestrator wrap **every** tool call:

1. **`admitToolCall`** (`src/runs/executor.ts`) runs the governance checks in a
   fixed **safety → cost → rate** order and returns the next state:
   - **Safety first.** `decideExecution(killSwitches, target)` — if any switch
     is active for the target, the run is `kill`-terminated with the switch's
     `userVisibleStatusCopy` and optional `safeDegradationModeId`. Kill switches
     **take precedence over everything**.
   - **Cost next.** For each budget, `checkBudget` is evaluated; an `exceeded`
     verdict is a **hard stop** (the run is killed with a budget-exhausted
     status copy), while `warn` / `grace` accumulate as `budgetWarnings` and let
     the run continue.
   - **Rate last.** An optional token-bucket throttle yields a `throttled`
     outcome with `retryAfterSeconds` — **back-pressure, never termination**.
2. **`dispatchGuardedToolCall`** (`src/runs/dispatcher.ts`) invokes the supplied
   `runTool(run)` **only when** `admitToolCall` returns `admit`; a denied,
   throttled, killed, or run-terminal admission never touches the tool.
3. **`runGuardedToolPlan`** (`src/runs/orchestrator.ts`) walks a
   `PlannedToolCall[]` plan through the dispatcher, producing
   `ExecutedToolCall[]` and a `GuardedToolPlanResult`.

### Where it mounts: `POST /v1/agentic/runs/execute`

The seam is mounted in the BFF at `apps/oshun/bff/src/agentic/runs-route.ts`
(`registerAgenticRunsRoute`). It is **fail-closed and server-authoritative**:

- **Fail-closed by default.** The default tool registry
  (`notConfiguredAgentToolRegistry`) has **no tools**, so any planned `toolId`
  that isn't a configured function returns **`503 agent_tools_not_configured`**
  — the route never fabricates a run. A live `web.fetch` / `sophia.ground` is
  only swapped in at deploy time.
- **Malformed → `400`.** Missing `runId` / `tenantId` / `rootAgentId` /
  non-empty `plan` of `{ toolId }`.
- **Scope check (S7) → `403 tool_scope_missing`.** A tool with a scope
  requirement runs only when the caller's **real** `authContext.scopes` satisfy
  it; the executor never invents an operator tier for whoever shows up.
- **Server-authoritative identity (S8).** `actorId` and `tenantId` are taken
  from `authContext` (the auth claim), not the request body — so a caller can't
  reshape the target an armed switch matches. Kill-switch target metadata
  (`family` / `providerId` / `region`) is keyed server-side per `toolId`.
- **Ownership (S9) → `409 run_id_conflict`.** A `runId` already persisted by
  another member is never overwritable.
- **Live kill-switch propagation.** Operator-armed switches are read from
  `adminAgenticOperationsStore` on **every request**, so arming a switch takes
  effect at the next tool-call boundary (the ≤5s propagation budget).

## Budgets, quotas, throttles, and kill switches

### Budgets

`src/budgets/budgets.ts` is self-contained. `BUDGET_CATEGORIES` (9) are
`cost-units`, `input-tokens`, `output-tokens`, `gpu-minutes`, `voice-seconds`,
`avatar-seconds`, `retrievals`, `external-calls`, `storage-bytes` — matching the
cost-ledger counters. `BUDGET_SCOPES` (7) are `tenant`, `role`, `agent`, `run`,
`tool`, `user`, `pipeline`. `checkBudget` returns one of four verdicts: `ok`,
`warn`, `grace` (carrying a `graceEndUnixSeconds` so a soft overrun has a
deadline rather than a cliff), and `exceeded`. Period rollover is supported, so
a budget refills at the next window.

### Kill switches

`src/budgets/kill-switch.ts` has `KILL_SWITCH_SCOPES` (7): `agent`, `family`,
`tenant`, `provider`, `region`, `tool`, `global`. `decideExecution` picks the
**broadest** active switch as the "blamed" switch for user-facing copy, using a
real blame priority:

```
global 6 > region 5 > provider 4 > tenant 3 > family 2 > agent 1 > tool 0
```

so a tenant operator sees the global-outage copy rather than a confusing
per-tool message when a global switch is armed. The decision returns a
`userVisibleStatusCopy` and an optional `safeDegradationModeId` (a graceful
fallback rather than a blank failure).

## Tool catalog, grants, and the grant resolver

### The 21-id catalog

The canonical tool catalog lives in **contracts**, not the studio:
`libs/contracts/src/agent/tools.ts`. `V1_TOOL_IDS` is exactly **21** entries
(the grouping obscures the count —
`generate.image`/`generate.video`/`generate.audio` are three,
`memory.read`/`memory.write` are two, `calendar.read`/`calendar.write` are two):

```
web.fetch · source.fetch · sophia.ground · sophia.fact_check ·
memory.read · memory.write · persona.invoke · generate.image ·
generate.video · generate.audio · composition.suggest ·
themis.adjudicate · code.exec · file.read · file.write ·
calendar.read · calendar.write · notify.send · handoff ·
approval.request · agent.terminate
```

`V1_TOOL_CATALOG` carries `catalogVersion: '1.0.0'` and
`publishedAt: '2026-05-11T00:00:00Z'`. Two real functions back the "tool
versioning / deprecations surfaced at registry-review time" promise:
`resolveToolDependencies` walks per-tool semver dependencies (returning
`unknown-tool` or `semver-range-no-match`), and `surfaceDeprecations` lists
tools whose `deprecation.deprecatedAt` falls inside a review window, along with
their `replacementToolId` and dependent tools.

### Grant scopes and resolver priority

`GrantScopeSchema` (`libs/contracts/src/agent/tool-grants.ts`) enumerates the
five scopes; the resolver (`src/grants/resolver.ts`) orders them by
`GRANT_SCOPE_PRIORITY` — **the most specific grant wins**:

| Scope                   | Priority |
| ----------------------- | -------- |
| `per-run`               | 100      |
| `per-pipeline-instance` | 80       |
| `per-session`           | 60       |
| `per-user`              | 40       |
| `per-tenant`            | 20       |

`resolveGrant` returns the highest-priority unexpired, unrevoked match, or a
typed `GrantDenialReason`: `no-matching-grant`, `all-matches-expired`, or
`all-matches-revoked`. The resolver also provides the lifecycle plumbing:
`recheckQueuedRunAtDispatch` (a grant revoked while a run sat queued is caught
at dispatch), `emitRevocationCascade` / `emitExpiryCascade` (propagate to active
sub-runs), `renewGrant` (rejects with `rationale-required` on empty rationale
and `audit-required` on a missing audit event id), and
`emitToolKillSwitchCascade`, which stamps each cascaded event with
`withinFiveSecondSlo` (`latency <= 5`) — the concrete ≤5s revocation SLO.

## Multi-agent hand-off protocol

`src/handoff/handoff-protocol.ts` enforces the parent→sub-agent contract. The
`APPROVAL_REQUIREMENTS` are `none`, `operator`, `teacher`, `guardian`, and
`user-confirm-before-proceed`; `DEFAULT_DEPTH_CAP = 5`. `validateDispatch`
rejects a dispatch with a typed `DispatchDenialReason`:

| Reason                        | Rule enforced                                                                                                                                                                      |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cycle-detected`              | re-entering an ancestor `agentId` is blocked unless `allowCycle` is declared at the chain root                                                                                     |
| `depth-cap-exceeded`          | hand-off depth must stay within the cap (default 5)                                                                                                                                |
| `authority-widening-rejected` | the sub-agent's tool authority must be a **subset** of the parent's, its `maxFanoutDegree` no larger, and its `capabilityModifiers` no broader — authority can narrow, never widen |
| `budget-non-positive`         | a sub-budget must be positive                                                                                                                                                      |
| `budget-exceeds-parent`       | a sub-budget cannot exceed the parent's remaining budget                                                                                                                           |

Budget is **deducted at dispatch** (`deductDispatchBudget`) and reconciled at
termination (`reconcileTerminalBudget`), which returns unused budget to the
parent and, on overrun, emits an `overshootEvent` of kind
`subagent_budget_exceeded`. Attribution is preserved via
`deriveAttributionChain` so artifact provenance carries the
`rootAgent → producingAgent` chain.

## Cost-quality modes

`src/modes/modes.ts` defines five `COST_QUALITY_MODES` with **concrete declared
thresholds** (not just qualitative labels):

| Mode                | `maxCostUnits` | `p95LatencyBudgetMs` | `minEvaluationScore` | `multiAgent` | `humanCheckpointCount` | `hasExternalSideEffects` |
| ------------------- | -------------- | -------------------- | -------------------- | ------------ | ---------------------- | ------------------------ |
| `fast`              | 50             | 1 500                | 0.6                  | false        | 0                      | true                     |
| `balanced`          | 500            | 10 000               | 0.7                  | false        | 0                      | true                     |
| `deep`              | 5 000          | 120 000              | 0.8                  | true         | 1                      | true                     |
| `exhaustive`        | 50 000         | 1 800 000            | 0.85                 | true         | 3                      | true                     |
| `rehearsal-dry-run` | 1 000          | 60 000               | 0                    | true         | 0                      | **false**                |

`rehearsal-dry-run` is the safety valve: it has **no external side effects**, so
a pipeline can be exercised end-to-end without touching live providers or
spending real resources. `checkModeCompliance` flags `cost-exceeded`,
`latency-exceeded`, and a below-bar evaluation score; `applyOverride` is
**tighten-only** — a customer or tenant operator may switch modes only within
the domain's `allowedModes`, and a `cost-raise-rejected` result blocks raising
the cost ceiling under `tightenOnly`.

## Capability isolation, secret access, and adversarial tests

`src/capabilities/` is the security boundary. `tool-isolation.ts` ships real
SSRF guards: `isPrivateOrLoopbackHost` rejects RFC-1918 ranges (10.0.0.0/8 →
`0x0a000000`–`0x0affffff`; 172.16.0.0/12; 192.168.0.0/16), loopback (127/8,
`localhost`, `::1`, `0.0.0.0`), link-local (`169.254.*`, `fe80:`), IPv6
unique-local (`fc/fd`), and IPv4-mapped IPv6 (`::ffff:` is unwrapped and
re-checked), returning a typed `EgressDenialReason` (`ssrf-private-ip` /
`ssrf-link-local` / `ssrf-loopback` / `host-not-in-allowlist` /
`scheme-not-allowed` / `method-not-allowed` / `invalid-url`). `matchHostPattern`
supports `*.` wildcard subdomains. `checkSecretAccess` denies with
`cross-tenant`, `agent-not-permitted`, or `tool-not-permitted`.

`adversarial-tests.ts` is the concrete security-test catalog —
`ADVERSARIAL_CATEGORIES` covers `prompt-injection`, `tool-call-exfiltration`,
`scope-escalation`, `sandbox-escape`, `persona-bypass`, `policy-bypass`, and
`source-fabrication`, each fixture declaring an `expectedVerdict` of `refuse`,
`sanitize`, or `comply-and-disclose`.

## Champion-challenger rollout and gold sets

`src/feedback/champion-challenger.ts` runs a **real** two-proportion z-test on
task-success rate, **not** a toy comparison. `evaluateReadiness` requires
`MIN_SAMPLES = 100`; below that it returns `not-enough-samples`. The z-statistic
is converted to a two-sided p-value via a genuine Abramowitz & Stegun 26.2.17
normal survival-function approximation (`normalSf`), compared against
`P_THRESHOLD = 0.05`. Verdicts: `not-enough-samples`, `inconclusive`,
`challenger-worse`, `challenger-ready`. Rollout runs in `shadow` (challenger
runs in parallel but never returns to the user) or `canary` (a bounded
`canaryFraction` of live traffic) mode via `routeInvocation`.
`ROLLOUT_TRANSITIONS` are `promote-challenger-to-champion`
(**`readiness-required`** — promotion is refused unless the readiness verdict is
`challenger-ready`), `freeze-rollout`, and `rollback-to-prior-champion`. The
durable gold-set pipeline (`feedback/gold-set-pipeline.ts`) round-trips
operator-promoted gold-set entries through `DurableGoldSetEntryRow` records,
surfaced via the admin `gold-sets/promote` and `champion-challengers/rollout`
routes.

## Customer-/creator-facing invocation

`src/invocation/invocation.ts` gates invocation behind entitlements and a
preflight declaration. `INVOCATION_SURFACES` enumerates where a run can be
triggered; `validatePreflight` checks the `InvocationEnvelope`'s declared
`entitlements` set against required scopes and returns `missing-entitlement` per
gap, alongside the declared source set, declared tone, declared persona, and a
`ConsentConfirmation`. This is the layer that binds a customer invocation to
Lilith tone policy and entitlement before any tool ever runs — the studio
counterpart of the BFF's S7 scope check.

## The seven V1 pipelines

`src/pipelines/v1-pipelines.ts` declares **seven** `PipelineSpec`s, all
`lifecycleState: 'live'`. Each pins a DAG `plan`, `participatingAgentIds`,
`expectedHandoffDepth`, mandatory approval gates, an `evaluationFixtureSetId`,
and a `totalBudgetCap`:

| Pipeline (`pipelineId`)        | Invocation tier   | `totalBudgetCap` |
| ------------------------------ | ----------------- | ---------------- |
| `veritas.weekly_briefing_pack` | creator           | 5 000            |
| `veritas.story_drafting`       | platform-operator | 12 000           |
| `tara.seasonal_program`        | platform-operator | 10 000           |
| `nisaba.edition_study_guide`   | customer          | 3 000            |
| `nyx.event_explainer_set`      | tenant-operator   | 2 500            |
| `arete.weekly_review_draft`    | customer          | 500              |
| `metis.course_from_byom`       | customer          | 20 000           |

There are **two distinct Veritas pipelines** that a reader could conflate:
`VERITAS_WEEKLY_BRIEFING_PACK` (creator-tier, aggregates a week's stories into a
multi-format briefing pack) and `VERITAS_STORY_DRAFTING`
(platform-operator-tier: enumerate sources → ingest → extract claims →
fact-check → contradiction check → counterclaim → Lilith tone review → editorial
inbox). `v1-pipelines.ts` flags this overlap in a comment; they are not
duplicates.

**The pipeline SPECS live in `@oshun/agentic-studio`, not in
`libs/oshun/agent-pipelines/`.** The `@oshun/agent-pipelines` package (v0.1.0)
is a **4-file re-export shim** whose `src/index.ts` simply re-exports the
resolver and the pipeline specs from `@oshun/agentic-studio`; it is **not** an
independent implementation. Companion-doc references that send readers to
`agent-pipelines/` for the implementation point at the wrong package.

`PipelineRegistry` (`src/pipelines/pipeline-registry.ts`) validates and stores
specs; `PIPELINE_TIERS`, `effectivePipelineForTenant`, and
`TenantPipelineCustomization` support tenant-scoped customization. Scheduling
and observability are real symbols: `PipelineSchedule` and `nextFireFor` compute
the next fire time, and `buildPipelineObservabilityReport` /
`detectPipelineDeviation` (`pipelines/observability.ts`) surface drift between
expected and observed runs.

## Dashboard, replay, and streaming progress (data layers)

These are **query and data layers**, not a rich React admin page.
`dashboard/dashboard-query.ts` materializes `DashboardEntry` rows with
`RUN_LIST_BUCKETS`, `bucketForStatus`, cost bands (`costBandFor`), and latency
bands (`latencyBandFor`), and `filterDashboard` applies a `DashboardFilter`.
`runs/replay.ts` provides `buildReplayTimeline` and `reconstructStateAt` — the
"time-travel" is a pure reconstruction over the recorded envelope, not a
separate durable replay engine. `runs/streaming-progress.ts` defines
`RUN_PROGRESS_KINDS`, `PROGRESS_VISIBILITY`, `shouldDeliverToSubscriber` (so a
customer never sees an internal operator event), and `summarizeForCustomer`. The
operator _console_ is the admin `agentic-operations` API routes plus
`tenant-admin`'s single `agents/page.tsx`.

## The autonomous creative orchestrator (undocumented until now)

`libs/oshun/creative-orchestrator` (`@oshun/creative-orchestrator` v0.0.1,
depending on `@oshun/ai`) is the actual **brief → produced-content engine** —
fully real with **58 passing tests** — and is not mentioned anywhere else in the
V1 docs. It is built on the shared `@oshun/ai/agent-loop` primitives
(`runStructuredOutput`, `runReflexion`) and is **fail-loud throughout**: it
never fabricates a plan or an artifact, and it fails loudly when no provider or
generator is wired. Exported pieces:

- **`decomposeBrief`** — turns a brief into a schema-validated, acyclic
  `CreativePlan` DAG (`CREATIVE_PLAN_SCHEMA`), with real DAG validation
  (`validateDagStructure`, `detectCycle`, `topologicalOrder`).
- **`routePlan` / `CreativeOrchestrator` / `orchestrateBrief`** — governed
  dispatch of plan nodes to domain generators under real
  budget/kill-switch/throttle governance (`BudgetGovernanceGate`, with
  `ALLOW_ALL_GATE` for tests).
- **`reviseArtifact`** — a bounded generate → critique → revise (Reflexion) loop
  around every artifact, with critics `createMetricCritic`,
  `createLlmJudgeCritic`, and the non-provider-gated default
  `createContentEvalCritic`.
- **Domain adapters** — `createYemayaAgentGenerator` wires Yemaya specialized
  agents in; `createMetisNarrator` wires Metis narration in.

This is the engine that the Studio's pipelines orchestrate against. Like the
rest of the autonomy story, its **planner and generators are real seams**, and
the end-to-end "content actually gets produced through live providers" path
depends on those providers being wired at deploy time (the e2e walkthrough
`agentic-pipeline-customer-invocation` is classified "deep" precisely because
its tool DAG is creds-bound).

## The autonomous creative direction plane

Everything above is **trigger-driven** — a human, customer, or schedule invokes
a pipeline. `libs/oshun/creative-autonomy` (`@oshun/creative-autonomy`, 100
passing tests across 8 files) is the plane that closes the loop the other way:
it decides _what_ to create, produces it, judges it, routes it past humans, and
learns from their reactions. One `AutonomousCreativeDirector.runCycle()` pass:

1. **Sense** — registered `SignalSource`s emit evidence-bearing
   `OpportunitySignal`s. Four deterministic sources ship: catalog gaps
   (demand-weighted coverage deficit + staleness), engagement decay
   (historical-peak decline with residual demand), calendar events (triangular
   lead-time fit), and audience requests (rater-count saturation × rating need,
   clustered from real `FeedbackEvent`s). Signals decay exponentially by
   half-life and dedupe by (kind, topic); zero sources is a typed error, never
   "no opportunities."
2. **Ideate** — signals become `ContentIdea`s two ways: deterministic derivation
   via a signal-kind → angle affinity model with per-angle brief templates, and
   an optional LLM `IdeaSynthesizer` seam whose proposals are **admitted only**
   if they name a registered category, an unbanned topic, and an actual sensed
   signal (`unknown-signal` rejection otherwise). Each idea gets an auditable
   compellingness _prior_: a steering-weighted blend of timeliness, novelty
   (token-Jaccard vs. the recent portfolio), audience value, strategic fit, and
   feasibility.
3. **Select** — a **Thompson-sampling bandit** (real Beta posteriors,
   Marsaglia–Tsang gamma sampling, injected RNG) draws per-category acceptance
   beliefs; ideas pack greedily by priority-per-cost under the cycle budget,
   per-category caps, and a topic-cluster guard. Every skipped idea carries a
   typed reason.
4. **Produce** — each selected idea is produced as K variants through the
   `ContentProducer` seam (canonically `createOrchestratorProducer` over the
   creative orchestrator above). **Every provider-shaped call flows through a
   `ConcurrencyGovernor`**: bounded slots (default 2), token-bucket launch rate,
   and AIMD adaptation — a classified 429/session-limit error halves effective
   concurrency and opens a cooldown; consecutive successes earn slots back. The
   plane cannot hammer a session limit by construction.
5. **Judge** — variants meet in a round-robin pairwise tournament fitted with
   **Bradley–Terry MM iterations** (ε-regularized); the champion faces a
   7-dimension compellingness rubric (hook, clarity, novelty,
   emotional-resonance, grounding, structure, audience-fit) scored by an
   injected judge panel with a cross-judge disagreement penalty and a
   **human-calibration offset** learned from review outcomes. The publish gate
   demands score ≥ bar _and_ panel confidence — an unconfident pass is not a
   pass; failures park with named failing dimensions.
6. **Route past humans** — per-category `AutonomyLevel`: `human-approval`
   (nothing moves without an explicit decision), `review-window` (veto window;
   silence auto-publishes), `full-auto` (publishes immediately, still listed for
   after-the-fact feedback). The `ReviewQueueStore` holds every item with its
   full `ProvenanceChain` (signal → idea → score → selection rationale →
   tournament strength → gate verdict) and an immutable audit history.
7. **Learn** — approve/reject/veto/tweak/window-expiry outcomes update the
   bandit posteriors (documented reward constants), per-category calibration
   EMAs, and typed `SteeringSuggestion`s. Human steering is a versioned
   `CreativeDirection` document (tenets, emphasize/avoid themes, banned topics,
   priorities, tighten-only autonomy overrides, quality bars); the learner
   **proposes** changes but never edits human-owned lists.

The BFF mount is `apps/oshun/bff/src/agentic/autonomy-route.ts`
(`/v1/agentic/autonomy/*`): cycle trigger + reports + status (operator-gated),
the review queue with approve/reject/veto/request-tweak actions, steering
GET/PUT, learner suggestions, and published-item feedback. Same posture as the
runs route: the loop/gate machinery is real and in-process; the
producer/publisher/judge seams bind at the deployable boundary, and with none
bound the default plane is **fail-closed** — `POST cycles/run` returns
`503 autonomy_director_not_configured`, never a fabricated cycle. Launch
categories (`V1_AUTONOMY_LAUNCH_CATEGORIES`) are conservative: everything
requires human approval except Nyx sky-event briefs, which get a veto window.

**The seams are now bound to the real monorepo systems**
(`apps/oshun/bff/src/agentic/autonomy-bindings/`, wired by
`createBoundCreativeAutonomyPlane` in `server.ts`): opportunity signals from the
**Nyx ephemeris** (credential-free lunar phases / season markers / featured
events through the lead-time model); production through the creative
orchestrator over an LLM text generator (structured
`{title, text, citations[]}`) plus the **generation-job pipeline**
(image/audio/video jobs drained programmatically via
`processQueuedGenerationJobs` — the Isis release gate and released-output
catalog apply to autonomous work identically); a specialist judge panel — the
**content-quality-judge** engine (N-sample pointwise craft scoring +
position-bias-mitigated pairwise for the tournament, over an
`@oshun/ai`→`@iris/agents-core` provider bridge), the deterministic **slop**
novelty judge, the **Lilith** content-QA tone judge, and a **Sophia**
declared-citation grounding judge (partial panels: a judge scores only its
competent dimensions, and an uncovered dimension is a typed configuration error,
never a gap-filled score); publication through the **studio-authoring editorial
lifecycle** (draft → in-review → approved → published under the Lilith gate — a
blocker refuses the publish and the item honestly retries); and the **admin
kill-switch store** as the cycle abort signal (global or
`agent-family:creative-autonomy`). The LLM-shaped pieces resolve from
`OPENROUTER_API_KEY` (`OSHUN_AUTONOMY_LLM_MODEL` override); the startup log
states BOUND vs fail-closed. Per-system findings and the honest remaining-gap
ledger: `CREATIVE_AUTONOMY_UNDERLYING_AUDIT_2026-07-02.md`.

## How the pieces fit (a single guarded tool call)

1. A trigger (user, pipeline, or schedule) lands on
   `POST /v1/agentic/runs/execute`. The BFF derives `actorId`/`tenantId` from
   the auth claim, fails closed (`503`) on any unconfigured tool, checks scopes
   (`403`), and checks ownership (`409`).
2. The route builds an `AgentRunEnvelope` plus server-policy-derived
   `GovernedPlannedToolCall[]`, materializes exact per-run grants for bounded
   low/medium catalog tools (or requires an injected pre-approved broker for
   secrets, egress, or high/critical authority), and calls
   `runGovernedToolPlan`.
3. At each boundary, the runtime refreshes pause/cancel/kill state, resolves a
   fresh exact capability grant, applies secret scope,
   filesystem/process/network isolation, egress allowlists and the two-phase
   SSRF guard, then runs `admitToolCall` for kill-switch and latest-meter budget
   admission.
4. The boundary persists a pending call before dispatch. The injected tool runs
   only after grant, isolation, kill, and budget checks pass. Its ok, partial,
   or error result replaces the pending record with redacted arguments, result
   or error, resolved grant, capability audit ids, evidence, effects, artifacts,
   latency, and measured usage. Denied attempts are recorded too.
5. Sub-agent hand-offs go through `validateDispatch` (subset authority, depth
   cap, budget ≤ parent remaining), deduct budget at dispatch, and reconcile
   unused budget back to the parent at termination.
6. Every attempt's actual or policy-defined denied/failed charge updates the
   total, per-stage, per-tool, and category cost ledger and consumes the mutable
   budget meters. That measured usage is consumed before the next admission, so
   a multi-step plan cannot reuse its initial balance. The lifecycle store
   writes through the updated envelope and retains kill/budget audit events; the
   route returns the same telemetry-bearing run as a JSON-safe projection.

The generated field-by-field boundary is
[Agentic Run Execution Fields](./agentic-run-execution-fields.generated.md).

## Related

- [High-Level Architecture](./high-level-architecture.md)
- [Sophia — Grounding Substrate](./substrate-sophia.md)
- [Lilith — Contemplative Policy Substrate](./substrate-lilith.md)
- [Isis — Generation Control Substrate](./substrate-isis.md)
- [Iris — Assistant Memory Substrate](./substrate-iris.md)
- [Generation Audience Tiers](./generation-audience-tiers.md)
- [Trust, Safety, and Privacy](./trust-safety-and-privacy.md)
- [Communication Patterns](./communication-patterns.md)
- [Subsystem Glossary](./glossary.md)
- [`V1/features.md` § Agentic AI Studio](../features.md#agentic-ai-studio)
- [`V1/features.md` § Tool Catalog, Grant Semantics, and Multi-Agent Protocol](../features.md#tool-catalog-grant-semantics-and-multi-agent-protocol)
- Backlog §18 in [../TODOS.md](../TODOS.md); provider topology in
  [../DEPENDENCIES.md](../DEPENDENCIES.md); hub:
  [../ARCHITECTURE.md](../ARCHITECTURE.md)
