| name | role-identity |
| description | Assemble a canonical role identity (P3 adapter per ADR-017 §2.1 Layer 3) for a dispatch — resolve a role name against the canonical roster (Directive |
Role-identity
When to invoke
Invoke tools/assemble-role.sh to obtain a canonical RoleIdentity for a named role — typically inside a dispatch-construction flow (e.g., authoring a dispatch contract) or for inspection of a role's canonical roster/tier classification.
Per ADR-017 §2.1 Layer 3 + plan §Phase 3 Task 3.4: assemble-role.sh is the P3 adapter bash primitive thin-wrapping python3 -m role_schema.cli assemble-role. The CLI subcommand resolves --role <name> against the canonical roster constants at tools/role_schema/ports/role_identity.py:39-62.
What it does
- Parses
--role <name>; resolves against the canonical roster (Directive #10 + working-vocabulary + tier-overlay-only).
- If the role is canonical, constructs a
RoleIdentity via the _assemble_role_identity helper (sets name, canonical_in, tier; iron_law / halt_criteria / sign_off_shape / dispatch_packet_template / traits default to empty per Task 3.4 DO Q3 disposition — per-role base file authoring is a follow-up scope per options.md §9.5).
- If
--include-traits, populates the traits[] field with all 11 on-disk standing-binding trait names sorted alphabetically (universal-traits read per options.md §9.5; identity-independent per Phase 2 Task 2.3).
- Emits a JSON diagnostic to stdout.
Canonical RoleIdentity output shape (authoritative)
{
"assembled": true,
"role": "<role-name>",
"identity": {
"name": "<role-name>",
"canonical_in": "directive_10",
"tier": "T2",
"iron_law": [],
"halt_criteria": [],
"sign_off_shape": {},
"dispatch_packet_template": {},
"traits": [],
"collision_resolution": null
}
}
Field semantics (mirrors tools/role_schema/ports/role_identity.py:83-117):
assembled: boolean — true if the role is in the canonical roster; false otherwise (halts).
role: echo of the input --role <name> for traceability.
identity.canonical_in: T2 roster membership discriminator — "directive_10" (7 Appendix B §1.3 canonical roles), "working_vocabulary" (4 Annotation 1 + R-Q1a working-vocab roles), "extension" (HC#3a extension-hook roles), or null (tier-overlay-only roles; DO at T0).
identity.tier: governance tier per Appendix B §1.1 — "T0" / "T1" / "T2".
identity.iron_law / halt_criteria / sign_off_shape / dispatch_packet_template: empty by default; per-role base file authoring at tools/role_schema/roles/<name>.py is deferred to a follow-up task per options.md §9.5 design intent (Task 3.4 DO Q3 disposition).
identity.traits: empty by default; populated with all 11 on-disk standing-binding trait names sorted when --include-traits is set (universal-traits read per options.md §9.5; identity-independent per Phase 2 Task 2.3).
identity.collision_resolution: per-port collision-winner map for trait composition (null if no collisions expected).
12 canonical role names
Per role_identity.py:39-62 + Task 3.4 DO Stage 1 Q1 disposition (plan L574 "2 tier-overlay roles" pre-finalization text reconciled to 1 per design comment at role_identity.py:57-59 — Orchestrator is in directive_10 only, not separately in tier_overlay_only):
- DIRECTIVE_10_ROLES (7): Consigliere, Orchestrator, Operator, Experimenter, Validator, Observer, Librarian
- WORKING_VOCABULARY_ROLES (4): Implementer, Architect, Reviewer, Auditor
- TIER_OVERLAY_ONLY_ROLES (1): DO
Contract with primitive
- Primitive:
tools/assemble-role.sh
- Flags / inputs:
--role <name> (required; canonical role name from the 12 enumerated above)
--include-traits (optional; populates traits[] with all 11 on-disk standing-binding trait names sorted)
--json (machine-readable output; default behavior)
--pretty (pretty-printed JSON via indent=2)
-h / --help (usage)
Failure stdout shape
{
"assembled": false,
"role": "<role-name>",
"violations": [
{
"halt_class": "halt-role-not-canonical",
"severity": "block",
"detail": "role '<role-name>' is not in any canonical roster (directive_10 + working_vocabulary + tier_overlay_only); 12 canonical role names available"
}
]
}
- Exit semantics: 0 = assembled (success); 1 = not assembled (role not in any canonical roster); 2 = invocation error.
Example invocations
tools/assemble-role.sh --role Consigliere --pretty
tools/assemble-role.sh --role Architect --include-traits --pretty
tools/assemble-role.sh --role DO --pretty
tools/assemble-role.sh --role NonexistentRole
Halt conditions (tool-local; do NOT propagate to ADR-017 §2.4 canonical 7-class taxonomy)
halt-role-not-canonical (block): --role <name> is not in any canonical roster (directive_10 + working_vocabulary + tier_overlay_only); 12 canonical role names are available.
Future / Open
Per-role base file authoring (per options.md §9.5 design intent; Task 3.4 DO Q3 disposition deferred):
The current _assemble_role_identity helper (cli.py:353-380) emits a MINIMAL RoleIdentity with empty iron_law / halt_criteria / sign_off_shape / dispatch_packet_template fields. options.md §9.5 L1003-1029 designs per-role base files at tools/role_schema/roles/<name>.py (e.g., roles/architect.py exporting ARCHITECT = RoleIdentity(name="Architect", iron_law=[...], halt_criteria=[...], ...)). These files were never authored in Phases 1-2; a follow-up task (filing as bd issue at Task 3.4 closure per DO Q3 disposition) will:
- Author 12 per-role base files (or partial subset)
- Populate substantive Iron-Law clauses + halt-criteria references + sign-off shapes + dispatch-packet templates per AGENTS.md / GSOM-001 / contract §2 codification
- Extend
_assemble_role_identity to compose the role base with the registry rather than emit empty fields
Per-dispatch trait narrowing (per options.md §9.5 L982): traits attach at role-injection per dispatch declaration; the --include-traits flag implements the universal-traits read (all 11 attach by default) — per-dispatch narrowing happens at dispatch-validate time via the contract's explicit traits: field. A --contract <path> flag on assemble-role (returning only contract-declared traits) is a future refinement.
Related
- ADR-017 §2.1 Layer 3 (canonical anchor;
tools/assemble-role.sh explicitly enumerated alongside the 4 other bash primitives).
- ADR-017 §2.2 (canonical packet shape;
role: field).
- ADR-017 §2.5 (Trait composition for cross-cutting role disciplines).
- options.md §9.4 (the 11 standing bindings as traits).
- options.md §9.5 (Trait + role assembly example — Architect dispatch with all 12 traits).
tools/role_schema/ports/role_identity.py (P3 port: RoleIdentity + IronLawClause + roster constants).
tools/role_schema/traits/ (11 standing-binding trait modules; each exposes TRAIT: Trait constant).
tools/role_schema/cli.py:assemble_role (Python implementation; this skill's underlying mechanism; sixth cli.py subcommand surface — completes plan §Task 1.8's enumerated 3 deferred subcommands).
tools/dispatch-validate.sh (sibling primitive — pre-launch validation; uses the same _assemble_role_identity helper for tier-identification at validation time).