name: compose-org description: > Fork an agentic substrate into a customer-flavoured digital clone using an org-brief YAML (from research-company) + the matching industry primer. Clones the substrate to a sibling repo, rebrands literal tokens, repacks the data-fabric generators against the brief's subsidiaries + named ELT, swaps the Kuzu entity-kind tables per the primer, extends the domain registry with the primer's workflow library, generates personae (ELT named from the brief, archetypes from the primer), seeds cadenced rituals + narrative arcs, and scaffolds Node MCP mocks for the brief's stack overrides. Local-only fork by default; no GitHub push. Refuses dirty trees, idempotent re-runnable. USE FOR: fork the substrate for a named customer (Telco, FSI, Airline, Retail, OEM, …), produce a digital-clone-grade demo repo. DO NOT USE FOR: incrementally adding one domain (use compose-domain inside the fork — planned, not yet available), authoring a new substrate from scratch, pitch decks. metadata: version: "1.0.2"
compose-org
Fork the agentic substrate into a customer-flavoured digital clone by joining a thin org-brief with the matching industry primer.
research-company → briefs/<slug>-org-brief.yaml
↓
industry-primers/<vertical>.md
↓
compose-org (this skill)
↓
<substrate>-<slug>/ (local fork)
↓
make up (operator)
When to use
Invoke after research-company has produced an org-brief whose
meta.status is ready and whose meta.primer points at an
existing industry primer.
Inputs
- Brief path — absolute path or path relative to cwd to the
org-brief produced by
research-company(e.g.briefs/<slug>-org-brief.yamlif you ran the catalog-clone flow, or<cwd>/briefs/<slug>-org-brief.yamlif you ran the remote-bootstrap flow per RUNBOOK.md). - Substrate source (optional) — one of:
- A local path to an existing substrate clone (e.g.
../zava-control-plane). - A git URL to clone from (e.g.
https://github.com/arturcrmbot/zava-control-plane). - Omitted — defaults to cloning from
https://github.com/arturcrmbot/zava-control-plane.
- A local path to an existing substrate clone (e.g.
- Fork target path (optional) — where to write the fork.
Defaults to
<cwd>/zava-control-plane-<slug>(a sibling of thebriefs/directory inside cwd).
Output
A new local git repo at the fork-target path containing the rebranded
- customised substrate. No GitHub remote is configured. Operator
runs
gh repo createlater if they want to push.
Pre-flight (refuse to proceed if any fail)
| Check | Failure mode |
|---|---|
| Brief file exists and parses as YAML | Stop; ask operator. |
brief.meta.status == ready |
Stop; tell operator to sign off the brief first. |
brief.meta.primer resolves to an existing primer |
Stop; offer to graduate a stub primer or pick a different vertical. |
Substrate source available — either the supplied local path is a clean git tree on default branch, OR the supplied/default URL is reachable via git ls-remote |
Stop; refuse to clone from a dirty state or a network failure. |
| Fork target path does not already exist | Stop. To re-run, the operator removes the target dir first (or uses --allow-overwrite flag explicitly — see § "Idempotent re-runs"). |
git, python3 ≥ 3.11, node ≥ 20, npm available |
Stop; install hint per missing tool. |
Substrate paths (Zava control plane reference)
The skill assumes the target substrate follows the
zava-control-plane
layout. The phases below reference these paths verbatim. If you fork
this skill for a different substrate, update the path table here.
| Concept | Path in substrate |
|---|---|
| Subsidiary seed list | api/server/data_fabric/employee_gen.py (SUBSIDIARIES tuple) |
| Client/Brand generator | api/server/data_fabric/client_brand_gen.py |
| Cadenced rituals seed | api/server/data_fabric/cadenced_rituals.py |
| Narrative arcs seed | api/server/data_fabric/narrative_arcs.py |
| Kuzu entity schema | api/server/services/entity_graph.py |
| Function registry | api/shared/functions.py |
| Persona registry | api/shared/personas.py + api/server/personae/<role>/ |
| Domain registry | api/shared/domains.py |
| Node mocks | mocks/<id>/ |
| Rebrand playbook | plan/archive/refactor-rebrand-zava-1.md |
The ten phases
Phase 0 — Pre-flight
Run every check in the table above. Print a green/red summary. Stop on any red.
Phase A — Acquire the substrate
Either clone fresh from a git URL, or copy from a local path, depending on what was supplied:
# Default — fresh clone from the public substrate repo
git clone https://github.com/arturcrmbot/zava-control-plane <fork-target>
# OR — if a local substrate path was supplied
git clone <local-substrate-path> <fork-target>
cd <fork-target>
git remote remove origin # no remote — local-only
git checkout -b main # ensure clean main
Phase A always re-points origin to nothing — the fork is
local-only by default. The operator runs gh repo create later
if they want to push.
Phase B — Rebrand (literal find-and-replace)
The substrate already ships a rebrand playbook —
plan/archive/refactor-rebrand-zava-1.md —
that documents every literal token to swap. Follow it verbatim with
the mappings derived from the brief:
| Old token | New token (derived from brief) |
|---|---|
Zava Control Plane |
<brief.identity.short_name> Control Plane |
zava-control-plane |
zava-control-plane-<brief.identity.slug> |
Zava (literal, with trailing space) |
<brief.identity.short_name> |
Zava- |
<brief.identity.short_name>- |
zava.skill, zava.tool.*, zava.fleet_manager.* |
<brief.identity.slug>.skill, <brief.identity.slug>.tool.*, <brief.identity.slug>.fleet_manager.* |
Zava (standalone, last) |
<brief.identity.short_name> |
Tight allowlist of file extensions: .md, .py, .ts, .tsx,
.js, .jsx, .yml, .yaml, .json, .toml, .sh, .css,
.html, Dockerfile, Makefile.
Forbidden paths — never edit (would corrupt binary assets or
break tests with cosmetic changes): **/*.png, **/*.jpg,
**/*.avif, **/*.svg, **/*.mp4, **/azurite-data/**,
**/data/portal/**/*.kuzu, **/data/.eval/**, **/__pycache__/**,
**/.venv/**, **/node_modules/**, **/.git/**.
Commit the rebrand as one atomic commit:
chore: literal rebrand <substrate> → <substrate>-<slug>.
Phase C — Repack the data fabric
C.1 — SUBSIDIARIES tuple
Replace the SUBSIDIARIES tuple at the top of
api/server/data_fabric/employee_gen.py with rows derived from
brief.subsidiaries[]. Format (per existing convention):
SUBSIDIARIES: tuple[str, ...] = (
# one entry per brief.subsidiaries[].name
"<Subsidiary 1 name>",
"<Subsidiary 2 name>",
...
)
If the brief has fewer than 5 subsidiaries, pad with placeholder
opcos drawn from the primer's regulator countries (e.g.
<short_name> Singapore Pte Ltd) to keep the substrate's data-volume
expectations intact.
C.2 — Client/Brand generator
Replace api/server/data_fabric/client_brand_gen.py with the
primer's vertical-equivalent. For telco, that means generating
Customer rows (enterprise accounts) and Service SKUs instead of
Client rows and Brand rows. Seed customer names from
brief.customers_or_segments; seed Service catalogue from the
primer's typical Service list.
The function signature generate_clients_and_brands(...) stays the
same (callers in pack.py don't change); only the implementation
swaps. Add a one-line module docstring noting the source primer.
C.3 — Cadenced rituals seed
Append to api/server/data_fabric/cadenced_rituals.py one row per
ritual in the primer's "Canonical cadenced rituals" table. The
substrate's ritual schema is {id, display_name, cadence, owner_function} — read the file's existing format and match.
C.4 — Narrative arcs seed
Append to api/server/data_fabric/narrative_arcs.py one row per
brief.strategic_themes[]. The substrate's narrative-arc schema is
{id, headline, summary, function_focus, timeline_hint}.
Phase D — Schema swap (Kuzu entity kinds)
Edit api/server/services/entity_graph.py to:
- Rename the agency-specific node tables per the primer's
"Canonical vertical entity kinds" table. Telco mapping:
Brand→ServiceCampaign→CircuitPitch→QuoteMediaPlan→CapacityPlan
- Add the new-kind tables the primer introduces (telco:
Site,PointOfPresence,Customer,Incident,Ticket,NetworkElement,IPPrefix,BGPPeering,CrossConnect,Licence,Spectrum). - Update the projection mapping in
api/server/services/entity_projections.pyso existing code that wrote toBrandnow writes toService, etc. Mechanical find-and-replace inside that file ONLY.
Respect the stored Kuzu schema-syntax constraints — inline
LIMITints, backtick reserved words, trailingPRIMARY KEY (id), noSET n += $map.
Phase E — Functions & personae
E.1 — Function registry
Replace api/shared/functions.py with rows derived from the
primer's "Canonical functions" table. Each function gets:
Function(
id="<id>",
display_name="<display_name>",
importance="<hero|core|support>",
)
E.2 — Persona folders
For each row in brief.leadership[], ensure a folder exists at
api/server/personae/<role>/ with a SKILL.md whose frontmatter
includes the real name + title + function from the brief. If the
folder already exists in the substrate (e.g. ceo, cfo, coo),
edit the frontmatter — don't overwrite the persona logic.
For each persona archetype in the primer's "Org chart archetypes"
section that does NOT correspond to a brief-named leader, generate
a folder at api/server/personae/<id>/ with a stub SKILL.md. The
decision_policy block can be a minimal default; archetype names
get expanded by faker in employee_gen.py at boot.
Update api/shared/personas.py to register every new persona.
Phase F — Domain composition
Append rows to api/shared/domains.py for each entry in the
primer's "Proposed-domains starter library". Use the substrate's
Domain(...) constructor signature verbatim:
Domain(
workflow_type="<workflow_type>",
display_name="<display_name>",
workflow_id_prefix="<UPPER-PREFIX>-",
orchestrator_name="<PascalCase>Orchestrator",
operator_surface="...",
phases=(...),
hitl_gates=(...),
skills=(...),
function="<function-id>",
realistic_interval_seconds=<int>,
stub=True, # all new domains start as stubs
),
Mark every newly-added domain as stub=True — the operator
graduates them one at a time via compose-domain inside the new
fork. Do not generate orchestrator files / graphs / skills here.
That's compose-domain's job.
Tag each new domain with the brief's strategic-theme overrides:
domains whose function is in any brief.strategic_themes[].function_focus[]
get importance: hero; the rest importance: supporting (subject
to ≤ 3 hero cap).
Phase G — Stack mocks
For each row in brief.stack_overrides[], scaffold a Node mock
under mocks/<id>/:
mocks/<id>/server.ts— minimal FastMCP-style mock, ~80 lines, copies the shape of an existing mock undermocks/(e.g.workday,concur).mocks/<id>/package.json— one entry inmocks/<id>/package.jsonreferencing the same parent deps as existing mocks.- Port assignment: next free port in the 4200–4299 range (read existing mocks/* to find used ports).
Generic Zava mocks (workday, concur, etc.) stay in place — the overrides are additive, not replacing.
Phase H — Re-seed the data fabric
Run the substrate's snapshot regeneration:
cd <fork-target>
make funcvenv # one-time, Windows-friendly
uv sync
python -m api.server.data_fabric.pack --regenerate-snapshot
This rebuilds the Kuzu snapshot under data/snapshots/ so a
cold-start make up reads the new entity-kind tables + new
subsidiaries.
Phase I — Smoke test
make test # substrate's existing test suite
If anything fails:
- Test failures referencing literal
Zavastrings — incomplete rebrand; re-run Phase B with the failed-test paths added to the allowlist. - Kuzu schema errors — Phase D mistake; check primer's relations
list and the substrate's
_VALID_RELS. - Persona registry mismatch — Phase E.2 missed a row; cross-check
personas.pyagainstapi/server/personae/folder list.
Do NOT proceed to hand-off with red tests. Fixes are a tight loop between the agent and the operator.
Phase J — Hand off
Print to operator:
✅ Fork ready: <fork-target>
git log --oneline # one rebrand commit + N compose commits
cd <fork-target>
make up # boots Azurite, mocks, FastAPI, control-plane UI
Visit http://localhost:5273 (control plane)
http://localhost:5275 (blueprint microsite)
To promote a stub domain to live:
copilot
> Run compose-domain on `<workflow_type>` (inside this fork)
To push to GitHub later:
gh repo create zava-control-plane-<slug> --private
git remote add origin <url>
git push -u origin main
Idempotent re-runs
Re-running compose-org against an existing fork target is supported
via --allow-overwrite. The skill:
- Checks the existing fork's
git statusis clean. - Discards uncommitted changes only if the operator confirms.
- Re-applies every phase as if from a fresh clone.
If the operator has hand-edited the fork, the safest path is to delete the fork dir and re-run from scratch — the brief is the source of truth.
Safety rails
- No
gh repo createautomatic push. Forks are local-only by default; pushing to GitHub is an explicit operator action. - Rebrand allowlist is tight. Binary assets,
.git/,node_modules/,.venv/,azurite-data/,data/.eval/are never edited. - No
.png/.mp4/ avatar regeneration. Demo media is intentionally inherited — the rebrand is text-only. - Refuses to overwrite without explicit flag. First failure mode for accidentally re-running is a no-op.
- Per-phase commit boundaries. Every phase commits atomically
so
git revert <sha>rolls back one phase cleanly.
Output budget
A first-run compose-org against a Tier-1 vertical produces:
- ~30–40 commits
- ~150–250 files modified by the rebrand
- ~10–15 new files (mocks, persona folders)
- ~5,000–10,000 lines of diff total
If diff exceeds 20,000 lines, the rebrand allowlist is probably too broad — re-check the forbidden-paths list.
Iterating the skill
When a generated fork looks wrong:
- Per-phase issues: fix the phase prose in this SKILL.md, then
re-run with
--allow-overwrite. - Primer issues: fix the matching primer file, then re-run.
- Brief issues: fix the brief, re-run.
Two runs against the same brief + primer should diff to nothing meaningful except commit timestamps.
Downstream
After compose-org finishes, the operator's typical next steps:
- Verify —
make up, walk the control plane UI, confirm domain names, persona names, KPIs match expectations. - Graduate a stub domain — pick a hero domain (e.g.
quote-to-circuitfor telco) and runcompose-domainon it inside the new fork. That fleshes out orchestrator / phase graphs / agent skills / personae. - Demo prep — boot, time-warp scrub, capture a 30-min walkthrough.