sage_skill
Call a Sage agent skill with shared org memory.
Shape
{ skill: string; input: object; timeoutMs?: number }Example
{ skill: "draft_engagement_brief", input: { intake } }Atelier · loading…
Architecture
Written for the engineering evaluator on a buying team. The run lifecycle, the five step kinds, the event bus envelope, the trace model, and the tenancy invariants — in the language a security review actually uses.
Lifecycle
┌──────────────┐ trigger ┌──────────────┐
│ Flow Version │ ────────────▶ │ Run │
└──────────────┘ └──────┬───────┘
│ for each step
▼
┌──────────────┐
│ StepKind? │
└──────┬───────┘
┌─────────────┬─────────┼─────────┬─────────────┐
▼ ▼ ▼ ▼ ▼
┌─────────────┐ ┌──────────┐ ┌──────┐ ┌──────────┐ ┌──────────┐
│ sage_skill │ │human_task│ │ gate │ │event_wait│ │ product_ │
└─────────────┘ └──────────┘ └──────┘ └──────────┘ │ action │
│ │ │ │ └──────────┘
▼ ▼ ▼ ▼ │
writes to atelier_traces (append-only) │
│ │
▼ ▼
┌─────────────────────────────────────┐
│ Run terminal state: succeeded | │
│ failed | halted │
└─────────────────────────────────────┘
Step kinds
Atelier ships with exactly five step kinds. They are not generic verbs — they are the primitives the orchestration layer needs to compose any operational playbook. Each kind is a row in atelier_step_kind and carries a typed input schema.
sage_skill
Call a Sage agent skill with shared org memory.
Shape
{ skill: string; input: object; timeoutMs?: number }Example
{ skill: "draft_engagement_brief", input: { intake } }human_task
Assign a checkpoint to a project member; wait for resolution.
Shape
{ assignee: UserId | RoleId; description: string; deadline?: ISODate }Example
{ assignee: "role:partner", description: "Review SOW draft" }product_action
Invoke a registered cross-product action (Sentinel, RFP, Janice, etc.).
Shape
{ product: PCProduct; action: string; payload: object }Example
{ product: "rfp", action: "generate_proposal", payload: { opportunityId } }event_wait
Block run progression until a matching event arrives on the bus.
Shape
{ eventType: string; match?: ExpressionDsl; timeoutMs?: number }Example
{ eventType: "catalyst.purchase.confirmed", match: "payload.org_id == run.org_id" }gate
Evaluate an expression on accumulated run context; branch or halt.
Shape
{ expression: ExpressionDsl; onPass: StepId; onFail: "halt" | StepId }Example
{ expression: "context.mission_fit_score >= 0.7", onPass: "draft", onFail: "halt" }Run lifecycle
| State | Description | Transitions |
|---|---|---|
pending | Run created; first step not yet picked up. Created on Flow trigger. |
|
running | Worker is executing a step. Exactly one step is active at a time per run. |
|
waiting | Run is blocked on an external signal — incoming event or human checkpoint. |
|
succeeded | All steps completed; terminal state. |
|
failed | A step exceeded retries or hit a hard failure; terminal state. |
|
halted | A gate evaluation returned a halt directive; terminal state. |
|
Bus · Trace · Tenancy
Cross-product events flow over the shared PC Event Bus. Each event carries a signed envelope with org_id, event_type, payload, idempotency_key, and an HMAC-SHA256 signature derived from a per-product shared secret. Subscribers verify the signature before processing and use the idempotency_key to deduplicate retries — re-delivery is at-least-once, processing is exactly-once-per-key.
atelier_traces is the append-only log of everything that happened in a run. Every step start, completion, retry, agent call, human action, gate evaluation, and event dispatch writes a row. The schema is denormalized for SQL query convenience — you can answer "show me every run where Sage drafted a proposal and a human rejected it" with one SELECT and no joins to anything except atelier_runs and atelier_flow_versions for naming.
Every atelier_* table has org_id NOT NULL plus an RLS policy keyed on it. The pc_runtime role is the only path to data outside of admin tooling, and it carries org_id in the connection via SET LOCAL atelier.org_id = $1. Even a SQL injection that escapes parameter binding cannot exfiltrate cross-tenant data — the RLS policy filters every row before it leaves the database.
Need a security review packet?
Enterprise customers get a full architecture + security review document, SOC2 in-progress attestation, data-handling addendum, and direct access to the PC engineering team for vendor-assessment Q&A. Reach out and we'll route the packet.
See the security overview →Open a workspace
Atelier opens in fifteen minutes with five starter Flows and every Perpetual Core product already connected. Evaluate for fourteen days; bring your team when you're ready.