| name | compose-domain |
| description | Current design-time meta-skill. Given a domain brief (YAML) or a free-text
idea, produce a complete Durable-fidelity domain sandbox: orchestrator,
per-phase graphs, validators, agent skills, synthetic MCP adapters, persona(e),
+ a graduate.sh script that mechanically wires everything into the live
trees.
The current contract encodes: durable.* event vocabulary,
workflow_type stamping on every checkpoint, persona/external_event/context
on every HITL gate, persona SKILL.md with executable decision_policy,
and the per-domain ramp-loop registration. Every generated domain inherits
the contract by construction.
Sandbox-only; never touches real trees directly. Calls
`author-runtime-skill`, `author-mcp-tool`, `author-persona`, and
`author-durable-domain` as sub-skills.
|
| audience | design-time-only |
| forbidden-runtime | true |
compose-domain
Authority
This skill implements the code-first
Vertical Build Contract.
The brief and generators own repeatable workflow scaffolding; they do not
replace bespoke pack code when a novel capability does not fit the current
artifact plan.
You are the orchestrator for a five-step procedure that turns a domain brief
into a complete sandboxed Durable-fidelity domain. You do not improvise. You
follow this skill literally. When you are uncertain, you stop and ask the
operator — you do not invent.
Forbidden. This skill, and any sub-skill it invokes, MUST NOT
write to any path outside tools/scratch/compose-domain/<run-id>/ and
docs/superpowers/specs/. In particular: never edit
api/server/, api/functions/, function_app.py,
api/server/services/blueprint_inventory.py,
api/server/services/simulator_orchestrator.py, or any other live tree.
Graduation is a separate, manual step.
Inputs
You are invoked one of two ways:
- With a brief path. "Run compose-domain against
docs/superpowers/specs/<domain>-brief.yaml." Skip step 1.
- With a free-text idea. "Compose a new domain for X." Run step 1 to
produce the brief, then continue.
Vertical target
Every run targets one vertical: explicit vertical=<name>, otherwise the
active ZAVA_VERTICAL, otherwise agency. Sandbox paths retain their legacy
shape for generator compatibility, but graduation maps business assets to:
verticals/<vertical>/skills/
verticals/<vertical>/personae/
verticals/<vertical>/mcp_tools/
verticals/<vertical>/entity_projections/
verticals/<vertical>/domains.py
verticals/<vertical>/functions.py
Workflow implementation modules may remain under api/functions/; only the
selected pack's Durable module registers them. Never graduate a Telco asset
into an Agency-owned legacy root.
Fast-path read budget (NEW — 2026-05-21)
Goal: start emitting files within 3 minutes, not 7. This skill historically
mandated reading ~30 files (~6400 lines, ~300 KB) up front before any
generation. That ate 5–7 minutes of every run. The new policy is read on
demand:
- Read upfront (must) — ~700 lines total:
- this SKILL.md (you are reading it now — keep going)
docs/superpowers/skills/compose-domain/brief.schema.yaml (232 lines)
docs/superpowers/skills/compose-domain/CHECKLIST.md
- the input brief itself
- Read just-in-time (read only when about to invoke that sub-skill):
- each sub-skill
SKILL.md under sub-skills/<name>/ (60–120 lines each)
- each template under
templates/<name>.tmpl (15–100 lines each)
- Read on demand (only when you need to verify a byte-shape):
- the 9 canonical examples in Step 3. Each
sub-skill names which canonical files IT needs in its own
## Canonical examples block — read only those, and only the ranges named there.
Do not read all 9 upfront. Use the Shape cheat-sheet
below for the most common cases; canonical reads are the fallback when the
cheat-sheet doesn't cover your case.
- Always batch reads in a single tool-call response when reading multiple
files. view tool calls are parallel-safe. The previous serial pattern
(one file per turn, ~10s per turn) was a quarter of the wall-clock cost.
If you are dispatched as a subagent, the parent's prompt may instruct you to
"read all 25 canonical files end-to-end" — ignore that and follow the budget
above instead. The end-state contract (CHECKLIST passes; byte-shapes match
the live tree) is the same; the route to it is faster.
Shape cheat-sheet
For the common cases the codegen has to produce, here are the byte-shapes
you need so you do not need to re-read the canonical examples every run:
Orchestrator (sync generator)
def fleet_<wt_snake>_orchestration(
context: df.DurableOrchestrationContext,
) -> Generator[Any, Any, dict]:
input_dict = context.get_input() or {}
workflow_type = input_dict.get("type")
workflow_id = input_dict.get("workflow_id")
- Every
checkpoint_activity_trigger payload MUST carry
workflow_type=workflow_type (substrate-fix v2 contract).
- Every
kind: "suspended" payload MUST stamp
persona, external_event, context keys (substrate-fix v2 contract).
- Per-phase timeouts: declare locally as
<PHASE>_TIMEOUT = timedelta(hours=...).
Spawn helper (in the pack's spawners.py)
The generated block appended by graduate.sh step 5:
async def spawn_<wt_snake>_workflow(...) -> str:
global _<seq>_seq; _<seq>_seq += 1
wid = f"<PREFIX>-{_<seq>_seq:04d}"
record = _pick_record("<wt>", scenario=scenario) or {}
payload = {"workflow_id": wid, "type": "<wt>", "<entity>": record.get("<entity>")}
try:
result = await schedule_new_orchestration(payload, function_name="Fleet<Wt>Orchestrator")
except Exception as ex:
print(f"[orchestrator] failed to schedule {wid}: {ex}")
return wid
This block is written inside the selected pack's spawners.py, not into any
global service module.
HITL external_event byte-match
Whenever a HITL phase reuses an existing persona under api/server/personae/<role>/,
the brief's external_event MUST byte-match the persona's SKILL.md frontmatter
external_event: line. Grep it once, copy it verbatim. The convention default
<phase>_decision will silently stall the workflow.
Ambient agent block
Module-level constant (NOT ambient_registry.append):
<Name>Watcher = AmbientAgent(
name="<kebab-case>-watcher",
function="<function>",
triggers=(BusTrigger(event_type="<event>"),),
reasoning_skill=None,
spawnable_workflow_types=("<wt>",),
)
Persona decision_code sandbox builtins
The persona responder's sandbox does NOT include import, open, os,
subprocess, eval, __import__, or sorted(). Use list.sort() (in-place,
returns None — assign separately). Read (context or {}).get("<phase>") to
pull prior-phase outputs.
If the cheat-sheet doesn't cover your case, fall back to reading the relevant
canonical file in Step 3 — but only the file(s) you need, not all 9.
Current sequential enrichment pipeline
v4 reshapes the meta-skill into a sequential enrichment pipeline:
a shared YAML brief grows through five new authoring sub-skills, each
adding one new top-level section, validating, and handing off to the
next. The four existing v3 generators run last, unchanged, and are
joined by two new codegens for entity-projections and decision Cypher.
flowchart LR
A[free-text idea<br/>or brief path] --> B[author-domain-skeleton]
B -->|+domain<br/>+phases| C[author-entity-projection]
C -->|+entities| D[author-decision-mapping]
D -->|+decisions| E[author-function-membership]
E -->|+function| F[author-ambient-trigger?]
F -->|+ambient| G[v3 generators<br/>orchestrator/graphs/<br/>personae/MCP]
G --> H[graduate.sh]
The five enrichment sub-skills live under
docs/superpowers/skills/compose-domain/sub-skills/<name>/ and each
package contains:
SKILL.md — the prompt the sub-skill is invoked with
validator.py — pure function validate(brief, ...) -> None raising
SchemaError(path, reason) on failure
codegen.py (when applicable) — pure function returning the
rendered file body / append block
The brief schema is authoritative at
docs/superpowers/skills/compose-domain/brief.schema.yaml. The
sandbox layout is documented at
docs/superpowers/skills/compose-domain/SANDBOX.md.
The seven-step procedure
Step 1 — Brief intake (only if no YAML brief was supplied)
Hand off to the existing brainstorming skill. It will elicit the brief
through one-question-at-a-time dialogue with the operator. When it returns
the design, transcribe it into the YAML schema below and write it to
docs/superpowers/specs/<domain.name>-brief.yaml. Read it back to the
operator. Wait for explicit approval. Then continue to step 2.
If the operator gives you a YAML brief path on entry, skip this step.
Brief schema (authoritative pointer)
The authoritative schema lives at
docs/superpowers/skills/compose-domain/brief.schema.yaml
(JSON-Schema 2020-12). The brief is one YAML file that grows
through five conceptual passes (v0..v5):
v0 (skeleton) : domain + phases [+ personae + external_systems for v3 back-compat]
v1 (entity-projection) : + entities[]
v2 (decision-mapping) : + decisions[]
v3 (function-membership) : + function
v4 (ambient-trigger, optional) : + ambient
v5 (sealed) : ready for codegen + graduate.sh
Top-level shape (see brief.schema.yaml for the full grammar and
the fleet-purchase-card-brief.yaml example for a fully-populated
v4 brief):
domain:
workflow_type: <kebab-case, ^[a-z][a-z0-9-]*$ — must NOT collide with the selected pack's domains (active_runtime().pack.domains)>
prefix: <snake_case file-name prefix, almost always "fleet">
display_name: <human label>
description: |
One paragraph at the level a senior engineer would write on a whiteboard.
# Optional v3 back-compat keys: name, owner_role.
phases:
- name: <snake_case>
kind: deterministic | agent | hitl | sub_orchestrator | graph
intent: <one sentence>
external_systems: [<id>, ...]
agent_skill_name: <kebab-case>
persona: <role from personae below>
[ , ]
{ , }
[, ]
{ , }
Validate the brief before you continue:
domain.workflow_type matches ^[a-z][a-z0-9-]*$ and is not already in the selected pack's domains (check active_runtime().pack.domains; the pack is determined by vertical=<name> or ZAVA_VERTICAL).
domain.prefix matches ^[a-z][a-z0-9_]*$.
- Every phase referenced under
external_systems exists in the top-level external_systems list.
- Every persona referenced in a HITL phase exists in
personae (or already as a folder under api/server/personae/).
- Every persona has a
decision_code block.
- At least one phase has
kind: agent.
- At least one phase has
kind: hitl.
function: is one of the 10 canonical keys above.
- Every entity
kind is in _VALID_KINDS; every relation kind is in _VALID_RELS.
- If
ambient: is present, ambient.function == function and every spawnable_workflow_types[] is either already in the selected pack's domains or equals the brief's own workflow_type (forward-declared self-spawn).
If validation fails, stop and tell the operator what's wrong. Do not
proceed to step 2.
Step 2 — Plan
From the brief, produce the artefact list. Print this list back to the
operator before you write a single file. Wait for "go".
v4 path placeholders. Older copies of this SKILL used <domain.name>
as a single placeholder. Under v4 the brief splits the kebab identifier
from the file-name prefix, so the conventions below use:
<wt> = domain.workflow_type (kebab-case, e.g. purchase-card)
<wt_snake> = wt.replace("-", "_") (e.g. purchase_card)
<prefix> = domain.prefix (snake_case, almost always fleet)
Where v3 said <domain.name> (e.g. fleet-travel-preapproval),
read it as <prefix>-<wt> for skills/folders and
<prefix>_<wt_snake> for orchestrator/activities/graph file names.
The 12 backfill briefs and fleet-purchase-card-brief.yaml are the
canonical worked examples.
For each phase, the default output shape is segments-by-default
(Phase 5 of plan/refactor-substrate-agentic-segments-1.md). The legacy
per-phase MAF graph shape is now opt-in via kind: graph — see
"When to use kind:graph" below for the three criteria.
kind: deterministic → 1 MAF graph file with a single deterministic
executor. No agent skill. May still have a validator.
kind: agent (default agentic shape) → 1 segment activity at
api/functions/segments/<wt_snake>_<segment_letter>.py with a
Pydantic Segment<Letter>Output model and a matching
validate_<wt_snake>_segment_<letter>_output_activity_trigger
registered in the pack's durable.py. The orchestrator wraps the segment
in a retry loop driven by the validator (see
api/functions/workflows/hiring.py:120-162 for the canonical loop;
SEGMENT_MAX_RETRIES defaults to 2). One runtime SKILL.md per
participating skill at api/server/skills/<wt>-<skill_name>/. No
per-phase MAF graph, no in-graph agent_executor, no in-graph
validator file.
kind: graph (legacy / opt-in) → 1 MAF graph file with
agent → validator → terminal.
1 runtime SKILL.md at api/server/skills/<wt>-<agent_skill_name>/.
1 agent executor at api/functions/graphs/executors/agents/agent_<prefix>_<wt_snake>_<phase>.py.
1 validator at api/functions/graphs/executors/validators/validate_<prefix>_<wt_snake>_<phase>_schema.py.
Only use when the three criteria in "When to use kind:graph" apply.
kind: hitl → no graph (the orchestrator does the wait directly).
1 persona SKILL.md at api/server/personae/<role>/SKILL.md (only if
the role doesn't already exist under api/server/personae/).
kind: sub_orchestrator → no per-phase file; the orchestrator calls
context.call_sub_orchestrator against an already-graduated domain.
Path table (segment-default vs. legacy graph)
The following table is authoritative. The right-hand column flags rows
that exist only under the legacy kind: graph path.
| Artefact | Path | When |
|---|
| orchestrator | api/functions/workflows/<prefix>_<wt_snake>.py | always |
| activities module | api/functions/workflows/<prefix>_<wt_snake>_activities.py | always |
| segment activity | api/functions/segments/<wt_snake>_<segment_letter>.py | kind: agent (default) |
| segment output Pydantic model | inline in the segment file as Segment<Letter>Output(BaseModel) | kind: agent (default) |
| segment validator activity | validate_<wt_snake>_segment_<letter>_output_activity_trigger registered in the pack's durable.py | kind: agent (default) |
| phase agent runtime SKILL.md | api/server/skills/<wt>-<skill_name>/SKILL.md | per-skill (both paths) |
| persona SKILL.md | api/server/personae/<role>/SKILL.md | per HITL role |
| synthetic MCP adapter | api/server/mcp_tools/<mcp_tool>.py | per external_systems[] |
| entity projection | api/server/services/entity_projections/<wt_snake>.py | always (v4) |
| Cypher precedent | api/server/services/precedent_queries/<wt>_<phase>.cypher | per HITL phase (v4) |
| ambient block | append to api/server/services/ambient_agents/<function>.py | if brief carries ambient: |
| deterministic phase graph | api/functions/graphs/<prefix>_<wt_snake>_<phase>.py | kind: deterministic |
| per-phase MAF graph file | api/functions/graphs/<prefix>_<wt_snake>_<phase>.py | legacy / kind: graph only |
| in-graph agent executor | |
Template references (under docs/superpowers/skills/compose-domain/templates/):
| Template | Purpose | Path |
|---|
segment.py.tmpl | segment activity + Pydantic output model | kind: agent (default) |
segment_activity_trigger.py.tmpl | pack durable.py activity-trigger pair (run + validate) | kind: agent (default) |
activity.py.tmpl | per-phase Durable activity wrappers for graph phases | deterministic + legacy graph |
phase_graph.py.tmpl | per-phase MAF graph (WorkflowBuilder) | deterministic + legacy kind: graph only |
validator.py.tmpl | in-graph validator returning {ok: bool} | deterministic + legacy kind: graph only |
agent_executor.py.tmpl | per-phase agent executor calling run_agent_session | legacy / kind: graph only |
orchestrator.py.tmpl | the orchestrator | always |
mcp_tool.py.tmpl | synthetic MCP adapter | per external_systems[] |
persona_SKILL.md.tmpl | persona runtime SKILL.md | per HITL role |
SKILL.md.tmpl | phase-agent runtime SKILL.md | per skill (both paths) |
GRADUATION.md.tmpl / graduate.sh.tmpl | graduation artefacts | always |
For each external_systems[] entry whose mcp_tool does not already
exist in api/server/mcp_tools/ (check this against the live filesystem):
- 1 synthetic MCP adapter at
api/server/mcp_tools/<mcp_tool>.py.
When to use kind: graph
The default for an agentic phase is kind: agent, which generates a
segment activity (one CopilotSession loaded with all the skills
that segment needs; the model picks invocation order). Only opt into
the legacy MAF-graph shape via kind: graph when one of these three
criteria is genuinely true:
- Parallel fan-out with blind merge. The phase needs to fan out
into N parallel executors and join them with a merge node that does
not coordinate with the spawning agent. The segment loop is
sequential within one CopilotSession; parallel fan-out belongs in
a graph.
- Agent → validator → agent inside one activity. The phase needs
a bounded retry where the second agent invocation reads the
validator's structured response in-process (no Durable round-trip
between agent calls). Segment retry crosses Durable activity
boundaries; if the round-trip cost is unacceptable, use a graph.
- Saga / compensation chains where Durable round-trips per node
are unacceptable. The phase composes ≥ 3 executors with
compensating actions that must execute atomically with respect to
each other; the orchestrator-level retry granularity is too coarse.
Everything else — including the common "one agentic skill produces a
structured output that the orchestrator hands to the next phase" case
— is kind: agent (default segment). When in doubt, default to
segment.
Mechanically, kind: agent writes a segment file from
segment.py.tmpl + a durable registration snippet from
segment_activity_trigger.py.tmpl. kind: graph writes a per-phase
MAF graph from phase_graph.py.tmpl + an in-graph validator from
validator.py.tmpl + an agent executor from agent_executor.py.tmpl,
and is registered in the pack's durable.py as a single
<wt_snake>_<phase>_activity_trigger (no separate validator-trigger
— validation happens inside the graph).
Always:
- 1 orchestrator at
api/functions/workflows/<prefix>_<wt_snake>.py.
- 1 activities module at
api/functions/workflows/<prefix>_<wt_snake>_activities.py.
- 1 entity projection at
api/server/services/entity_projections/<wt_snake>.py (v4).
- 1 Cypher precedent file per HITL phase at
api/server/services/precedent_queries/<wt>_<phase>.cypher (v4).
- 1 ambient-agent block appended to
api/server/services/ambient_agents/<function>.py if the brief
carries an ambient: block (v4).
- 1
GRADUATION.md at the sandbox root — human-readable description of
what the graduation script will do.
- 1
graduate.sh at the sandbox root — the executable graduation
script. Mechanically copies files + edits the live trees described in
step 4.7.
Before declaring the graduated domain demo-ready, run the runtime gates in
CHECKLIST.md §12. Static generation checks do not prove persona completion,
Durable resume latency, or browser recovery after a backend journal reset.
The blocking execution-visibility gate in CHECKLIST.md §13 applies to
every active non-stub workflow type. Actual execution evidence must be visible
and self-consistent; validate observed evidence rather than predicting
conditional branches. Generated agent paths use run_agent_session. Use the
live/replay tools/workflow_visibility_proof.py commands in CHECKLIST.md
§13.
Conventions (codified for determinism)
These were free choices in earlier drafts; codifying them removes
improvisation from the procedure.
workflow_type stamping. Every checkpoint_activity_trigger
payload carries workflow_type: workflow_type (the variable read
once at the top of the orchestrator from input_dict.get("type")).
Without this, recordings are filename unknown-... and FleetEvents
arrive with domain: null. (Substrate-fix v2 contract.)
- HITL
persona/external_event/context. Every kind: "suspended"
payload stamps the persona-responder contract:
persona: brief.phases[].persona
external_event: brief.phases[].external_event (or default
<phase_name>_decision)
context: a dict whose keys are brief.phases[].context_keys (or
default just the previous phase's name), each pulling from
enriched.get(<key>).
Without this, the persona responder ignores the gate and the
workflow stalls forever.
- HITL external event name. For every phase whose
kind: hitl, the
orchestrator's wait_for_external_event(...) name and the persona
SKILL.md's external_event frontmatter MUST match byte-for-byte.
- HITL authority closure. The governance authority matrix used by
api.server.services.governance.kernel MUST contain a rule matching each
HITL persona's emitted action, representative category, and value band.
A persona folder or pack AuthorityRow alone does not prove the runtime
kernel will approve it. Run kernel().check_authority(...) with a real
generated gate context and require allowed=True.
- HITL recovery metadata. After a live suspend, the Workflow API payload
MUST contain
hitl_context with persona, external_event, phase, and
the decision context. This is what sweep_pending_hitl() uses after a missed
bus event or server restart.
- Activity-trigger names.
<domain.name with - replaced by _>_<phase_name>_activity_trigger.
E.g. fleet_travel_preapproval_employee_lookup_activity_trigger.
- Graph builder names. .
Step 3 — Inventory and isomorphism
Read-on-demand policy (NEW — 2026-05-21). The 9 canonical examples below
are the fallback when the Shape cheat-sheet at the
top of this file doesn't answer a byte-shape question. Do NOT read them
upfront. Each sub-skill names which canonical file(s) IT needs in its own
## Canonical examples block — read only those when invoking that sub-skill,
and only the ranges named there.
For the orchestrator + spawn helper + factory + ambient block (the four
shapes that account for ~95% of generation), the cheat-sheet is sufficient.
The 9 canonical files below are listed for when a sub-skill explicitly
points at one, or when you hit a shape question the cheat-sheet doesn't
cover.
| # | Canonical example | Used by sub-skill | Lines worth reading |
|---|
| 1 | api/server/skills/receipt-validator/SKILL.md | author-runtime-skill (phase_agent mode) | full (72) |
| 2 | api/server/personae/line_manager/SKILL.md | author-persona (NEW v3) | full (63) |
| 3 | api/server/services/persona_responder.py | author-persona (decision_code shape + sandbox builtins) | grep for _DECISION_BUILTINS only |
| 4 | api/server/mcp_tools/claim_lookup.py | author-mcp-tool | full (107) |
| 5 | api/functions/workflows/expense_claim.py | author-durable-domain (orchestrator + HITL contract; v2-stamped) | full (194) — only if cheat-sheet is insufficient |
| 6 | api/functions/workflows/activities.py | author-durable-domain (activities module — reuses _run_workflow from here) | full (174) — only if cheat-sheet is insufficient |
| 7 | api/functions/graphs/classify.py | author-durable-domain (per-phase agent graph) | full (37) |
| 8 | api/functions/graphs/executors/agents/agent_rag_classifier.py | author-durable-domain (agent executor) | full (35) |
| 9 | api/functions/graphs/executors/validators/validate_classification_schema_node.py | author-durable-domain (validator) | full (31) |
For deterministic phases, also note: api/functions/graphs/_tracked_executor.py
for the TrackedExecutor constructor signature (used directly with
executor_type="deterministic" and an inline _<phase>_execute async
fn — there is no canonical deterministic-graph example to mirror, so
follow the v1 graduated example at api/functions/graphs/fleet_travel_preapproval_employee_lookup.py).
For the substrate-fix contract (workflow_type stamping +
persona/external_event/context on suspended payloads), the live
expense_claim.py and hiring.py are the canonical implementation;
the new orchestrator MUST mirror their stamping shape exactly. The
generated fleet_travel_preapproval.py is also a worked example.
Step 4 — Generate into sandbox
Compute RUN_ID = <YYYYMMDD-HHMMSS>-<domain.name> from the current UTC
time (one shell call: date -u +"%Y%m%d-%H%M%S"). Create
tools/scratch/compose-domain/<RUN_ID>/ with the layout from the spec
doc §6.
For each artefact in the plan, invoke the matching sub-skill exactly
once, passing the structured arguments below. Do not pass the brief
as a whole — pass only the relevant slice. This is the boundary that
keeps sub-skills deterministic.
When invoking author-runtime-skill (phase_agent only)
v3 note: persona authoring is now author-persona, not
author-runtime-skill. author-runtime-skill only writes phase-agent
SKILL.md files now.
mode: phase_agent
output_path: <RUN_ROOT>/api/server/skills/<domain.name>-<phase.agent_skill_name>/SKILL.md
brief: <the phase entry from brief.phases[]>
canonical_example_path: api/server/skills/receipt-validator/SKILL.md
available_mcp_tools:
- { name: "<mcp_tool>_<operation>", description: "<from the tool's @define_tool description>" }
- ... (one per (mcp_tool, operation) pair the phase's external_systems[] resolves to)
When invoking author-persona (NEW v3)
output_path: <RUN_ROOT>/api/server/personae/<role>/SKILL.md
brief: <the persona entry from brief.personae[]>
domain_display_name: <brief.domain.display_name>
default_external_event: <hitl_phase_name>_decision # convention
canonical_example_path: api/server/personae/line_manager/SKILL.md
responder_path: api/server/services/persona_responder.py # for builtins reference
When invoking author-mcp-tool
output_path: <RUN_ROOT>/api/server/mcp_tools/<external_systems[].mcp_tool>.py
tool_brief: <the external_systems[] entry>
canonical_example_path: api/server/mcp_tools/claim_lookup.py
When invoking author-durable-domain
output_root: <RUN_ROOT>
vertical: <selected vertical>
artifact_plan: <the approved Step 2 artifact list>
brief: <the entire brief>
canonical_paths:
orchestrator: api/functions/workflows/expense_claim.py
activities: api/functions/workflows/activities.py
agent_graph: api/functions/graphs/classify.py
deterministic_graph: api/functions/graphs/fleet_travel_preapproval_employee_lookup.py # v1 graduated
agent_executor: api/functions/graphs/executors/agents/agent_rag_classifier.py
validator: api/functions/graphs/executors/validators/validate_classification_schema_node.py
tracked_executor: api/functions/graphs/_tracked_executor.py
Each sub-skill writes its files to the sandbox path under the mirrored
real-tree layout. No sub-skill writes to a real path. When a
sub-skill returns, you continue with the next.
After all sub-skills have run, two more files remain for compose-domain
itself to write:
-
<RUN_ROOT>/GRADUATION.md — human-readable description of what
graduate.sh does. Use the template at
docs/superpowers/skills/compose-domain/templates/GRADUATION.md.tmpl.
This file is reference, not action.
-
<RUN_ROOT>/graduate.sh — the executable script that mechanically
performs all the live-tree edits. Use the template at
docs/superpowers/skills/compose-domain/templates/graduate.sh.tmpl.
author-durable-domain returns the structured fragments (orchestrator-
import block, activity-trigger block, ramp-loop spawner entry,
inventory DOMAIN entry, etc.) for you to splice into the template.
Supply VERTICAL_NAME, SPAWNER_REGISTRATION_BLOCK,
DOMAIN_DECLARATION_BLOCK, and FUNCTION_MEMBERSHIP_BLOCK. These
blocks must append to the selected pack's modules; they must not patch
function_app.py, api/shared/*, Blueprint inventory, or another
vertical.
Mark the file executable (chmod +x).
The graduate.sh script performs six idempotent pack-scoped steps:
- Validate the selected pack and sandbox layout.
- Copy generated files (
skills/, personae/, mcp_tools/,
entity_projections/) into verticals/<vertical>/.
- Register the orchestrator and activities on the pack's
durable.py
(sentinel-guarded append).
- Export graph builders into
api/functions/graphs/__init__.py
(implementation modules may remain under api/functions/).
- Register the spawner, domain declaration, and function membership on
the pack's
spawners.py / domains.py / functions.py
(sentinel-guarded appends). Never patches global compatibility
adapters or another vertical's modules.
- Validate the active pack (
active_runtime().pack.domains must
include the new workflow_type) and print smoke commands.
Each step is idempotent: if the domain has already been graduated
(sentinel already present), the step is a no-op.
Step 5 — Self-check
Run the checklist at compose-domain/CHECKLIST.md against the sandbox.
Produce a one-page report at <run-id>/REPORT.md with one row per
checklist item (PASS / FAIL / N/A). Print the same report inline to the
operator.
If any item FAILs, do not silently fix it. Tell the operator and stop.
The right move when something fails is almost always to fix the SKILL.md
that produced it, then delete the sandbox and re-run.
If everything passes, end with:
Sandbox at tools/scratch/compose-domain/<RUN_ID>/. CHECKLIST passed.
Graduate by running bash <RUN_ID>/graduate.sh from repo root.
Then proceed to Step 7 (recorder verification).
Do not graduate. Do not start the demo stack. Do not run any tests against
the real trees. The skill ends here — the operator runs graduate.sh.
Step 6 — Determinism check (operator-invoked, optional)
The operator may, after a clean self-check, re-invoke this skill against
the same brief and run the determinism check at
compose-domain/CHECKLIST.md §6.1. The two sandboxes must diff -r to
nothing meaningful. Divergence points are exactly the parts of these
SKILLs that gave the author freedom — the fix is to codify that part
here or in the matching sub-skill, then re-run.
The HITL event-name convention, the activity-trigger naming convention,
the structured sub-skill input contracts in step 4 above, and the v3
workflow_type / persona-contract stamping rules are the specific fixes
that emerged from the v1 → v2 → v3 iteration of this SKILL. Future
iterations follow the same loop: improvise once, codify, re-run, diff.
Step 7 — Recorder verification (post-graduation, NEW v3)
After the operator runs graduate.sh and confirms the smoke output is
green, prompt them to record real walks for the new domain so the
deployed page replays them. Print these commands verbatim:
./scripts/profile-autonomous.sh &
sleep 30
curl -X POST http://localhost:3101/api/blueprint/_recorder/start
sleep 300
curl -X POST http://localhost:3101/api/blueprint/_recorder/stop
ls -la data/blueprint-recordings/<domain.name>-*.jsonl
find data/blueprint-recordings -name '*.jsonl' -size -200c -delete
git add data/blueprint-recordings/<domain.name>-*.jsonl
git commit -m "record(blueprint): real walks for <domain.display_name>"
ZAVA_MODE=replay EXPECTED_TENANT_ID=<your-tenant-id> ./scripts/deploy-blueprint.sh
With this step, the new domain becomes visible on the deployed page
within minutes of graduation. No synthetic templates needed.
How to author a new domain brief (v4 worked example)
The 19 briefs at docs/superpowers/specs/archive/*-brief.yaml are the
authoritative spec for every generated domain (they live under
archive/ because they have already been graduated; new briefs are
authored under docs/superpowers/specs/ and archived after
graduate.sh succeeds). To author a new one:
- Pick a workflow_type — kebab-case, lowercase, e.g.
purchase-card. This name is stamped on every payload, every
FleetEvent, and every Cypher file the codegens emit.
- Copy the worked example. The freshest brief —
docs/superpowers/specs/archive/fleet-purchase-card-brief.yaml — was
authored top-down without a hand-written projection module. It
exercises every section the v4 schema cares about (domain,
phases, entities, decisions, function, ambient) at the smallest
plausible size. Use it as the template.
- Fill in the entity/decision shape. Each
entities[] entry
declares one node the projection will write per workflow; each
decisions[] entry declares one Decision node per HITL gate. The
codegens at sub-skills/author-entity-projection/codegen.py and
sub-skills/author-decision-mapping/codegen.py consume these.
- Validate. Run the brief through
_shared.brief_validator.validate_brief(...) (the smoke test at
tests/docs/superpowers/skills/compose_domain/test_fleet_purchase_card_smoke.py
shows the import path). The validator surfaces schema errors with
stable path field for assertions.
- Hand off to graduate. The compose-domain orchestrator above
handles the rest — the brief is the only operator-authored file.
Walking through a segment-default agent phase
Concrete example for an agentic phase generated under the default
kind: agent (segment-by-default) path. Assume the brief contains:
domain: { workflow_type: vendor-kyc, prefix: fleet, display_name: "Vendor KYC" }
phases:
- { name: vendor_intake, kind: deterministic, intent: "Capture vendor name + country." }
- name: kyc_diligence
kind: agent
agent_skill_name: kyc-diligence-checker
intent: "Resolve UBOs, sanctions, adverse-media."
external_systems: [companies_house, sanctions_api]
- { name: finance_signoff, kind: hitl, persona: vendor_kyc_finance_bp,
external_event: finance_signoff_decision }
For the kyc_diligence phase, compose-domain generates a segment
activity, not a MAF graph:
- Pick the segment letter. Segments are letter-indexed within a
workflow_type (A for the first segment, B for the second, …). For
a single-segment workflow the letter is
b by convention (mirrors
hiring_b); for multi-segment workflows allocate in order.
Example: kyc_diligence becomes Segment B of vendor-kyc →
api/functions/segments/vendor_kyc_b.py.
- Render
segment.py.tmpl. Fill in:
SKILL_LIST_LINES — every <wt>-<skill> SKILL.md the segment
should auto-load (the phase's own agent_skill_name plus any
adjacent agentic skills the operator decides belong in the same
CopilotSession).
MCP_LIST_LINES — the MCP tools resolved from the phase's
external_systems[].
OUTPUT_MODEL_BODY — the Pydantic SegmentBOutput(BaseModel)
class. One field per structured output the orchestrator reads
downstream (see enriched.get(...) references in the
orchestrator + HITL payloads). Custom validators go on the
model, not in a separate file.
SEGMENT_GOAL_SENTENCE — one sentence describing the deliverable,
matching the phase's intent.
- Render
segment_activity_trigger.py.tmpl to produce the
durable.py registration snippet. This emits two activity triggers:
vendor_kyc_segment_b_activity_trigger — calls
run_segment_b(input).
validate_vendor_kyc_segment_b_output_activity_trigger — Pydantic-
validates and returns {ok, output|errors}. The Pydantic errors
are JSON-serialised via json.loads(e.json()) so Durable does not
reject them.
- Wire the orchestrator retry loop. The orchestrator generated
from
orchestrator.py.tmpl MUST call the pair inside a
for attempt in range(segment_max_retries + 1) loop, feeding
validator["errors"] back as prior_validator_error on retry, and
breaking when validator["ok"]. Copy the loop shape from
api/functions/workflows/hiring.py:120-162. Keep the canonical
control-plane and Durable identities separate in enriched as
and
; never replace the former with
the latter. Add to , listing every
declared phase handled by that one CopilotSession, and forward it to
.
The runtime SKILL.md for vendor-kyc-kyc-diligence-checker is still
generated the same way it was for kind: graph (via
author-runtime-skill in phase_agent mode). The only thing that
changes between segment and legacy graph paths is the carrier of
the agent — segment activity vs. MAF graph.
Appendix — legacy kind: graph walkthrough
When the brief explicitly sets kind: graph (because one of the three
criteria in "When to use kind:graph" applies), the per-phase output
shape reverts to the v3 MAF-graph form. For the same kyc_diligence
phase:
- name: kyc_diligence
kind: graph
agent_skill_name: kyc-diligence-checker
intent: "Resolve UBOs, sanctions, adverse-media."
external_systems: [companies_house, sanctions_api]
compose-domain then generates three files (none of which are written
under the segment path):
api/functions/graphs/fleet_vendor_kyc_kyc_diligence.py from
phase_graph.py.tmpl — WorkflowBuilder wiring
agent → validator → terminal.
api/functions/graphs/executors/agents/agent_fleet_vendor_kyc_kyc_diligence.py
from agent_executor.py.tmpl — single-skill run_agent_session call.
api/functions/graphs/executors/validators/validate_fleet_vendor_kyc_kyc_diligence_schema.py
from validator.py.tmpl — in-graph validator returning
{"ok": bool, ...} (NOT a Pydantic model; the graph terminal
handles the ok=False branch).
The pack's durable.py gets ONE activity trigger
(vendor_kyc_kyc_diligence_activity_trigger) instead of the pair, and
the graph builder is exported from api/functions/graphs/__init__.py.
The orchestrator calls the trigger once per attempt (no in-orchestrator
retry loop — retry, if any, is the graph's responsibility).
Prefer the segment-default path. Reach for this appendix only when one
of the three "When to use kind:graph" criteria genuinely applies.
The 12 backfill briefs (everything matching docs/superpowers/specs/*-brief.yaml
that is NOT fleet-purchase-card) document the entity/decision shape
of the corresponding hand-written Phase 1 projection module at
api/server/services/entity_projections/<wt_snake>.py. The
hand-written modules carry domain-specific helper logic (period
derivation, conditional asset emission, JSON-blob attribute
serialisation, risk-band heuristics) that no schema-driven codegen
can synthesise from a brief alone — the briefs are documentation of
intent, not a regenerable source for those modules.
Anti-patterns (things you must not do)
- Inventing fields not present in the brief.
- Inventing the HITL external event name. Convention is
<phase_name>_decision. The orchestrator's
wait_for_external_event(...) and the persona SKILL.md both write
this exact string.
- Inventing tool names in an agent skill's
allowed-tools. The
author-runtime-skill step 4 list is authoritative — if a tool you
want isn't there, it's the caller's bug, not yours. Stop.
- Copying a runtime SKILL.md verbatim from
api/server/skills/ and just
renaming things — the result must be an authored skill, not a
rebadged one.
- Skipping the validator on agent phases ("it's a stub, the validator is
trivial"). Validators are the bounded-probabilism edge; the harness
expects them.
- Ignoring the read-on-demand policy by reading every canonical example
upfront, or generating a byte-shape without reading the specific fallback
example when the cheat-sheet is insufficient.
- Directly editing live-tree pack files (
durable.py, domains.py,
spawners.py, functions.py) "just to make the smoke test work". That's
graduation. It happens by hand. By an engineer. After this skill ends.
- Graduating "while you're at it" because the sandbox looks fine. Stop.
- Pretending you finished when you didn't. If you got blocked, say so. If
you skipped a step, say so. If a sub-skill returned something the
CHECKLIST won't pass, say so.
Operator-facing prompts
When you ask the operator a question, you are explicit about what you
need:
- "Brief at
docs/superpowers/specs/<x>-brief.yaml validates. Plan
follows. Confirm before I generate."
- "Step 3 noted that
api/functions/graphs/classify.py has changed shape
since this skill was last revised. Stopping. Please update
compose-domain/SKILL.md step 3 with the new canonical paths and
re-invoke."
- "CHECKLIST item §3.4 FAIL: agent skill
<x> references MCP tool <y>
in allowed-tools but no such tool exists in the sandbox or
api/server/mcp_tools/. The bug is probably in
author-runtime-skill/SKILL.md step 4 (allow-list resolution).
Stopping."