compose-persona
Design-time meta-skill (v1). Given a persona brief (YAML) or a free-text idea, produce a complete Persona artefact set: a SKILL.md (frontmatter + executable decision_policy + prose body), a Python registry entry pasteable into `api/shared/personas.py`, and a graduate.sh script that mechanically wires the persona into the live trees. Sandbox-only; never touches real trees directly. Calls `author-persona` as its sole sub-skill. Sister to `compose-domain`. Where `compose-domain` graduates a whole Durable workflow + skills + personae for one new business process, `compose-persona` graduates ONE persona — useful when an existing domain needs an additional approver / reviewer, or when a new function (finance, legal, IT) needs its cast filling out before its domain lands.
- License 许可
- MIT
- 最近更新
- 2026-09-28
- Stars
- ★ 1
- Forks
- 2
01 前言 Front Matter
- name
- compose-persona
- audience
- design-time-only
- description
- Design-time meta-skill (v1). Given a persona brief (YAML) or a free-text idea, produce a complete Persona artefact set: a SKILL.md (frontmatter + executable decision_policy + prose body), a Python registry entry pasteable into `api/shared/personas.py`, and a graduate.sh script that mechanically wires the persona into the live trees. Sandbox-only; never touches real trees directly. Calls `author-persona` as its sole sub-skill. Sister to `compose-domain`. Where `compose-domain` graduates a whole Durable workflow + skills + personae for one new business process, `compose-persona` graduates ONE persona — useful when an existing domain needs an additional approver / reviewer, or when a new function (finance, legal, IT) needs its cast filling out before its domain lands.
- forbidden-runtime
- true
03 SKILL.md 正文
compose-persona (v1)
You are the orchestrator for a five-step procedure that turns a persona brief into a complete sandboxed persona artefact set. 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-persona/<run-id>/anddocs/superpowers/specs/. In particular: never editapi/server/personae/,api/shared/personas.py,api/server/services/persona_responder.py, or any other live tree. Graduation is a separate, manual step (the operator runs the generatedgraduate.sh).
Inputs
You are invoked one of two ways:
- With a brief path. "Run compose-persona against
docs/superpowers/specs/<role>-persona-brief.yaml." Skip step 1. - With a free-text idea. "Compose a new persona for X." Run step 1 to produce the brief, then continue.
The five steps
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.
Capture the operator's answers into the YAML schema below and write
to docs/superpowers/specs/<role>-persona-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, v1)
persona:
role: <snake_case role name, used as folder/file prefix>
archetype: approver | subject | reviewer | delegate | notifier
scope_function: finance | hr | it | procurement | legal | legal_privacy | commercial | candidate
scope_business_unit: <string or "*"> # OPTIONAL; default "*"
scope_geography: <string or "*"> # OPTIONAL; default "*"
workflow_label: <human label of the domain this persona belongs to>
external_event: <snake_case> # OPTIONAL; default <role>_decision
default_authority_band: <free-text> # OPTIONAL; documentation only
uses_authority_mcp: true | false # OPTIONAL; default false
description: <one sentence>
decision_policy_paragraph: |
One paragraph stating the rule the persona uses to decide.
decision_inputs: # OPTIONAL; documentation
- <context_key_the_policy_reads>
- ...
decision_authority_action: <action> # REQUIRED iff uses_authority_mcp=true.
# Must match an `action` in
# data/synthetic/authority/matrix.json.
decision_code: |
# Python source the persona_responder compiles. Reads `context`,
# assigns `decision` ("approve" | "reject" | "escalate") and
# `reason` (str). Mirror the decision_policy_paragraph above.
# The responder runs this in a sandboxed namespace with a small
# whitelist of safe builtins (see api/server/services/persona_responder.py
# _DECISION_BUILTINS — currently includes `authority_check` for
# personae that consult the delegated-authority MCP).
pass
Validate the brief before continuing:
rolematches^[a-z][a-z0-9_]*$.archetypeis one of the five allowed strings.scope_functionis one of the eight allowed strings.external_eventis^[a-z][a-z0-9_]*$if provided.- If
uses_authority_mcp: true,decision_authority_actionis set AND the decision_code containsauthority_check(...). - The decision_code uses only the safe builtins from
api/server/services/persona_responder.py_DECISION_BUILTINS(noimport, noopen, noos, nosubprocess, noeval). - The decision_code assigns both
decisionandreason.
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".
Always:
- 1 persona SKILL.md at
<RUN_ROOT>/api/server/personae/<role>/SKILL.md. - 1
REGISTRY-ENTRY.pyat the run root containing the Python snippet to splice intoapi/shared/personas.pyPERSONASdict. - 1
GRADUATION.mddescribing whatgraduate.shwill do. - 1 executable
graduate.shthat mechanically performs the live-tree edits.
Step 3 — Inventory and isomorphism
Before invoking author-persona, read exactly these 3 files
end-to-end. They are the canonical examples this skill mirrors.
| # | Canonical file | Why |
|---|---|---|
| 1 | api/server/personae/finance_bp/SKILL.md |
Authority-MCP-using persona (template for uses_authority_mcp: true) |
| 2 | api/server/personae/line_manager/SKILL.md |
Inline-logic persona (template for uses_authority_mcp: false) |
| 3 | api/server/services/persona_responder.py (_DECISION_BUILTINS) |
Sandbox surface — whitelist of callables the decision_code may use |
Drift in any of these means downstream output will be stale-shaped — stop and update this SKILL before continuing.
Step 4 — Generate into sandbox
Compute RUN_ID = <YYYYMMDD-HHMMSS>-<role> from current UTC
(date -u +"%Y%m%d-%H%M%S"). Create
tools/scratch/compose-persona/<RUN_ID>/.
4.1 — Invoke author-persona
output_path: <RUN_ROOT>/api/server/personae/<role>/SKILL.md
brief: <persona block from the YAML brief>
domain_display_name: <persona.workflow_label>
default_external_event: <persona.external_event or persona.role + "_decision">
canonical_example_path:
- api/server/personae/finance_bp/SKILL.md (when uses_authority_mcp: true)
- api/server/personae/line_manager/SKILL.md (when uses_authority_mcp: false)
responder_path: api/server/services/persona_responder.py
author-persona writes the SKILL.md to the sandbox path.
4.2 — Write <RUN_ROOT>/REGISTRY-ENTRY.py
# Splice into api/shared/personas.py inside the PERSONAS dict.
# Place under the matching domain section comment block.
"<role>": Persona(
role="<role>",
archetype="<archetype>",
scope_function="<scope_function>",
workflow_label="<workflow_label>",
external_event_default="<external_event>",
scope_business_unit="<scope_business_unit>", # default "*"
scope_geography="<scope_geography>", # default "*"
default_authority_band=<None or "<text>">,
uses_authority_mcp=<True | False>,
description="<description>",
),
4.3 — Write <RUN_ROOT>/GRADUATION.md
Human-readable explanation of what graduate.sh does:
- Copy
<RUN_ROOT>/api/server/personae/<role>/SKILL.md→api/server/personae/<role>/SKILL.md. - Splice
<RUN_ROOT>/REGISTRY-ENTRY.pycontent intoapi/shared/personas.pyPERSONASdict (between the appropriate per-domain comment markers — operator's manual call where exactly). - Add
<role>toPERSONA_AUTO_CLOSEenv var ONLY if the demo profile expects this persona to auto-close gates. Default: do not add (production-honest default). - Run
pytest tests/api/shared/test_personas_registry.pyto confirm the registry validates with the new entry.
4.4 — Write <RUN_ROOT>/graduate.sh
Executable script:
#!/usr/bin/env bash
# graduate.sh — mechanically promote the persona generated at this run root
# into the live trees. Re-runnable; idempotent on the SKILL.md copy.
# The registry-entry splice is intentionally manual — printed to stdout for
# the operator to paste into the right per-domain section of personas.py.
set -euo pipefail
RUN_ROOT="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$RUN_ROOT/../../.." && pwd)"
ROLE="<role>"
SKILL_SRC="$RUN_ROOT/api/server/personae/$ROLE/SKILL.md"
SKILL_DST="$REPO_ROOT/api/server/personae/$ROLE/SKILL.md"
if [ ! -f "$SKILL_SRC" ]; then
echo "ERROR: missing SKILL.md at $SKILL_SRC" >&2
exit 1
fi
mkdir -p "$(dirname "$SKILL_DST")"
cp "$SKILL_SRC" "$SKILL_DST"
echo "wrote $SKILL_DST"
cat <<EOF
Manual splice required:
-----------------------
Open $REPO_ROOT/api/shared/personas.py and paste the entry below
inside the PERSONAS dict, under the matching domain's comment block.
EOF
cat "$RUN_ROOT/REGISTRY-ENTRY.py"
echo
echo " Then run: uv run pytest tests/api/shared/test_personas_registry.py"
chmod +x the file.
Step 5 — Self-check + operator handoff
Before returning, verify:
- All four files exist at
<RUN_ROOT>/. SKILL.mdparses as YAML frontmatter + markdown body (yaml.safe_load(frontmatter_block)works; body has the three required##sections fromauthor-persona).SKILL.mddecision_policyblock compiles (compile(source, "<test>", "exec")doesn't raise).REGISTRY-ENTRY.pyis valid Python and uses one of the documentedarchetypevalues.graduate.shis executable (-xbit set).
Return a one-paragraph summary to the operator naming the four generated paths and the next-step command (the manual splice + the pytest command).
Anti-patterns
- Letting the prose paragraph and the executable block disagree. The prose tracks the code; if they diverge, the brief author needs to resolve it before this skill runs.
- Generating a persona that uses the authority MCP without setting
uses_authority_mcp: truein the registry entry. The registry flag is the auditable signal that this persona doesn't inline thresholds. - Modifying the live
api/shared/personas.py. Always emit theREGISTRY-ENTRY.pysnippet for the operator to splice manually. Mechanical splicing risks placing the entry under the wrong per-domain comment block. - Adding the persona to
PERSONA_AUTO_CLOSEautomatically. Production-honest default: every gate stays open until explicitly opted in. Demo profiles set the allow-list explicitly.
04 同仓库更多 Skills
proactive-customer-care
Proactively notifies and, where warranted, extends a small bounded goodwill gesture to a customer whose itinerary materially changed.
payment-exception
Resolves a failed balance payment by retrying it to complete the booking, or releasing the booking's inventory when retry is not viable.
hotel-supplier-recovery
Restores accommodation for a shortfall-hit hotel allotment by moving contracted rooms from a donor allotment with headroom, resolving the reported supplier shortfall.
holiday-sales-booking
暂无简介
flight-disruption-recovery
暂无简介
destination-operations
Dispatches a replacement resort transfer when an arrival is at risk, rebinding any travellers on the at-risk transfer to the replacement.