flow-persona-author
Per-persona behavioral persona-doc authoring sub-skill. Writes ONE markdown file per persona at docs/product/personas/<slug>.md, conforming to the canonical persona template. Single Agent(persona-doc-author) per persona slug.
This skill is NOT user-invocable (disable-model-invocation: true, per Q7). The orchestrators (/flow:start-project, /flow:add-domain) dispatch it by name; flow-doc-author and flow-journey-author are its siblings.
Ordering constraint. Runs AFTER flow-journey-author (which runs after flow-doc-author). Both upstream sets must exist before a persona is authored: the story docs are the source of the personas: slug set this skill enumerates, and the journey docs are the persona-doc-author agent's richest behavioral source (their per-phase persona mindset lines, pain points, and decision points). Authoring a persona before its journeys exist would starve the agent of exactly the material that lifts a persona above the generic-block P5 floor.
The persona subsystem this skill completes: existence floor (flow_persona_lint.py / persona-exists, BC-12573, deterministic) → depth grader (quality-reviewer doc_kind: persona_doc, rubric P1–P5, LLM) → author (persona-doc-author, dispatched here). ADR-041 fixes the field's meaning: personas: is behavioral persona-doc slugs only, never RBAC/access roles.
1. Authoring strategy — whole-file agent, NO builder
Unlike flow-doc-author (story) and flow-journey-author (journey), there is no build_*_frontmatter.py step. Those skills stamp front-matter deterministically because it carries Linear children: [BC-…] / parent_issue / a milestone UUID the agent cannot know. A persona's front-matter is role / device / linear_label / last_reviewed — all derivable from this skill's inputs, none from Linear — so Agent(persona-doc-author) writes the front-matter itself and returns the whole file. The one field the agent does not invent is last_reviewed: it stamps the dispatcher-supplied today.
The skill's only post-processing on the returned markdown:
- Strip HTML comments — the agent cites a load-bearing source inline as an
<!-- … -->comment (and may emit<!-- TODO: <field> -->for a genuinely-unknown specific); strip whole-line and inline HTML comments before writing. - Catch the error sentinel — if the agent returns a single
<!-- PERSONA-DOC-AUTHOR-ERROR: <reason> -->(missing required input), do NOT write the file; surface it in the end-of-run summary and continue (Section 5). - Write
docs/product/personas/<slug>.md.
The agent contract (inputs, steps, output) is agents/persona-doc-author.md. The substance bar is skills/_shared/quality-rubric.md P1–P5 scored by quality-reviewer (doc_kind: persona_doc); the structural floor is the separate persona-exists gate.
2. The persona set — reconciled union of story slugs ∪ inventory column
The set of personas to author is the union of:
- Story-doc
personas:slugs — walk everydocs/product/flows/<domain>/*.mdstory doc, collect each non-emptypersonas:front-matter slug (the same parse asflow_persona_lint.py: strip a trailing(qualifier), split on,and;). This is the authoritative set — it is exactly what the persona-exists floor checks. - Inventory persona column / intent
## Target users— themaster-flow-inventory.mdpersona column andintent.md## Target usersmay name a persona the stories also reference; reconcile (dedup by slug) so the authored set matches the slugs in play.
minus honest-empty — a story with personas: [] / absent contributes nothing (ADR-029 honest-empty canon; presence, not non-emptiness). A pure-automation flow that names no behavioral persona adds no row.
Authoring the reconciled union — rather than the inventory column alone — is what makes the dispatch test's "every produced persona resolves its own existence check" true by construction: the set this skill authors is exactly the set the floor lints.
Per slug, the dispatcher gathers the persona-doc-author inputs:
| Input | Source |
|---|---|
slug |
the reconciled personas: slug (kebab-case = filename = role:) |
display_name |
inventory persona-column display, or a derived <Title> (<one-clause role>) |
device |
inventory/intent if present, else <!-- TODO: device --> for the agent to infer from journeys |
repo_root |
the consumer repo absolute path |
template_path |
docs/templates/persona.md (seeded into the consumer at Phase 1 templates-scaffold) |
intent_path |
docs/product/intent.md |
journey_paths |
every docs/product/journeys/<domain>.md whose personas: aggregate includes this slug — the agent's richest source |
served_flows |
the flow IDs whose story personas: include this slug (for ## Touchpoints + scope shape) |
partial_state |
any interview note / existing thin persona to enrich / failure-they-can't-absorb hint |
today |
the run's ISO date for last_reviewed |
3. Dispatch pattern — 1 agent per persona; parallel across personas
| Invocation | Wall time |
|---|---|
/flow:add-domain (1 domain) |
the new domain's persona set is usually 1–3 slugs → ~60-90s (parallel) |
/flow:start-project (multi-domain) |
the project-wide reconciled union, often ~3–7 slugs → ceil(K/10) * ~90s; K≤10 → ~90s |
K = number of unique persona slugs across the project (NOT domains — a persona is authored once and cross-linked from every story/journey that serves it, so a 7-persona project is 7 agents regardless of domain count). Parallel across personas within a ~10 concurrency cap; never split one persona across agents (a single agent preserves the doc's internal voice + the P5 no-byte-reuse-across-siblings property).
4. Idempotency — skip-if-exists + --force
Same contract as Q15.3 / Q16.3. Pre-write check per docs/product/personas/<slug>.md:
- Default: skip an existing persona doc (do not clobber a hand-authored or already-reviewed persona) + summarize the skip at end-of-run.
--force: overwrite.- Interactive mode: per-doc
AskUserQuestion.
A persona that already exists and passes is the common case on /flow:add-domain (a new domain often reuses an existing persona); skip-if-exists keeps the new domain's stories cross-linking to the existing doc rather than rewriting it.
5. Failure recovery — log + continue
Same as Q15.5 / Q16.5. A persona whose agent returned the PERSONA-DOC-AUTHOR-ERROR sentinel (or whose write failed) surfaces in the end-of-run summary; the user re-runs the skill with --force (idempotent against the already-written set). One failed persona never aborts the batch.
6. INDEX — author/refresh docs/product/personas/INDEX.md
After the batch, ensure docs/product/personas/INDEX.md exists and carries a row per successfully-written persona (the canonical INDEX schema: | Persona | Device | Status | File |, Status ∈ {Drafted, Reviewed}). A persona whose agent returned the PERSONA-DOC-AUTHOR-ERROR sentinel was not written and gets no row — the INDEX never advertises a doc that isn't on disk.
Status rule (the human-certifies-not-the-agent invariant — this skill never self-certifies to Reviewed):
- A freshly-authored persona lands as
Drafted. It is promoted toReviewedonly afterquality-reviewerpasses it. - A persona rewritten under
--forceis reset toDraftedeven if its prior row readReviewed— the rewrite is unreviewed content, so a staleReviewedwould be a false certification. - A persona skipped (skip-if-exists, default mode) keeps its existing row and status untouched (its on-disk doc was not changed).
7. Fidelity / mechanical layer
bash scripts/verify-docs.sh once after the batch (front-matter presence, link resolution, freshness — same as Q15.4 / Q16.4). The substance review (quality-reviewer doc_kind: persona_doc, P1–P5) and the existence floor (persona-exists) are separate gates, not part of this skill — this skill authors; the gates certify.
8. User-confirmation gates — 0 synchronous gates
Filesystem write; git review is the implicit gate. Contributes 0 to the orchestrator's gate budget. Same rule as Q15.6 / Q16.6.
Ordering constraint (recap)
Serial across the doc layers: flow-linear-scaffold → flow-doc-author → flow-journey-author → flow-persona-author. The persona is authored last among the doc authors because it is the synthesis layer — it reads the stories (its slug source) and the journeys (its behavioral source) and cross-links back to both. The story ## Actor and journey persona-section links to the persona doc are forward references that resolve at the end-of-run link-resolution check (deterministic docs/product/personas/<slug>.md paths).
/flow:retrofit-project is a follow-on (BC-14018 scopes /flow:start-project + /flow:add-domain only); a retrofit run authors personas the same way once wired.
Worked example
/flow:start-project greenfield, 5 domains, 3 unique personas across them:
- Skill walks
docs/product/flows/<domain>/*.mdacross all 5 domains → collects the union of non-emptypersonas:slugs → 3 unique (operator,dispatcher,client), reconciled against the inventory column. - Per slug, gathers inputs:
journey_paths= the journeys whose aggregatepersonas:includes the slug;served_flows= the flows whosepersonas:includes it. - Skip-if-exists: 0 of 3 exist → fans out 3 background
Agent(persona-doc-author)calls in parallel (1 batch under the ~10 cap). - Each agent returns the whole file; skill strips HTML comments and writes
docs/product/personas/{operator,dispatcher,client}.md. ~90s wall. - Authors
docs/product/personas/INDEX.mdwith 3Draftedrows. - Mechanical layer:
bash scripts/verify-docs.sh→ 0 errors (every story## Actorlink + journey persona link now resolves). - End-of-run summary:
flow-persona-author: 3/3 personas authored (Drafted), 0 skipped, 0 failed. Run quality-reviewer doc_kind: persona_doc to promote to Reviewed.
See also
agents/persona-doc-author.md— the per-persona agent contract (inputs, steps, whole-file output) this skill dispatches.templates/docs/templates/persona.md— the canonical persona template (5 FLOOR sections) the agent conforms to; seeded into the consumer at Phase 1.skills/flow-journey-author/SKILL.md— the mirror sub-skill + immediate predecessor (provides the journey docs as the agent's behavioral source).skills/flow-doc-author/SKILL.md— upstream sub-skill (provides the story docs whosepersonas:this skill enumerates).skills/_shared/quality-rubric.md— persona dimensions P1–P5, the substance barquality-reviewerscores against.scripts/lib/flow_persona_lint.py— the persona-exists floor (BC-12573); the set this skill authors is exactly the set this lint checks.docs/decisions/041-fda-personas-behavioral-slugs-only.md—personas:= behavioral persona-doc slugs only (the convention this skill's set-enumeration assumes).