Split tasks by profile
Turn what has to be built into one self-contained scope per profile, written
to docs/delivery/assignments.json.
The profiles are the project's personas — they already exist, with a daily
rate. Never invent a role that is not in the personas file; if the work needs a
profile that is missing, say so and leave personaId empty rather than
fabricating one.
Inputs — read these before writing anything
| Source |
Default path |
What you take from it |
| Personas |
docs/delivery/personas.json |
the only allowed set of profiles (id, name, description, tjm) |
| Costing sheets |
docs/delivery/costing/*.json |
existing features/tasks, mandays, complexity, moscow, priority, personaId, predecessorId |
| Specs |
specs/ |
the requirements that define the work |
| Decisions |
docs/decisions/ |
constraints already settled — do not re-open them |
| Diagrams |
docs/diagrams/ |
the module/component boundaries that scopes should follow |
| Code |
the repository |
the real directories, so every scope entry is a path that exists |
When the host project puts an artifact somewhere else, use the host path.
If a costing sheet already sets personaId on a task, that assignment wins.
Your job there is to group and sequence it, not to reassign it.
Method
- Assign by module/domain tree, not by task. Give each profile a
self-contained set of directories. One writer per file at any time — if two
profiles would touch the same file, that is a design error, fix the split.
- Sequence the foundations first. Shared pieces everyone consumes (auth,
contracts, shared types, event cores) go to whoever unblocks the rest, and
their items come first.
- Name every coordination seam. Where two scopes meet, apply
owner exposes (service + shared contract), consumer wires on top. List the
seam explicitly with the contract that crosses it.
- Carry the numbers through. When a costing task exists, reuse its
mandays; do not re-estimate silently. When you do estimate, say so in why.
- Mark what is blocked. A missing credential, a pending decision or an
unanswered question is a gate, not a task. Gates name what unblocks them
and who approves.
- Write a brief per profile that is safe to send as-is: scope, items, first
steps, seams. Never put private judgements about a person in the file.
See work-splitting when the question is how to
cut the work itself rather than who takes which part.
Output
File: docs/delivery/assignments.json — a single JSON object.
{
"version": 1,
"generatedAt": "<YYYY-MM-DD>",
"sources": [],
"assignments": [
{
"personaId": "",
"personaName": "",
"scope": [],
"brief": "",
"items": [
{
"id": "A1",
"title": "",
"why": "",
"firstSteps": [],
"specRef": "",
"taskRef": "",
"mandays": 0,
"gate": ""
}
],
"seams": [{ "with": "", "contract": "", "rule": "" }]
}
],
"gates": [{ "what": "", "blocks": [], "unblockedBy": "", "approver": "" }],
"overlapRisk": ""
}
Rules:
personaId — an id from the personas file, or "" when no persona fits
(then explain the missing profile in brief).
personaName — the persona's name, or the profile you would need.
scope — repository-relative directory paths that exist. No globs, no ...
items[].id — A1, A2, … unique across the whole document.
items[].firstSteps — 1 to 3 concrete actions, each naming a file or a command.
items[].mandays — a number; 0 when unknown. Never a string.
items[].gate — the what of a gate in gates[], or "".
seams[].with — the personaName on the other side of the seam.
gates[].blocks — item ids that cannot start until the gate clears.
- Every key is always present; use
"" / [] / 0 for unknowns, never null.
If the file already exists, read it first. Keep existing items[].id values
stable so links from elsewhere keep resolving; add, update or remove entries
rather than regenerating fresh ids.
Procedure
Read the personas file. If it is missing or empty, stop and tell the user to
create personas first — there is nothing to split work across.
Read the costing sheets and the specs; list the work items.
Group items into per-profile scopes following the Method above.
Read the existing docs/delivery/assignments.json if present, and merge.
Write the document.
Validate:
python3 skills/split-tasks-by-profile/scripts/validate_assignments.py \
docs/delivery/assignments.json docs/delivery/personas.json
Fix everything it reports and re-run until it prints OK.
Report: how many profiles, how many items, the single biggest overlap risk,
and every open gate with its approver.
Boundaries
- Do not commit or push. The user commits.
- Do not change the personas file or the costing sheets from this skill.
- Do not write private notes about individuals — this file is shared.
1---2name: split-tasks-by-profile3description: Split delivery work across the project's personas (profiles) and write the assignment plan as validated JSON. Use when the user asks who does what, to dispatch or assign tasks, to split work by profile or role, to build a work-breakdown per developer, or to produce an assignment brief per team member.4license: MIT5---67# Split tasks by profile89Turn what has to be built into **one self-contained scope per profile**, written10to `docs/delivery/assignments.json`.1112The profiles are the project's **personas** — they already exist, with a daily13rate. Never invent a role that is not in the personas file; if the work needs a14profile that is missing, say so and leave `personaId` empty rather than15fabricating one.1617## Inputs — read these before writing anything1819| Source | Default path | What you take from it |20| --- | --- | --- |21| Personas | `docs/delivery/personas.json` | the **only** allowed set of profiles (`id`, `name`, `description`, `tjm`) |22| Costing sheets | `docs/delivery/costing/*.json` | existing features/tasks, `mandays`, `complexity`, `moscow`, `priority`, `personaId`, `predecessorId` |23| Specs | `specs/` | the requirements that define the work |24| Decisions | `docs/decisions/` | constraints already settled — do not re-open them |25| Diagrams | `docs/diagrams/` | the module/component boundaries that scopes should follow |26| Code | the repository | the real directories, so every `scope` entry is a path that exists |2728When the host project puts an artifact somewhere else, use the host path.2930If a costing sheet already sets `personaId` on a task, that assignment **wins**.31Your job there is to group and sequence it, not to reassign it.3233## Method34351. **Assign by module/domain tree, not by task.** Give each profile a36 self-contained set of directories. **One writer per file at any time** — if two37 profiles would touch the same file, that is a design error, fix the split.382. **Sequence the foundations first.** Shared pieces everyone consumes (auth,39 contracts, shared types, event cores) go to whoever unblocks the rest, and40 their items come first.413. **Name every coordination seam.** Where two scopes meet, apply42 *owner exposes (service + shared contract), consumer wires on top*. List the43 seam explicitly with the contract that crosses it.444. **Carry the numbers through.** When a costing task exists, reuse its45 `mandays`; do not re-estimate silently. When you do estimate, say so in `why`.465. **Mark what is blocked.** A missing credential, a pending decision or an47 unanswered question is a **gate**, not a task. Gates name what unblocks them48 and who approves.496. **Write a brief per profile** that is safe to send as-is: scope, items, first50 steps, seams. Never put private judgements about a person in the file.5152See [`work-splitting`](../work-splitting/SKILL.md) when the question is how to53cut the work itself rather than who takes which part.5455## Output5657File: `docs/delivery/assignments.json` — a single JSON object.5859```json60{61 "version": 1,62 "generatedAt": "<YYYY-MM-DD>",63 "sources": [],64 "assignments": [65 {66 "personaId": "",67 "personaName": "",68 "scope": [],69 "brief": "",70 "items": [71 {72 "id": "A1",73 "title": "",74 "why": "",75 "firstSteps": [],76 "specRef": "",77 "taskRef": "",78 "mandays": 0,79 "gate": ""80 }81 ],82 "seams": [{ "with": "", "contract": "", "rule": "" }]83 }84 ],85 "gates": [{ "what": "", "blocks": [], "unblockedBy": "", "approver": "" }],86 "overlapRisk": ""87}88```8990Rules:9192- `personaId` — an `id` from the personas file, or `""` when no persona fits93 (then explain the missing profile in `brief`).94- `personaName` — the persona's `name`, or the profile you would need.95- `scope` — repository-relative directory paths that exist. No globs, no `..`.96- `items[].id` — `A1`, `A2`, … unique across the **whole** document.97- `items[].firstSteps` — 1 to 3 concrete actions, each naming a file or a command.98- `items[].mandays` — a number; `0` when unknown. Never a string.99- `items[].gate` — the `what` of a gate in `gates[]`, or `""`.100- `seams[].with` — the `personaName` on the other side of the seam.101- `gates[].blocks` — item ids that cannot start until the gate clears.102- Every key is always present; use `""` / `[]` / `0` for unknowns, never `null`.103104If the file already exists, **read it first**. Keep existing `items[].id` values105stable so links from elsewhere keep resolving; add, update or remove entries106rather than regenerating fresh ids.107108## Procedure1091101. Read the personas file. If it is missing or empty, stop and tell the user to111 create personas first — there is nothing to split work across.1122. Read the costing sheets and the specs; list the work items.1133. Group items into per-profile scopes following the Method above.1144. Read the existing `docs/delivery/assignments.json` if present, and merge.1155. Write the document.1166. **Validate**:117118 ```bash119 python3 skills/split-tasks-by-profile/scripts/validate_assignments.py \120 docs/delivery/assignments.json docs/delivery/personas.json121 ```122123 Fix everything it reports and re-run until it prints `OK`.1247. Report: how many profiles, how many items, the single biggest overlap risk,125 and every open gate with its approver.126127## Boundaries128129- Do not commit or push. The user commits.130- Do not change the personas file or the costing sheets from this skill.131- Do not write private notes about individuals — this file is shared.