skills/architecture-dev-handoff/SKILL.md
Generate Component Development Handoff documents from architecture documentation. Creates per-component, audience-segmented (Dev/QA/Ops) implementation guides in an 8-section condensed format with deliverable assets (OpenAPI specs, DDL scripts, Kubernetes manifests). Invoke when handing off a component for implementation, when a dev team needs implementation specs, or when generating development handoff documents.
npx skillsauth add shadowx4fox/solutions-architect-skills architecture-dev-handoffInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Manually activated when users request component handoff document generation. It is NOT automatically triggered.
Activation Triggers:
/skill architecture-dev-handoff/skill architecture-dev-handoff all/skill architecture-dev-handoff [component-name]NOT Activated For:
architecture-docs skill)architecture-compliance skill)architecture-component-guardian skill)architecture-peer-review skill)As of v3.7.0, this skill is an orchestrator. Per-component generation runs in isolated sub-agent contexts via agents/generators/handoff-generator.md (model: sonnet) and agents/builders/handoff-context-builder.md (model: sonnet, added in v3.13.0). The orchestrator stays in main context; sub-agents do the heavy per-component reads.
--parallelism N, capped at 8), one message per batch. Default raised from 2 in v3.13.0 — sub-agents are read-only on independent payloads, so 4-way fan-out cuts wall-clock by ~50% on 8-component runs without extra rate-limit risk on standard tiers.Why this matters: previous versions re-read 7 shared architecture files plus the full adr/ directory once per component. For a 10-component run that was ~750–2,750 KB of redundant reads. The orchestrator now reads them exactly once and slices per component into payload files that sub-agents consume.
See PAYLOAD_SCHEMA.md for the orchestrator → sub-agent contract.
This skill generates Component Development Handoff documents — one per component — that give development teams everything needed to implement a component without reading the full ARCHITECTURE.md suite.
Scope: C4 Level 2 (Container) components only. C4 Level 1 (System) descriptors are excluded — they describe system boundaries, not implementable units.
Output location: handoffs/ directory at the project root
Per-component output (all names use lowercase kebab-case — no spaces, no uppercase, no underscores):
handoffs/NN-<component-name>-handoff.md — 8-section audience-segmented handoff document (Dev / QA / Ops tracks; TEMPLATE_VERSION 2.0.0)handoffs/assets/NN-<component-name>/ — scaffolded deliverable assets (OpenAPI spec, DDL, Kubernetes manifests, etc.)handoffs/README.md — managed index updated after each generationThe <component-name> slug is derived from the component file name in docs/components/ (e.g., docs/components/03-payment-service.md → handoffs/03-payment-service-handoff.md).
CRITICAL REQUIREMENT: Generated handoff documents MUST preserve template format exactly.
Strict Preservation Rules:
[PLACEHOLDER] tokens — text inside [...] brackets[NOT DOCUMENTED] and add entry to section D1 (Open Questions & Assumptions)These rules apply to both the orchestrator (payload construction) and the sub-agent (template filling).
All handoff documents must trace data back to architecture docs.
docs/components/01-api-gateway.md, docs/07-security-architecture.md)[NOT DOCUMENTED — add to docs/NN-file.md] and record in section D1CRITICAL REQUIREMENT: Every generated handoff document and asset file MUST be an exact representation of what is specified in the architecture documentation. No more, no less.
Rules:
[NOT DOCUMENTED — add to <source-file>] in handoff sections or # TODO: [NOT DOCUMENTED] in asset filesBefore any workflow, resolve the absolute path to the plugin installation so sub-agents can read the handoff template, section-extraction guide, asset-generation guide, and asset index.
Step 0a — Glob (dev/local mode):
Glob for: **/{sa-skills,solutions-architect-skills}/skills/architecture-dev-handoff/SKILL.md
The brace expansion matches both marketplace installs (sa-skills/ in ~/.claude/plugins/cache/...) and local dev clones (historical repo folder solutions-architect-skills/). If found, strip /skills/architecture-dev-handoff/SKILL.md from the result to get plugin_dir.
Step 0b — Marketplace fallback:
If Glob returns nothing, set:
plugin_dir = ~/.claude/plugins/marketplaces/shadowx4fox-solution-architect-marketplace
Step 0c — (removed in v3.13.0)
Earlier versions mirrored four reference files (HANDOFF_TEMPLATE.md, SECTION_EXTRACTION_GUIDE.md, ASSET_GENERATION_GUIDE.md, assets/_index.md) to /tmp/handoff-plugin-refs/ so sub-agents could Read them despite permission scoping. As of v3.13.0 those references are bundled directly into agents/generators/handoff-generator.md (sub-agent system prompt). Sub-agents no longer Read any plugin file at runtime, so the staging fallback is dead overhead and has been removed.
plugin_dir is still resolved in Steps 0a/0b — the orchestrator uses it to invoke bun [plugin_dir]/skills/architecture-dev-handoff/utils/{prepare-payload-dir,manifest}.ts. If a bun helper invocation fails with a permission error, check that Bash(bun *) is granted in project settings.
Step 1.0: Parse orchestrator flags
Inspect the user's invocation for flags. Default values apply when a flag is absent.
| Flag | Default | Validation | Purpose |
|------|---------|------------|---------|
| --parallelism N | 4 | integer 1–8; on out-of-range value, warn (⚠ --parallelism N out of range; clamping to <1\|8>) and clamp | Stage 5A batch size (handoff-document fan-out) |
| --asset-parallelism N | 4 | integer 1–8; on out-of-range value, warn (⚠ --asset-parallelism N out of range; clamping to <1\|8>) and clamp | Stage 5B batch size (handoff-asset-generator fan-out, v3.14.7+) |
| --force | false | boolean (presence) | Bypass handoffs/.manifest.json skip-if-unchanged (see Phase 4.5 / Item 7) |
Store the resolved values as parallelism, asset_parallelism, and force for use throughout the workflow. If no flags are present, behaviour matches today's defaults (parallelism = asset_parallelism = 4).
Why two parallelism flags? Stage 5A spawns one Sonnet handoff-generator per component (heavier — fills the full 8-section audience-segmented document). Stage 5B spawns one asset agent per (component, asset_type) tuple — typically lighter, and a mix of Sonnet (code-style assets) and Haiku (descriptor-style asset). The two stages have different cost profiles, so they expose independent dials. Most users leave both at the default 4.
Step 1.1: Locate ARCHITECTURE.md
Search order:
/docs subdirectoryExtract the project name (first H1), architect (metadata), and <!-- ARCHITECTURE_VERSION: X.Y.Z --> comment value.
Step 1.2: Validate Prerequisites
Check for:
ARCHITECTURE.md exists (navigation index)docs/components/ directory exists with at least one NN-*.md file (may be in system subfolders for multi-system architectures)docs/components/README.md exists (if not, suggest running architecture-component-guardian sync first)docs/components/<system-name>/NN-*.md with grouped tables in README.mdStep 1.3: Load Component Index
Read docs/components/README.md for the component table (5-column: #, Component, File, Type, Technology). If the table has system group headers (### System Name), parse all groups. If README absent, scan docs/components/*.md and docs/components/**/*.md.
Filter out non-L2 files using BOTH checks (either match excludes the file):
NN- prefix that match a subfolder name (e.g., payment-system.md) — these are C4 L1 system descriptors.**C4 Level:**. If the value is anything other than Container (L2), exclude the file.Only files that pass both checks enter the component list. Present the component list (grouped by system if multi-system).
Step 2.1: Ask user which components
Show the component index and ask:
"Which component(s) would you like to generate a handoff for? (Enter a number, component name, comma-separated list, or 'all')"
Exception: if the user explicitly named specific component(s) in their original request (e.g., "generate handoff for payment-api"), skip the prompt and use those.
Selection modes:
all → process every component in the index3) → process that component by index position1, 3 or payment-api, auth-service)C4 Level Validation Gate (applied to every selected component before proceeding):
For each selected component file, read its **C4 Level:** metadata field:
Container (L2) → proceedSystem (L1) or any non-L2 value → reject: "⚠ Skipping [Component Name] — C4 Level is [value], not Container (L2). Dev handoff documents are only generated for C4 L2 (Container) components."**C4 Level:** Container (L2) to [file path]."If all selected components are rejected, report and stop.
Step 2.2: Order selected components
Sort by component_index_position ascending. (Earlier versions sorted by dependency count to provide upstream context for downstream handoffs, but sub-agents never read other handoff outputs — they read only their assigned payload + bundled references — so dependency ordering provides no information benefit and added N component-file Reads per orchestration. Sub-agents can fan out in any order.)
handoff-context-builder sub-agentThe core change in v3.13.0. All shared-doc Reads, ADR indexing, per-component slicing, payload writing, dedup, and manifest skip-checks happen inside a single Sonnet sub-agent. The orchestrator's main context (running on the user's session model — typically Opus) never loads the ~80–120 KB of shared docs + ADR corpus.
Step 3.1: Detect architecture-doc language (kept on orchestrator — uses ARCHITECTURE.md already loaded in Phase 1)
Set once per run — propagated to the context-builder, which writes it into every payload's doc_language frontmatter field. Drives the template-variant selection for the c4-descriptor asset (English vs Spanish).
Detection order (stop at the first match):
ARCHITECTURE.md for <!-- LANGUAGE: es --> or <!-- LANGUAGE: en -->. If found, use that value.ARCHITECTURE.md and docs/01-system-overview.md for Spanish section headers that commonly appear in the canonical layout (## Contexto, ## Descripción, ## Visión general, ## Componentes, ## Decisiones arquitectónicas). If two or more distinct Spanish headings appear, set es. Otherwise set en.en.(Spanish-heading detection requires a one-time Read of docs/01-system-overview.md on the orchestrator. Acceptable cost — one shared file, used only for the language marker check.)
Step 3.2: Pre-cache context7 spec docs (optional, orchestrator-side)
If the context7 MCP tool is available and one or more selected components produce asset types, call resolve-library-id once per unique asset spec (OpenAPI, AsyncAPI, Kubernetes, the database engine for DDL, Avro, Protobuf, Redis). Cache the resolved library IDs and fetched docs for the duration of this orchestration run. The orchestrator passes the cache path hint to handoff-generator sub-agents in Phase 5 — context-builder does not use context7.
Skip silently if context7 is unavailable.
Step 3.3: Capture generation_date and prepare .cache/sa-skills/handoff-payloads/
generation_date=$(bun [plugin_dir]/skills/architecture-dev-handoff/utils/prepare-payload-dir.ts .cache/sa-skills/handoff-payloads)
The helper creates the payload directory (recursive mkdirSync — cross-platform, no shell mkdir -p dependency) and prints YYYY-MM-DD. Capture both for the context-builder spawn prompt. The path is project-relative and gitignored via setup-gitignore.ts, so it works identically on Linux, macOS, and Windows (native + WSL + Git Bash) without any /tmp permission grant.
Step 3.4: Compute per-component metadata for the spawn prompt
For each selected component, gather the following from its file header (15–30 line Read on the orchestrator — small, and necessary anyway for the C4 Level Validation Gate already done in Step 2.1):
slug (from filename)component_file (repo-relative path)type (from **Type:**)technology (list, from **Technology:**)component_index_position (NN- prefix)Pack these into a YAML list to embed in the spawn prompt body. The context-builder does NOT re-read these files for metadata extraction — it consumes the orchestrator's pre-extracted list directly. Step 3.4 keeps the orchestrator's pre-3 work consistent with what it already does for component selection.
Step 3.4a: Per-component explorer findings fan-out (v3.16.0+)
Spawn sa-skills:architecture-explorer in findings mode once per selected component, in parallel batches of parallelism (the Step 1.0 value, default 4, capped at 8). Each invocation passes both component_file (for the focus_component.related_adrs allowlist) AND query (for line-level evidence across the canonical doc surface). The agent emits an EXPLORE_FINDINGS block carrying both — the per-component ADR allowlist AND the per-component shared-doc match map for Step 3.5.
For each component, send one Task() with this prompt body:
Task(sa-skills:architecture-explorer)
prompt:
project_root: <project_root>
component_file: <abs path to component.component_file>
query: <component.slug>, <component.type>, <component.technology joined as ", ">
caller: orchestrator-dev-handoff
That's the complete prompt — no task_type, no config_path, no agent_version, no extra_terms, no force. The caller field is a contract anchor (see the explorer's Input Parameters): it does not change the explorer's output, but it gives the haiku a structured signal that a string-scanning orchestrator is reading the result, reducing the chance of prose drift. The explorer (a) reads the component's <!-- EXPLORER_HEADER --> block and copies related_adrs: into focus_component.related_adrs, and (b) runs parallel Grep + targeted Read across the canonical doc surface (ARCHITECTURE.md, docs/**/*.md, adr/**/*.md) for the query terms, emitting line-level matches with headings and 3–5-line excerpts.
Collect each findings block into a per-component map keyed by slug:
findings_by_component:
<slug-1>:
findings: | # full EXPLORE_FINDINGS YAML body
schema_version: 2
status: OK
query: "<slug>, <type>, <tech>"
total_files_matched: <N>
total_matches: <M>
files: [...] # line-level matches per file
focus_component:
file: docs/components/<system>/NN-<slug>.md
related_adrs: [ADR-018, ADR-031]
has_header: true
explorer_status: <OK|FAILED>
explorer_mode: <header | unheaded | failed>
<slug-2>:
...
Deriving explorer_mode:
header when the component's focus_component.has_header == true and related_adrs is populated (the canonical fast path).unheaded when has_header == false (legacy doc; downstream falls back to title-match against adr_index[*].terms).failed when the explorer returned status: FAILED.Track the mode counts for the Phase 7 report. Also track total_files_matched and total_matches summed across components for the new findings-evidence telemetry line.
Malformed-output detection + retry-once (v3.14.8+): for each explorer reply, check whether the literal token EXPLORE_FINDINGS: appears at the start of any line. If absent (the agent returned synthesis prose), spawn one retry of the same component's explorer call with the same component_file + query, prepending this corrective line to the prompt:
Your previous reply did not contain a literal `EXPLORE_FINDINGS:` line followed by a fenced YAML block. Re-run findings mode and obey the Output contract section: first character of your reply must be `E`; exactly one ```yaml ... ``` fence; no prose outside the fence except the optional "no matches" note.
The retry is a fresh Task() invocation (no edit-in-flight), capped at 1 per component. If the retry still lacks EXPLORE_FINDINGS:, treat that component's call as FAILED for the purposes of explorer_mode derivation and the rules below.
If any explorer call returns FAILED, has empty files[], or fails the malformed-output check above (including after the single retry), log a warning. The context-builder will run in v3.13.0 legacy in-builder grep slicing for those slugs.
If every explorer call returns FAILED, omit findings_by_component from the context-builder spawn prompt entirely — the builder falls back to the v3.13.0 legacy path corpus-wide.
Step 3.5: Spawn sa-skills:handoff-context-builder (single Task call, NOT batched)
Send ONE message containing one Task() invocation:
Task(sa-skills:handoff-context-builder)
prompt:
architecture_md_path: <abs path>
project_name: <from H1>
architect: <from metadata, or "Not specified">
architecture_version: <from <!-- ARCHITECTURE_VERSION: ... -->, or "unversioned">
doc_language: <en|es from Step 3.1>
generation_date: <YYYY-MM-DD from Step 3.3>
payload_dir: .cache/sa-skills/handoff-payloads
manifest_path: <project>/handoffs/.manifest.json
template_version: <output of `bun [plugin_dir]/skills/architecture-dev-handoff/utils/manifest-cli.ts template-version [plugin_dir]/skills/architecture-dev-handoff/HANDOFF_TEMPLATE.md`>
schema_version: "2"
force: <true|false from Step 1.0>
plugin_dir: <abs path>
handoffs_dir_abs: <project>/handoffs
components:
- slug: …
component_file: …
type: …
technology: [...]
component_index_position: "NN"
- slug: …
…
findings_by_component: # v3.16.0+ — from Step 3.4a
<slug-1>:
findings: | # full EXPLORE_FINDINGS YAML body
schema_version: 2
status: OK
query: "<slug>, <type>, <tech>"
total_files_matched: <N>
total_matches: <M>
files: [...]
focus_component:
file: docs/components/<system>/NN-<slug>.md
related_adrs: [...]
has_header: <true|false>
explorer_status: <OK|FAILED>
<slug-2>:
...
(Omit the findings_by_component key entirely if Step 3.4a reported every component's explorer call FAILED — the builder falls back to the v3.13.0 legacy path that does its own ADR indexing and in-builder grep slicing. Per-component fallback applies to individual FAILED slugs while keeping the findings-driven path for the rest.)
Wait for the CONTEXT_RESULT: block. The context-builder returns:
status — OK, PARTIAL, or FAILEDpayload_dir, shared_refs_path (optional)shared_docs_read, adrs_indexed, adrs_full_read (for the Phase 7 report)parse_fallback_slugs (components that fell back to ## Component File (raw))components[] — per-component { slug, payload_path, payload_hash, decision (SKIP|REGEN|FAILED), reason, asset_types, doc_language, handoff_file_rel }Failure handling:
status: FAILED → abort the orchestration with the context-builder's reason.status: PARTIAL → drop FAILED components, continue with the rest, surface them in Phase 7.status: OK → all components have a SKIP or REGEN decision.Store the components[] list as context_result for use in Phase 4 and Phase 6.
Backward compat: first run after upgrading from <v3.13.0 has no manifest → context-builder returns decision: REGEN for every component → manifest is created in Step 6.0. No surprises.
Recommended git policy: commit handoffs/.manifest.json alongside the handoff files. Teammates pulling the branch then benefit from skip-if-unchanged on their own machines. The file is small (one JSON object per component) and changes deterministically with the handoffs. If a project explicitly does not want to track it, add handoffs/.manifest.json to .gitignore; the skill works either way (just regenerates everything on the first machine that doesn't have it locally).
After receiving context_result from Phase 3.5, scan every component whose decision == SKIP for an existing handoff file written under TEMPLATE_VERSION 1.0.0 (the pre-v2 16-section format). When found, flip the decision to REGEN so the v1 file is rewritten under v2 on this run. This is silent and unconditional — auto-regen, not opt-in.
For each component in context_result.components with decision == SKIP:
<project>/<handoff_file_rel> (already provided by context-builder).<!-- TEMPLATE_VERSION: 1.0.0 -->.decision from SKIP to REGEN and append a reason: v1 template upgrade — auto-regen to TEMPLATE_VERSION 2.0.0.<!-- TEMPLATE_VERSION: 2.0.0 --> (or any other v2.x), keep the SKIP decision.--force to regenerate everything.After the scan, if any component was flipped, emit ONE summary line at the top of Stage 5 (do not prompt or pause):
Detected N v1 handoff(s) → forcing regen to TEMPLATE_VERSION 2.0.0
If N == 0, print nothing.
This phase adds at most one Read per SKIP component (~milliseconds). REGEN'd components have their orphan-asset handling and manifest update flow through Phases 5–6 normally — no special-casing past this point.
Filter context_result.components to entries where decision == REGEN. For each one, pre-create its asset output directory so the downstream handoff-generator sub-agent never needs Bash. Uses the generic ensure-dir.ts helper (no date side effect — prepare-payload-dir.ts is reserved for the Step 3.3 dir + date capture):
bun [plugin_dir]/scripts/ensure-dir.ts <project>/handoffs/assets/NN-<slug>
(SKIP'd components keep their existing asset directories untouched. FAILED components are excluded.)
As of v3.14.8, Stages 5A and 5B run as a single parallel cohort rather than sequentially. The handoff-generator (5A) and handoff-asset-generator (5B) Tasks both consume only payload_path (already on disk after Phase 3.5) — there is no cross-dependency between a component's handoff document and its asset files. Spawning them concurrently reclaims ~min(T(5A), T(5B)) of wall-time per run; on the observed single-component run that was ~200 s, scaling to 25–35 % off Phase 5 wall-clock on multi-component runs. Stage 5C still runs after both cohorts complete, editing each component's handoff document in-place to append asset-level gaps to section D1.
Concurrency budget (changed in v3.14.8): At any instant, the total number of in-flight sub-agents is parallelism + asset_parallelism (default 4 + 4 = 8), not max(parallelism, asset_parallelism). Cap each individually at 8 (so the absolute ceiling is 16). If your project rate-limits aggressively, lower --parallelism and/or --asset-parallelism accordingly.
Step 5.0: Build both queues from REGEN components
Take context_result.components (from Phase 3.5) and keep only entries with decision == REGEN. SKIP'd components are not spawned — their existing handoff files, asset files, and manifest entries remain valid.
From the REGEN list, build BOTH queues up-front:
asset_types[] into one tuple per asset (input for Stage 5B handoff-asset-generator Tasks). Use the static filename map and model-tier classification table below. Important — eager asset spawning: the asset queue is built from the REGEN list directly, NOT gated on Stage 5A success. Pre-v3.14.8 behavior was "drop from asset queue when 5A fails", but the queues now spawn together so this gating is no longer possible. See "Failure handling" below.If a component's asset_types[] is empty (skip-list type), no asset tuples are emitted for it. The component still appears in the Stage 6 manifest update (with assets: "") and the Phase 7 report.
Asset filename map (also embedded in handoff-asset-generator and in assets/_index.md):
| asset_type | Filename |
|------------|----------|
| openapi | openapi.yaml |
| ddl | ddl.sql |
| deployment | deployment.yaml |
| asyncapi | asyncapi.yaml |
| cronjob | cronjob.yaml |
| avro | schema.avsc |
| protobuf | schema.proto |
| redis | redis-key-schema.md |
| c4-descriptor | c4-descriptor.md |
Asset → model tier classification (frozen — change only by an explicit ADR):
| asset_type | Tier | Model | Rationale |
|------------|------|-------|-----------|
| openapi, ddl, deployment, asyncapi, cronjob, avro, protobuf, redis | code | sonnet | Strict syntactic schemas (YAML, SQL, Avro, Protobuf, structured Redis key/TTL/eviction tables) — Sonnet's structural precision matters |
| c4-descriptor | descriptor | haiku | Free-form markdown one-pager (EN/ES variant); template-fill task well within Haiku's capability |
Order the asset queue by (component_index_position, asset_type) for determinism. Order the handoff queue by component_index_position for determinism.
Send ONE message containing up to parallelism handoff-generator Tasks plus up to asset_parallelism handoff-asset-generator Tasks, all in parallel. Per-Task model pin still applies to the asset Tasks.
Handoff Task (one per REGEN component, up to parallelism per cohort message):
Task(subagent_type="sa-skills:handoff-generator", prompt="...")
Prompt body provides (from the matching context_result.components[] entry):
payload_path: absolute path to the component's payload fileoutput_handoff_path: absolute path <project>/<handoff_file_rel>component_slug, component_index_positionasset_types: copy of the payload's asset_types[] array — handoff-generator uses this to deterministically populate section C3 from the static filename mapcontext7_cache_hint (if context7 was used in Step 3.2): a path or inline summary — otherwise omit(output_assets_dir is not passed — handoff-generator does not write asset files. plugin_dir is not required — sub-agents bundle their references and do not invoke Bash.)
Asset Task (one per asset tuple, up to asset_parallelism per cohort message). Set the model: parameter on each Task call to the tuple's model_tier — this overrides the agent's frontmatter (which is intentionally unset). Pattern modeled on architecture-compliance/SKILL.md:863, 890:
Task(subagent_type="sa-skills:handoff-asset-generator", model="<sonnet|haiku>", prompt="...")
Prompt body provides:
payload_pathasset_typeoutput_asset_path (computed from the static filename map)component_slugdoc_language (relevant only for c4-descriptor; pass through for all asset types so the agent does not need to re-derive it)context7_cache_hint (if context7 was used in Step 3.2 — otherwise omit)Tail-batching: when both queues have remaining work after the first message, send subsequent messages each containing up to parallelism handoff Tasks + up to asset_parallelism asset Tasks. The cohort size of the tail batch may be smaller. Wait for each cohort message to complete before sending the next — this keeps in-flight sub-agents bounded by parallelism + asset_parallelism.
Each handoff-generator returns a HANDOFF_RESULT: block with status, handoff file path, [NOT DOCUMENTED] count, and validation notes. Each handoff-asset-generator returns an ASSET_RESULT: block:
ASSET_RESULT:
component_slug: <slug>
asset_type: <token>
status: OK | VALIDATION_FAILED | PAYLOAD_FAILED
asset_path: <abs path>
gaps:
- field: "..."
recommended_location: "..."
impact: "..."
validation_notes: "..."
Aggregate handoff results across all components, and index asset results by component_slug for Stage 5C. Track per-tier counts (sonnet_count, haiku_count) for Phase 7. Do NOT abort on any single failure — continue collecting and report at the end.
Failure handling — orphaned assets when 5A fails (v3.14.8+): Because asset Tasks are spawned eagerly from the REGEN list (not gated on 5A success), a component whose handoff-generator returns PAYLOAD_FAILED or VALIDATION_FAILED may still have asset files written to disk by its asset Tasks. Treatment:
rm in the orchestrator — explicitly avoided post-v3.13.0 because file removal is hard to gate by permission.5A.status == OK — orphaned assets do NOT enter the manifest's assets field, so they get overwritten on the next successful run.5A.status != OK and at least one 5B.status == OK asset, emit a separate line: Asset written but handoff doc failed: <asset_path> — will be regenerated next run.This trade is intentional: the wasted token cost (one component's asset cohort) is bounded and re-runs naturally clean up on the next successful pass; the wall-time savings (~200 s/component) accrue on every run regardless of failure rate. Track the 5A failure rate in Phase 7 — if it sustains above ~5 %, revisit (e.g. add a cheap pre-flight payload sanity check that gates 5B spawn).
Pre-v3.14.8, Stage 5B ran after Stage 5A completed for all components, with the asset queue built from 5A.status == OK only. The current v3.14.8 sequencing collapses 5A.* and 5B.* into the parallel cohort above. The model-tier classification, filename map, and per-asset prompt body are unchanged — only the spawn ordering changed.
Step 5B.1: Build the flat asset queue
For every component where Stage 5A returned status: OK, expand its asset_types[] into one tuple per asset:
- component_slug: <slug>
component_index_position: <NN>
asset_type: <token>
payload_path: <abs path to payload>
output_asset_path: <project>/handoffs/assets/NN-<slug>/<filename>
doc_language: <en|es>
model_tier: <sonnet | haiku>
output_asset_path is computed from the static filename map below. model_tier is computed from the static asset → tier classification table — no runtime lookup needed.
Asset filename map (also embedded in handoff-asset-generator and in assets/_index.md):
| asset_type | Filename |
|------------|----------|
| openapi | openapi.yaml |
| ddl | ddl.sql |
| deployment | deployment.yaml |
| asyncapi | asyncapi.yaml |
| cronjob | cronjob.yaml |
| avro | schema.avsc |
| protobuf | schema.proto |
| redis | redis-key-schema.md |
| c4-descriptor | c4-descriptor.md |
Asset → model tier classification (frozen — change only by an explicit ADR):
| asset_type | Tier | Model | Rationale |
|------------|------|-------|-----------|
| openapi, ddl, deployment, asyncapi, cronjob, avro, protobuf, redis | code | sonnet | Strict syntactic schemas (YAML, SQL, Avro, Protobuf, structured Redis key/TTL/eviction tables) — Sonnet's structural precision matters |
| c4-descriptor | descriptor | haiku | Free-form markdown one-pager (EN/ES variant); template-fill task well within Haiku's capability |
If asset_types[] is empty for a component (skip-list type), no tuples are emitted for it. The component still appears in the Stage 6 manifest update (with assets: "") and the Phase 7 report.
Step 5B.2: Partition the asset queue into batches of asset_parallelism
Use the asset_parallelism value resolved in Step 1.0 (default 4, capped at 8). Order the queue by (component_index_position, asset_type) for determinism, then partition. The tail batch may be smaller than asset_parallelism.
Step 5B.3: Spawn one asset batch per message
For each batch, send ONE message containing up to asset_parallelism Task() tool-use blocks in parallel. Each Task invokes sa-skills:handoff-asset-generator. Set the model: parameter on each Task call to the tuple's model_tier — this overrides the agent's frontmatter (which is intentionally unset). Pattern modeled on architecture-compliance/SKILL.md:863, 890:
Task(subagent_type="sa-skills:handoff-asset-generator", model="<sonnet|haiku>", prompt="...")
The prompt body provides:
payload_pathasset_typeoutput_asset_pathcomponent_slugdoc_language (relevant only for c4-descriptor; pass through for all asset types so the agent does not need to re-derive it)context7_cache_hint (if context7 was used in Step 3.2 — otherwise omit)Step 5B.4: Collect ASSET_RESULT blocks
Each agent returns an ASSET_RESULT: block:
ASSET_RESULT:
component_slug: <slug>
asset_type: <token>
status: OK | VALIDATION_FAILED | PAYLOAD_FAILED
asset_path: <abs path>
gaps:
- field: "..."
recommended_location: "..."
impact: "..."
validation_notes: "..."
Index results by component_slug for Stage 5C. If a Task returns VALIDATION_FAILED or PAYLOAD_FAILED, log it; do NOT abort — the component's handoff document is already written and other assets continue. Failures are surfaced in Phase 7. Track per-tier counts (sonnet_count, haiku_count) for the Phase 7 report.
Step 5B.5: Run asset batches sequentially
Wait for each batch to complete before spawning the next. This keeps total parallel asset-agent count ≤ asset_parallelism at any instant.
For each component slug where Step 5.2 produced at least one non-empty gaps[], perform ONE Edit on the component's handoff document to append asset-level gaps to section D1 (Open Questions and Assumptions). Components with zero asset gaps are skipped (no Edit). Components whose handoff-generator (5A) returned PAYLOAD_FAILED or VALIDATION_FAILED are skipped here too — there is no handoff doc to edit; their orphaned assets are surfaced separately in Phase 7.
Step 5.3.1: Build the merged gap block per component
Group all gaps[] from ASSET_RESULT by component_slug. For each component, render one block of lines using this format (matching the existing section D1 idiom):
- **Asset — <asset_filename>**: <field>
→ Recommended location: <recommended_location>
→ Impact: <impact>
Concatenate one entry per gap, ordered first by asset_type (alphabetical), then by gap order within that asset.
Step 5.3.2: Edit each handoff document once
For each component_slug with a non-empty gap block, do ONE Edit call on <project>/<handoff_file_rel>. Use the existing section D1 anchor in the handoff document:
[OPEN_QUESTIONS_LIST or "No gaps detected — all sections fully documented."]
This line appears verbatim only when handoff-generator emitted no A1–C2 gaps. Replace it with the rendered gap block.**Format for each gap:** and inserting the new block above it.If neither anchor is found (the handoff document failed validation), skip the Edit and surface the slug in Phase 7 with note asset-gap merge skipped: section D1 anchor not found.
Step 5.3.3: Confirm Edit success
The Edit tool returns success/failure. On failure, log the slug + failure reason; do not retry.
This step adds at most one Edit per component — typically 0 to N components depending on how many have asset-level gaps. There is no parallelism budget for Step 5.3; the orchestrator runs Edits sequentially in slug order.
Step 6.0: Update handoffs/.manifest.json
For each component that the orchestrator REGEN'd AND whose handoff-generator sub-agent returned status: OK, insert or replace its manifest entry using the payload_hash already returned by context-builder in Phase 3.5 (no need to re-hash on the orchestrator). Skipped components keep their existing entries untouched.
The <asset1>,<asset2>,... argument is the comma-separated list of asset filenames the orchestrator built deterministically from the payload's asset_types[] (filename map in Step 5.0). Filter the list to assets whose ASSET_RESULT.status == OK from Step 5.2 — failed asset Tasks do not appear in the manifest's assets field, which forces them to be retried on the next run.
bun [plugin_dir]/skills/architecture-dev-handoff/utils/manifest-cli.ts \
update <project>/handoffs/.manifest.json \
<slug> "<payload_hash from context_result>" <template_version> <schema_version> \
<architecture_version> <handoff_file_rel from context_result> \
<doc_language from context_result> <generator_version> "<asset1>,<asset2>,..."
<template_version> is the same value passed into the context-builder in Step 3.5 — sourced from the <!-- TEMPLATE_VERSION: --> marker inside HANDOFF_TEMPLATE.md via manifest-cli.ts template-version. Cache it once at the top of Phase 6 rather than re-running the CLI per component. Bumping the plugin version no longer invalidates manifest entries when the handoff template is byte-identical (v3.14.8+); only a TEMPLATE_VERSION bump or template content change does.
The CLI writes the manifest atomically (.tmp then rename). One invocation per regenerated component; skipped components are left as-is so their generated timestamp reflects when they were last actually produced.
Components whose handoff-generator (5A) sub-agent run returned status: VALIDATION_FAILED or status: PAYLOAD_FAILED are NOT written to the manifest — that ensures the next run treats them as "needs regen" until the underlying problem is fixed. Same applies to components the context-builder reported as decision: FAILED in Phase 3.5. Asset-level (5B) failures alone do not block the manifest update — only the failing asset is dropped from the assets field, while the handoff document and successful assets are still recorded. Orphaned assets (5B succeeded but 5A failed for the same component, possible since v3.14.8's eager-spawn cohort) are also excluded from the manifest, which guarantees they get overwritten on the next successful run.
Step 6.1: Create/Update handoffs/README.md
Write or update the managed index file. Format:
Line 1: <!-- managed by sa-skills:architecture-dev-handoff — do not edit manually -->
Line 2: [Architecture](../ARCHITECTURE.md) > Development Handoffs
Line 3: (blank)
Line 4: # Component Development Handoffs
Line 5: (blank)
Line 6: <intro paragraph>
Line 7: (blank)
Line 8: ## <System Name> — Development Handoffs
Line 9: (blank)
Line 10: | # | Component | Handoff File | Status | Assets | Generated |
Line 11: |---|-----------|-------------|--------|--------|-----------|
Line 12: | 5.1 | ... | ... | ... | ... | ... |
...
(blank)
## Related Documentation
(blank)
- link list
Table schema — exactly 6 columns:
| Column | Source |
|--------|--------|
| # | Filename prefix NN- → formatted as 5.N |
| Component | First # Heading in the component file |
| Handoff File | [NN-name-handoff.md](NN-name-handoff.md) relative link |
| Status | Ready (fresh) or Outdated (component file newer than handoff) |
| Assets | Comma-separated list of generated asset filenames, or — if none |
| Generated | ISO date (YYYY-MM-DD) |
Step 6.2: Update ARCHITECTURE.md Navigation
If ARCHITECTURE.md does not already include a link to handoffs/README.md, add one under the Component Details section or at the end of the navigation table.
Print the aggregated report:
Handoff generation complete
Generated: [N] component handoff(s) ([K] failed, [S] skipped)
Location: handoffs/
Components:
✓ [Component Name] — NN-name-handoff.md
Assets: openapi.yaml, ddl.sql
Gaps recorded: [M] open questions in D1
✓ [Component Name] — NN-name-handoff.md
Assets: deployment.yaml
Gaps recorded: 0
⏭ [Component Name] — skipped (unchanged since last generation)
⏭ [Component Name] — skipped (unchanged since last generation)
✗ [Component Name] — VALIDATION_FAILED: [notes]
Main-context reads (shared docs): 1 (ARCHITECTURE.md only — context-builder owns docs/04–09 + adr/)
Context-builder (sonnet) reads:
shared docs: [shared_docs_read from CONTEXT_RESULT]
ADRs indexed: [adrs_indexed]
ADRs full-Read: [adrs_full_read]
Explorer mode (per component): header: [Ch] | unheaded: [Cu] | failed: [Cf] (v3.16.0+ — `header` is the canonical fast path: `findings.focus_component.has_header == true`; `unheaded` falls back to title-match against `adr_index[*].terms`; `failed` invokes the legacy v3.13.0 in-builder grep slicing path for that component)
Explorer findings (per component): [TFM] matched files / [TM] total matches across [N] component(s) (v3.16.0+ — sum of `findings.total_files_matched` and `findings.total_matches` across all OK components; surfaces how much line-level evidence the context-builder starts from instead of grepping the shared docs itself)
Phase 5 cohort messages: [B_C] cohort(s) — handoff Tasks: {parallelism}-parallel sonnet | asset Tasks: {asset_parallelism}-parallel (sonnet: [X], haiku: [Y])
Step 5.3 gap-merge edits: [E] component(s) had asset-level gaps merged into D1
5A failure rate: [Kf] / [N_regen] handoff doc(s) failed ([P]%) — revisit gating if sustained > 5%
Orphaned assets: [O] asset file(s) written for components whose handoff doc failed (will be regenerated next run)
Skipped (unchanged): [S] component(s) — pass --force to regenerate
💡 Generated N handoffs. Run `/compact` to trim main context now if continuing.
If [S] > 0, list skipped components by slug on a separate line (e.g., Skipped: inbox-ux, inbox-realtime, notification-preferences) so the user can decide whether to --force a specific re-run.
If [O] > 0, list each orphaned asset file on its own line as: Asset written but handoff doc failed: <abs_asset_path> — will be regenerated next run. These appear when a component's handoff-generator (5A) returned PAYLOAD_FAILED|VALIDATION_FAILED while one or more of its asset Tasks (5B) succeeded — a side-effect of v3.14.8's eager-spawn parallelization. The next clean run produces both the handoff doc and overwrites the orphans.
[PLACEHOLDER] tokens (consumed by the sub-agent)ASSET_GENERATION_GUIDE.mdAdd to project .claude/settings.json:
"Write(handoffs/*)",
"Read(handoffs/*)",
"Write(handoffs/.manifest.json)",
"Read(handoffs/.manifest.json)",
"Write(.cache/sa-skills/handoff-payloads/**)",
"Read(.cache/sa-skills/handoff-payloads/**)",
"Read(~/.claude/plugins/marketplaces/shadowx4fox-solution-architect-marketplace/**)",
"Read(~/.claude/plugins/cache/shadowx4fox-solution-architect-marketplace/**)",
"Bash(bun *)",
"Agent(sa-skills:handoff-generator)",
"Agent(sa-skills:handoff-asset-generator)",
"Agent(sa-skills:handoff-context-builder)"
Directory creation (.cache/sa-skills/handoff-payloads/ and handoffs/assets/NN-<slug>/) and the per-run generation_date are produced by utils/prepare-payload-dir.ts, so the only bash grant required is the project-wide Bash(bun *). The helper uses mkdirSync(..., { recursive: true }) — POSIX-free, identical behavior on Windows native, WSL, and Git Bash. Earlier versions used Bash(mkdir *) plus a chained && date +%Y-%m-%d, which triggered a permission prompt on every run because the chained form was not pre-approved.
Why two plugin read grants? plugin_dir may resolve to either the marketplaces manifest path or the versioned cache install path depending on how the plugin was installed. Step 0 probes readability under both. (Sub-agents no longer Read plugin files at runtime as of v3.13.0 — references are bundled into agents/generators/handoff-generator.md and agents/builders/handoff-context-builder.md — but the orchestrator still needs to invoke bun [plugin_dir]/skills/architecture-dev-handoff/utils/*.ts helpers, which require the bun binary to load files from plugin_dir.)
development
Run risk and design-characteristics analyses over ARCHITECTURE.md documentation. Produces date-stamped reports in analysis/ covering ten lenses across two groups: HIGH-priority runtime/security — SPOF (single points of failure), Blast Radius (downstream cascade impact), Bottleneck (throughput chokepoints), Cost Hotspots (Pareto cost concentration), STRIDE (per-trust-boundary security threats); Strategic/sustainability — Vendor Lock-in (portability risk and exit cost), Latency Budget (per-hop SLO decomposition), Tech Debt/EOL (currency and deprecated tech), Coupling (fan-in/fan-out and cycles), Data Sensitivity (PII flow and encryption gaps). Each analysis can be requested individually, as a group, or all ten run in parallel. A consolidated Security Posture option (analysis 12) merges the STRIDE and Data Sensitivity reports into a single reviewer-fillable validation checklist of every security control to validate (markdown-only; exportable to a Word worksheet via architecture-docs-export). Output: analysis/<TYPE>-<YYYY-MM-DD>.md (default) OR analysis/<TYPE>-<YYYY-MM-DD>.html (interactive d3.js report; format is selected at runtime — Step 2.4). Requires ARCHITECTURE.md to exist (created by architecture-docs skill). Do NOT invoke for compliance contracts (use architecture-compliance), peer quality review (use architecture-peer-review), or ADR management (use architecture-definition-record).
development
On-demand export of architecture documents to professional Word (.docx) files. Exports are never automatic — invoke explicitly when ready to produce deliverables. Solution Architecture mode synthesizes an Executive Summary from docs/01-system-overview.md, the component index, and the compliance manifest (if present), then exports individual ADR docs. Handoff mode exports selected component development handoffs from handoffs/. Compliance mode exports selected compliance contracts from compliance-docs/. Security Posture mode exports the consolidated security validation checklist (analysis/SECURITY-POSTURE-<date>.md, from architecture-analysis) as a reviewer-fillable worksheet. IMPORTANT — this skill ONLY produces Word .docx files. It does NOT handle releasing, publishing, tagging, freezing, bumping, or finalizing an architecture version. For the Draft → Released lifecycle (git tag architecture-v{version}, archive snapshot, semver bump), use the `architecture-docs` skill (Workflow 10) instead. Do NOT invoke this skill when the user says "release my architecture", "release architecture", "publish architecture", "ship architecture", "tag architecture version", "freeze architecture", "bump architecture version", or "finalize architecture" — those route to `architecture-docs`.
testing
Generate Compliance Contracts (Contratos de Adherencia) from ARCHITECTURE.md files
testing
Use this skill whenever the user mentions deferred work, known compromises, shortcuts taken, "we'll fix this later," temporary workarounds, missing controls, or any architectural trade-off that should be made visible and tracked. Also trigger when arc42 Section 11 (Risks and Technical Debt) is being authored or updated, when a TDR / Technical Debt Record is requested by name, or when an ADR documents a decision that intentionally accepts debt. Do NOT use for general bug reports, feature requests, or backlog items — TDRs are for architectural or systemic compromises, not ordinary defects.