| name | arch:asyncapi-contract |
| argument-hint | [issue_number] |
| description | Generates an AsyncAPI 2.6 spec for new or changed event-driven channels in a story. Reads scout.yaml dependencies for SQS/SNS/EventBridge/Kafka references; writes async-{slug}.asyncapi.yaml to docs/stories/{id}/design/. Skips cleanly if no event channels are detected.
|
| context | fork |
| allowed-tools | ["Read","Glob","Grep","Bash","Write"] |
| write-paths | ["docs/"] |
AsyncAPI Contract Generator
Produces a minimal but complete AsyncAPI 2.6 spec covering only the new or changed
event-driven channels introduced by a story. Callable standalone or from
/arch:design-implementation.
Invocation:
/arch:asyncapi-contract 460 # for a specific issue
/arch:asyncapi-contract # uses $AGENT_DOCS_DIR/active-story.yaml
Constraints
- Write:
docs/ only
- Read: everything else — read-only
- One spec file per story; re-running overwrites the existing spec
Workflow
Step 1 — Resolve issue number
if [ -n "$ARGUMENT" ]; then
ISSUE_NUMBER="$ARGUMENT"
else
ISSUE_NUMBER=$(yq e '.issueNumber' "${AGENT_DOCS_DIR:-docs}/active-story.yaml" 2>/dev/null)
fi
if [ -z "$ISSUE_NUMBER" ] || [ "$ISSUE_NUMBER" = "null" ]; then
echo "ERROR No issue number. Pass one or run /github:story-fetch first."
exit 1
fi
REPOSITORY=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
SCOUT_YAML="docs/stories/${ISSUE_NUMBER}/scout.yaml"
Guard: if $SCOUT_YAML does not exist → exit with error pointing to /scout:prepare-for-dev.
Step 2 — Detect event bus references
Read docs/stories/{id}/scout.yaml. Scan both change_list[].file paths and
dependencies[].name + dependencies[].reached_via for event bus keywords:
SQS | SNS | EventBridge | Kafka | RabbitMQ | kinesis | pubsub | event.bus | messagebus
Also grep relevant files from change_list:
grep -rn -i -E "SQS|SNS|EventBridge|Kafka|RabbitMQ|sendMessage|publish\(|subscribe\(" \
$(echo "$CHANGE_LIST_FILES" | tr '\n' ' ') 2>/dev/null
If no event bus references found in scout data or files AND story body does not mention
"event", "queue", "topic", "message", "async", "publish", "subscribe" → print:
arch:asyncapi-contract: no event channels detected for issue #{id} — skipping
Exit 0.
Step 3 — Identify broker type and channels
From the detected references, determine:
- Broker: SQS | SNS | EventBridge | Kafka | RabbitMQ
- Channels: queue/topic names or ARN patterns (extract from grep output or dependency names)
- Direction: publish (producer) | subscribe (consumer) | both
Read story body for message payload context:
gh issue view $ISSUE_NUMBER --repo $REPOSITORY --json body --jq '.body'
Step 4 — Read NFR registry
Read nfr-registry.yaml for reliability and security NFRs relevant to message delivery
guarantees and authentication.
Step 5 — Generate AsyncAPI 2.6 spec
Determine {slug} from the primary domain/service name (2–3 words, kebab-case).
Produce the YAML spec:
asyncapi: "2.6.0"
info:
title: "{Service} Events"
version: "0.1.0-design"
description: |
Design-phase event spec for story #{issue_number}: {title}.
Generated by /arch:asyncapi-contract on {YYYY-MM-DD}.
Status: proposed — subject to change before implementation.
servers:
production:
url: "{broker-endpoint}"
protocol: {sqs|sns|kafka|amqp}
description: Production broker
channels:
"{queue-name-or-topic-arn}":
description: "Purpose of this channel"
publish:
operationId: publish{EventName}
message:
$ref: "#/components/messages/{EventName}Message"
bindings:
sqs:
queue:
name: "{queue-name}"
fifoQueue: false
subscribe:
operationId: on{EventName}
message:
$ref: "#/components/messages/{EventName}Message"
components:
messages:
{EventName}Message:
name: "{EventName}Message"
contentType: application/json
headers:
type: object
properties:
correlationId:
type: string
payload:
$ref: "#/components/schemas/{EventName}Payload"
schemas:
{EventName}Payload:
type: object
required: [eventType, timestamp, payload]
properties:
eventType:
type: string
enum: [{EVENT_TYPE_VALUES}]
timestamp:
type: string
format: date-time
correlationId:
type: string
payload:
type: object
description: "Event-specific data"
properties: {}
Broker-specific bindings:
| Broker | Binding key | Key fields |
|---|
| SQS | sqs | queue.name, queue.fifoQueue, queue.visibilityTimeout |
| SNS | sns | topic.name |
| EventBridge | http | method: POST, target EventBridge URL |
| Kafka | kafka | topic, partitions, replicas |
Step 6 — Write file
mkdir -p "docs/stories/${ISSUE_NUMBER}/design"
OUTPUT="docs/stories/${ISSUE_NUMBER}/design/async-${SLUG}.asyncapi.yaml"
Write the generated YAML to $OUTPUT.
Print:
arch:asyncapi-contract: wrote docs/stories/{id}/design/async-{slug}.asyncapi.yaml
Output
| File | Description |
|---|
docs/stories/{id}/design/async-{slug}.asyncapi.yaml | AsyncAPI 2.6 spec (design phase) |
Related Skills
| Skill | Role |
|---|
/arch:design-implementation | Orchestrator — calls this skill conditionally |
/arch:openapi-contract | Sibling — generates REST contracts |
/arch:design-review | Posts this file to GitHub for review |
/scout:prepare-for-dev | Must run first; writes scout.yaml consumed here |