with one click
process
// Extract a business process from registered repos via 7-axis code scan + gap-targeted interview, then emit lint-clean BPMN 2.0.
// Extract a business process from registered repos via 7-axis code scan + gap-targeted interview, then emit lint-clean BPMN 2.0.
DFD with trust boundaries + data classifications (Mermaid + optional Threat Dragon JSON). Source-of-truth for /threat-model.
Six-axis Feature Inventory (routes / models / jobs / tests / UI / docs) — a "what we must preserve" spec for greenfield rewrites.
Per-feature Mermaid flowchart — routes / models / jobs / screens. Consumes /extract-features inventory.
Onboard an external repo via a structured handover assessment + harnessability scoring across 5 codebase dimensions.
Self-contained HTML user-journey map (boxes/arrows with per-page modals) — preview between PRD and tech-design.
Interview-driven initiative → milestones → tasks with dependency-aware sequencing. Walks the operator from initiative-level goal through per-milestone Socratic interview, computes a topo-sorted recommended sequence, and optionally files each milestone as a Feature-shape ticket with `blocks` / `blocked by` cross-refs.
| name | process |
| description | Extract a business process from registered repos via 7-axis code scan + gap-targeted interview, then emit lint-clean BPMN 2.0. |
| argument-hint | <process-slug> [--from-endpoint METHOD /path] [--from-machine ClassName] [--from-job JobName] [--scope dir/] [--project name] [--pools] [--swimlanes] [--skip-lint] [--force] |
| allowed-tools | Bash, Read, Grep, Glob, Write |
Maps a named business process from what the codebase actually does to a stakeholder-shareable BPMN 2.0 file. Discovery is anchor-scoped + reachability-bounded — the skill follows only what's connected to the operator-supplied entry point, stops at the connected-component boundary, and crosses repo boundaries only when the target is in apexyard.projects.yaml.
Pairs with /c4 (static system topology) and /extract-features (exhaustive feature inventory) as the "what we already have" tooling family. All three are read-first, ask-only-when-the-code-doesn't-say.
| Dependency | Used for | Without it |
|---|---|---|
bash ≥ 4 | The skill itself | Required |
git | Anchor resolution + workspace cloning | Required |
gh | Cross-repo registry lookup | Optional — falls back to registry-only |
xmllint | XML validity check | Recommended; the smoke test needs it |
node + npx | bpmn-auto-layout for <bpmndi> coords AND bpmnlint for the gate | Without Node: the skill emits a bare BPMN (no <bpmndi>) and warns; with --skip-lint, also bypasses the lint gate. |
yq | Registry parsing | Falls back to grep when absent |
Same disclosure shape as docs/getting-started.md § "Optional: LSP-aware code navigation" — disclosed up front, surfaced when invoked, never silently fails.
Read the registry path via portfolio_registry, the per-project docs dir via portfolio_projects_dir, and the workspace dir via portfolio_workspace_dir — all from .claude/hooks/_lib-portfolio-paths.sh. Source the helper at the top of any bash block that touches those paths:
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-portfolio-paths.sh"
projects_dir=$(portfolio_projects_dir)
registry=$(portfolio_registry)
workspace_dir=$(portfolio_workspace_dir)
Defaults match the single-fork layout. Split-portfolio v2 adopters get the resolved sibling-repo paths transparently. Do not hardcode apexyard.projects.yaml or projects/ literals — the helper resolves whichever mode the adopter is in. See docs/multi-project.md.
Write targets (see me2resh/apexyard#373 + #443): paths documented as projects/<name>/X in this skill are canonical adopter-facing forms — implement them in bash as "${projects_dir}/<name>/X". Never construct from "${PWD}/projects/...", "$(git rev-parse --show-toplevel)/projects/...", or a literal ./projects/... — those break in split-portfolio v2 mode where projects_dir resolves to a sibling repo.
REQUIRED per-block preamble (see #443): Claude executes each bash block as a separate shell invocation. The projects_dir assignment from the Path resolution section above does NOT carry into later blocks. Every bash block that writes to a projects/<name>/X path MUST start with this three-line preamble so it's self-contained:
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-portfolio-paths.sh"
projects_dir=$(portfolio_projects_dir)
# ... now write to "${projects_dir}/<name>/X"
The Path resolution section's example sources the helper once for documentation purposes; it does not absolve later blocks from sourcing it themselves. Treat each bash fence as a fresh process.
# Anchored by a process slug + entry point
/process onboarding --from-endpoint POST /signup
/process checkout --from-machine CheckoutMachine
/process invoicing --from-job ProcessInvoiceJob
/process billing --scope src/billing/
# Multiple anchors compose (union, then connected-component)
/process onboarding --from-endpoint POST /signup --from-machine OnboardingMachine
# Project-targeted (when not invoked from inside workspace/<name>/)
/process onboarding --project signup-svc --from-machine OnboardingMachine
# Layout opt-in (default: swimlanes within one pool)
/process onboarding --pools # one pool per repo, message flows between
# Re-run a previously generated process — uses anchors recorded in <slug>.process-source.md
/process onboarding # regenerates same scope; OFFERS (default-no) to overwrite
/process onboarding --force # skip the overwrite prompt
If no anchor is given AND no <slug>.process-source.md exists yet, the skill stops and asks the operator for one. The skill refuses to scan the whole repo — anchors are mandatory.
projects/<project>/processes/<slug>.bpmn ← the BPMN 2.0 artefact (committed)
projects/<project>/processes/<slug>.process-source.md ← discovery report + interview answers (committed)
projects/<project>/processes/README.md ← one-row-per-BPMN index, auto-maintained
Sibling structure to /c4's projects/<name>/architecture/ and /extract-features's projects/<name>/feature-inventory.md.
The skill scans across seven axes — each is read-only and reachability-bounded from the anchor. Findings are printed for operator review before any file is written.
State machines and workflow DSLs declare the process explicitly; if one of these matches the anchor, it's the highest-signal source.
| Framework | Signature (grep fallback) |
|---|---|
| XState | \.machine\.(ts|js)$; createMachine\s*\(; import .* from ['"](xstate|@xstate)['"] |
| Temporal | @workflow.defn; @activity.defn; proxyActivities< |
| Cadence | @workflow_method; @activity_method |
| AWS Step Functions | \.asl\.json$; "States"\s*:; "StartAt"\s*: |
| Camunda / Cawemo | \.bpmn$; \.cmmn$ (existing files used as starting state, not overwritten blindly) |
| Inngest workflow | inngest\.createFunction\s*\( |
| Activiti / Flowable | \.bpmn(20)?\.xml$ |
Job chains describe deferred work — enqueue(A) → A runs → A enqueues B is a process flow.
| Framework | Signature |
|---|---|
| BullMQ flows | new FlowProducer\s*\(; \.add\s*\(\s*\{[^}]*children: |
| BullMQ chains | new Queue\s*\(; new Worker\s*\(; \.add\s*\(\s*['"][^'"]+['"] |
| Celery chains / chords / groups | chain\s*\(; chord\s*\(; group\s*\(; \.apply_async\s*\(; @shared_task |
| Sidekiq workflows | class \w+\s*\n\s*include Sidekiq::Worker; \.perform_async; sibling jobs in same module |
| Resque pipelines | Resque\.enqueue\s*\(; class extend Resque::Plugins::* |
| AWS SQS handlers | Lambda handlers with Records[].eventSource == "aws:sqs"; SAM Events: SQS: |
| Inngest steps | step\.run\s*\(; step\.sendEvent\s*\( |
For each job in the connected component capture: name, trigger (queue name / event), and the downstream jobs/events it dispatches.
Scheduled handlers are process entry points just like HTTP endpoints; trace what they dispatch.
| Source | Signature |
|---|---|
node-cron | cron\.schedule\s*\( |
@nestjs/schedule | @Cron\s*\(; @Interval\s*\(; @Timeout\s*\( |
| APScheduler | scheduler\.add_job; @scheduler\.scheduled_job |
| Celery beat | CELERY_BEAT_SCHEDULE; beat_schedule\s*= |
| GitHub Actions cron | on: schedule: cron: in .github/workflows/*.yml |
| Vercel cron | vercel\.json "crons" |
| EventBridge / CloudWatch Events | SAM Events: Schedule:; Terraform aws_cloudwatch_event_rule with schedule_expression |
| Cloudflare Workers cron | wrangler\.toml [triggers] crons |
For each schedule capture: cron expression, handler entry point, and what the handler dispatches downstream.
DB columns named status, state, phase, step, stage carry process state in the schema. Grep the column's literal value set + the service-layer code that writes each transition.
| Source | Signature |
|---|---|
| Prisma | prisma/schema.prisma → fields named status|state|phase|step|stage |
| TypeORM / Sequelize | @Column\s*\( on fields named status/state/...; enum types enum \w+Status |
| Drizzle | pgEnum\s*\(; mysqlEnum\s*\(; columns named status/state/... |
| SQLAlchemy / Django ORM | Column\s*\(.*Enum; models.CharField.*choices= |
| Active Record (Rails) | db/schema.rb columns named status/state; enum status: |
| Raw SQL | CREATE TABLE columns named status/state/phase |
| State-writing code | grep \.status\s*=\s*['"] then trace back to the function and its callers |
For each state column capture: full enum value set + the call sites that write each value. Each transition is a process edge.
Endpoint A → emit event / enqueue job → endpoint B handles. Trace across route files in the connected component.
| Pattern | Signature |
|---|---|
| Event emission | eventBus\.emit\s*\(; EventEmitter; Outbox writes; publishEvent\s*\( |
| Queue dispatch from handler | route handler body containing \.add\(.*Queue\s*\); enqueue\s*\(; .apply_async\s*\( |
| HTTP fan-out | route handler body containing fetch\s*\(; axios\.(get|post|...); httpx\.(get|post|...); requests\.(get|post|...); gRPC client calls |
| WebSocket emit | io\.emit\s*\(; socket\.emit\s*\(; ws\.send\s*\( |
Each event / dispatch / fan-out is a candidate edge in the process. Outbound HTTP calls + queue publishes are checked against the registry (axis 6 of the cross-repo flow below).
If the project already has process diagrams, use them as starting state — never overwrite blindly. The operator may have hand-edited them.
| Source | Signature |
|---|---|
| BPMN files | \.bpmn(20)?(\.xml)?$ anywhere in repo |
| CMMN files | \.cmmn(\.xml)?$ |
| Mermaid sequence diagrams in docs | docs/processes/*.md containing ```mermaid\nsequenceDiagram |
| Mermaid flowcharts in docs | docs/processes/*.md containing ```mermaid\nflowchart |
When an existing BPMN matches the slug or anchor, the skill loads it, parses it, and presents the existing model alongside the candidate from axes 1-5 as a diff during the candidate-review step.
README sections and docs/ files with headings like "Onboarding Flow" / "Order Lifecycle" or numbered-step lists are the author's own enumeration.
| Source | Signature |
|---|---|
| README sections | ^##\s+(.*Flow|.*Process|.*Lifecycle|.*Workflow) headers + the bullet/numbered list that follows |
docs/processes/** | entire directory, if present |
| Numbered-step lists | ^\s*\d+\.\s+ lines within a process section |
Use as confirmation / disambiguation source for step labels; don't trust as primary structure (often stale).
Required inputs (parsed from CLI args):
<process-slug> — short kebab-case identifier (onboarding, checkout, invoicing). The BPMN file name, the source-of-truth name, and the registry index key.<slug>.process-source.md to replay from:
--from-endpoint METHOD /path--from-machine ClassName--from-job JobName--scope dir/workspace/<name>/; explicit via --project <name>; falls back to operator-prompt when ambiguous.If no anchor is given AND no source-of-truth file exists, stop and ask:
Which process are we mapping?
process slug: ____________________ (e.g. "onboarding", "checkout")
one-line description: ____________________
AND ONE of:
HTTP endpoint: e.g. POST /signup
state machine class: e.g. OnboardingMachine
queue job name: e.g. ProcessSignupJob
directory path: e.g. src/onboarding/
Invoke discover.sh <project-root> --anchor=<json> --max-depth=6 (the discovery engine). It walks the seven axes, applies reachability bounding from the anchor, and outputs a structured candidate model:
{
"anchor": { "endpoint": "POST /signup", "machine": null, "job": null, "scope": null },
"axes_with_findings": ["1", "2", "3", "5", "7"],
"axes_empty": ["4", "6"],
"nodes": [
{ "id": "evt_start", "type": "event-start", "label": "Signup submitted", "source": "src/routes/signup.ts:14" },
{ "id": "task_validate", "type": "task", "label": "Validate signup payload", "source": "src/routes/signup.ts:18-32" },
{ "id": "task_create_user", "type": "task", "label": "Create user record", "source": "src/services/user.ts:45" },
{ "id": "task_send_verify", "type": "task", "label": "Send verification email", "source": "src/workers/email.ts:12 (queue: send-verify-email)" },
{ "id": "task_verify_identity", "type": "external-call", "label": "Verify identity", "source": "src/services/user.ts:60 → identity-svc#POST /verify-identity", "cross_repo": "identity-svc" },
{ "id": "gw_email_verified", "type": "gateway", "label": "Email verified?", "source": "User.emailVerifiedAt column" },
{ "id": "task_complete_profile", "type": "task", "label": "Complete profile", "source": "src/routes/profile.ts:24" },
{ "id": "evt_end", "type": "event-end", "label": "Onboarding complete", "source": "User.onboardedAt column" }
],
"edges": [
{ "from": "evt_start", "to": "task_validate", "kind": "sequence" },
{ "from": "task_validate", "to": "task_create_user", "kind": "sequence" },
{ "from": "task_create_user", "to": "task_send_verify", "kind": "sequence" },
{ "from": "task_send_verify", "to": "task_verify_identity", "kind": "message", "label": "verify-identity-requested" },
{ "from": "task_verify_identity", "to": "gw_email_verified", "kind": "sequence" },
{ "from": "gw_email_verified", "to": "task_complete_profile", "kind": "sequence", "label": "yes" },
{ "from": "gw_email_verified", "to": "evt_start", "kind": "sequence", "label": "no — resend" },
{ "from": "task_complete_profile", "to": "evt_end", "kind": "sequence" }
],
"external_touchpoints": [
{ "id": "ext_audit_log", "label": "Audit log service", "source": "src/services/audit.ts:8 → POST /audit-log" }
],
"ambiguities": [
{ "node": "task_send_verify", "question": "user-driven, service task, or external API call?" },
{ "node": "task_verify_identity", "question": "identity-svc is registered — follow the trace into it? (default: yes)" }
],
"missing_labels": [
{ "node_id": "task_step2", "current_label": "step2", "evidence": "src/routes/signup.ts:42" }
],
"invisible_lanes_candidates": [
"Admin approval — if signup matches fraud heuristics, code dispatches `ManualReviewJob` but the approver role isn't in the codebase. Confirm?"
]
}
When any node has cross_repo: <name> set, invoke cross-repo.sh --target=<name>:
Look up the target in apexyard.projects.yaml via portfolio_registry.
If the target is registered AND workspace/<name>/ exists → recurse discovery into the target with the receiving endpoint as the new anchor.
If registered AND workspace/<name>/ missing → OFFER to clone (default-yes):
Cross-repo handoff detected: signup-svc → identity-svc (POST /verify-identity)
identity-svc is registered in apexyard.projects.yaml but not cloned locally.
Clone now? (default: yes)
Yes → git clone github.com/<org>/identity-svc workspace/identity-svc (gitignored), then continue trace
No → render as external touchpoint with documentation
Decline → render as external touchpoint with <bpmn:documentation> explaining "registered but not cloned — clone via git clone github.com/<org>/<repo> workspace/<repo> to expand on re-run".
If the target is not registered → render as an external participant pool (out-of-org). Clearly marked so the trust boundary is visible.
Each repo encountered becomes a swimlane (default) or pool (when --pools set or interview-selected).
Print the full candidate model in compact form, plus the trace report:
PROCESS: onboarding
ANCHOR: POST /signup (signup-svc)
Discovery (5 / 7 axes had findings):
✓ Axis 1 — Explicit workflows: OnboardingMachine (XState) at src/onboarding/state.ts
✓ Axis 2 — Queues: send-verify-email (BullMQ) at src/workers/email.ts
✓ Axis 3 — Cron: nightly-resend-verify (node-cron) at src/workers/cleanup.ts
─ Axis 4 — State columns: User.status (pending|verified|complete) at prisma/schema.prisma
✓ Axis 5 — API choreography: POST /verify-identity → identity-svc
─ Axis 6 — Existing BPMN: none
✓ Axis 7 — Documented steps: README "Onboarding Flow" section
Scope: 12 nodes reachable from POST /signup, 3 external touchpoints not expanded
Cross-repo trail:
signup-svc → POST /verify-identity in identity-svc → emit "identity.verified" →
onboarding-svc subscribes → completes profile
(3 repos in scope; identity-svc and onboarding-svc both registered, both cloned)
Candidate model:
[Start] "Signup submitted"
→ [Task] "Validate signup payload" (src/routes/signup.ts:18-32)
→ [Task] "Create user record" (src/services/user.ts:45)
→ [Task] "Send verification email" (src/workers/email.ts:12, queue: send-verify-email)
→ [Message] "verify-identity-requested" → identity-svc
→ [Task] "Verify identity" (identity-svc / src/routes/identity.ts:8)
→ [Gateway] "Email verified?" (User.emailVerifiedAt)
yes → [Task] "Complete profile" (src/routes/profile.ts:24)
→ [End] "Onboarding complete" (User.onboardedAt)
no → loop to [Start]
External touchpoints (not expanded):
Audit log service (src/services/audit.ts:8) — expand into sub-process? [N/y]
Ambiguities to resolve (will ask in interview):
- task_send_verify — user-driven, service task, or external API call?
- task_step2 (src/routes/signup.ts:42) — what should this be called in the BPMN?
Invisible lanes (suggested):
- Admin approval lane — if signup matches fraud heuristics, code dispatches
ManualReviewJob but the approver role isn't in the codebase. Add lane? [Y/n]
Layout default: swimlanes within one pool (one lane per repo: signup-svc, identity-svc, onboarding-svc)
Change to pools + message flows? [N/y] ← shows trust boundaries explicitly
Accept candidate and continue to interview? (a)ccept · (e)dit · (q)uit
>
On e: enter an edit loop — operator can add/remove/relabel nodes, change edge kinds, redraw swimlane assignments, etc.
On q: exit without writing any files. The discovery report is discarded (re-run regenerates).
On a: continue.
Iterate over ambiguities, missing_labels, and invisible_lanes_candidates from step 2. Do not ask questions whose answer is already in the discovery report.
Each interview prompt is a single question:
Q1/4: task_send_verify (src/workers/email.ts:12) — what kind of task?
(a) service task — system-driven, no human
(b) user task — user has to do something
(c) send task — fires off a message/event
(d) external task — handled by an external system (mark for sub-process expansion)
Choice: a
Q2/4: task_step2 (src/routes/signup.ts:42) — what should this step be called in the BPMN?
Label: Persist signup metadata
Q3/4: Add admin-approval lane? (fraud-heuristic dispatch goes to ManualReviewJob; no in-code approver role)
[Y/n]: y
Lane name (default: "Fraud review"): Compliance review
Q4/4: External touchpoint "Audit log service" — sub-process or black-box?
(a) sub-process — expand inline
(b) black-box — single shape, no expansion
Choice: b
Answers are appended to the source-of-truth markdown for replay.
Invoke generate-bpmn.sh <project-root> <source-of-truth.md> -o <output.bpmn>:
<bpmn:definitions> root with targetNamespace="http://apexyard/process/<slug>"<bpmn:collaboration> containing per-repo pools (when --pools) or one pool with per-repo <bpmn:lane>s (default).<bpmn:process> per pool (default: one process).<bpmn:startEvent> / <bpmn:endEvent> for events; <bpmn:task> / <bpmn:serviceTask> / <bpmn:userTask> / <bpmn:sendTask> for tasks; <bpmn:exclusiveGateway> / <bpmn:parallelGateway> for gateways.<bpmn:sequenceFlow> for sequence edges; <bpmn:messageFlow> for cross-pool / message edges (only valid inside <bpmn:collaboration>, not inside <bpmn:process>).<bpmn:documentation> child citing source evidence (src/onboarding/state.ts:42-58, cron config, operator input).npx -y bpmn-auto-layout to populate <bpmndi:BPMNDiagram> coords. If Node is unavailable, emit without the <bpmndi> block and warn — Camunda Modeler shows "blank" but the file is still semantically valid; operators with Node can re-run later.Invoke lint.sh <output.bpmn>:
Run npx -y bpmnlint <output.bpmn> with the ruleset:
bpmnlint/recommended baselabel-required (every flow node has a label)no-disconnected (every node is on a path from a start event to an end event)no-implicit-split (multiple outgoing sequence flows need an explicit gateway).bpmnlintrc in the project root.If violations exist, surface each with file-line context, then offer:
bpmnlint reported 2 violations:
[ERROR] label-required — task_step2 has no label
element: bpmn:Task id="task_step2" (line 47)
[WARN] no-implicit-split — task_create_user has 2 outgoing flows without a gateway
element: bpmn:Task id="task_create_user" (line 52)
How to proceed?
(a) auto-fix where possible — adds placeholder labels, inserts exclusiveGateway for splits
(r) re-interview affected nodes
(s) accept with documented exception — adds <bpmn:documentation> stating the override
(q) quit, leave .bpmn unmodified
Choice:
On a: apply auto-fixes (labels from node.id slugified; gateways inserted with <bpmn:documentation> "auto-inserted by /process to satisfy no-implicit-split"); re-run lint.
On r: jump back to step 5 for only the affected nodes.
On s: write the exception to .bpmnlintrc (per-rule, per-element-id), document the exception in the BPMN's <bpmn:documentation>, re-run lint.
On q: stop. Source-of-truth markdown is still written so progress isn't lost.
The skill exits successfully only when bpmnlint --max-warnings 0 returns 0. Use --skip-lint to bypass (the operator owns the consequences).
Three files:
projects/<project>/processes/<slug>.bpmn — the artefactprojects/<project>/processes/<slug>.process-source.md — the discovery report + interview answers + final candidate model + lint dispositionprojects/<project>/processes/README.md — append/update the index rowIf <slug>.bpmn already exists:
projects/<project>/processes/<slug>.bpmn already exists (last written 2026-04-12).
Overwrite? [N/y]
Default N. On N, write to <slug>-<YYYY-MM-DD>.bpmn and tell the operator where the new file landed. The original is preserved. With --force, skip the prompt.
✓ /process onboarding complete
Artefact: projects/signup-svc/processes/onboarding.bpmn
Source: projects/signup-svc/processes/onboarding.process-source.md
Index: projects/signup-svc/processes/README.md
Scope: 12 nodes, 14 edges, 3 repos, 1 external participant
Cross-repo: signup-svc → identity-svc → onboarding-svc (all registered, all cloned)
Lint: bpmnlint clean (0 errors, 0 warnings)
Preview in Camunda Modeler:
open https://demo.bpmn.io and drag-drop the .bpmn file
or: install Camunda Modeler from https://camunda.com/download/modeler/
Re-run /process onboarding when the underlying code changes (it'll OFFER to overwrite).
Goal: map onboarding flow for a SaaS spanning three repos (signup-svc, identity-svc, onboarding-svc).
$ /process onboarding --from-endpoint POST /signup --project signup-svc
Discovery:
✓ Axis 1: OnboardingMachine at src/onboarding/state.ts (XState, 6 states, 8 transitions)
✓ Axis 2: send-verify-email queue handler at src/workers/email.ts
✓ Axis 5: outbound HTTP POST /verify-identity → identity-svc (registered)
Cross-repo trace:
signup-svc emits "identity.verified" on success
onboarding-svc subscribes (per its src/subscriptions/identity.ts:14)
onboarding-svc dispatches CompleteProfileJob
Candidate model: 12 nodes, 14 edges, 3 swimlanes (one per repo within one pool)
Ambiguities:
- task_send_verify — service / user / send / external?
- task_step2 in src/routes/signup.ts:42 — what label?
Operator review: accept
Interview:
Q1: task_send_verify → send task
Q2: task_step2 → "Persist signup metadata"
Q3: external touchpoint "Audit log service" → black-box
Generate BPMN: 1.2 KB raw XML + bpmn-auto-layout coords → 4.8 KB total
Lint: bpmnlint clean (0 errors)
✓ projects/signup-svc/processes/onboarding.bpmn written
Opens cleanly in Camunda Modeler with all three swimlanes, message flows between, and source citations under each element.
<camunda:formKey>, <zeebe:taskDefinition>) — out of scope. Vanilla BPMN 2.0 only. Adopters who want engine-specific extensions post-process with their engine's tooling..bpmn back into the interview state) — out of scope; re-run + OFFER-to-overwrite is the supported flow.A process diagram written from memory drifts the moment a developer changes a state-machine transition or adds a new queue handler. A process diagram extracted from code stays accurate but skips the invisible lanes (human approvers, external systems not called from this repo). This skill closes both gaps: code is the source, interview is the augmentation, the operator owns the disposition of ambiguities.
For microservice architectures, the cross-repo trace via the registry replaces the "let me check the other repo" stall in stakeholder meetings — the BPMN already includes the whole connected component, with trust boundaries visible when the operator chose pools+message-flows.
.bpmn back into the interview statePart of ApexYard — multi-project SDLC framework for Claude Code · MIT.