From ring-pm-team
Maps eventable points in a Lerian Go service for lib-streaming event emission. Produces a PM-validated event catalog and instrumentation-map.json via three-pass discovery (Survey, Slice, Mark).
How this skill is triggered — by the user, by Claude, or both
Slash command
/ring-pm-team:mapping-streaming-eventsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- User requests an event catalog, eventable-point inventory, or "where should we emit events" map
Runs before: ring:instrumenting-streaming-events
Complementary: ring:instrumenting-streaming-events, ring:codebase-explorer, ring:mapping-feature-relationships
Orchestrates 3-pass codebase discovery to produce an event catalog and instrumentation map for lib-streaming. You orchestrate. Agents explore. You NEVER read, write, or edit source code directly.
Announce at start: "Using ring:mapping-streaming-events through 7 gates (0-7)."
lib-streaming is a producer-only, write-only event-emission library for Lerian Go services. Events are emitted via emitter.Emit(ctx, EmitRequest{DefinitionKey, TenantID, Subject, Payload}) against the streaming.Emitter interface (constructed via streaming.NewBuilder().Catalog(...).Build(ctx)). Wire format: CloudEvents 1.0 binary mode. Each RouteDefinition selects a transport — Kafka, SQS, RabbitMQ, EventBridge, or Custom. Per-tenant SaaS subscription remains the primary delivery model regardless of transport; Kafka topic naming is lerian.streaming.<resource>.<event>[.vN] with tenant carried on the ce-tenantid CloudEvents header.
WebFetch canonical docs: https://raw.githubusercontent.com/LerianStudio/lib-streaming/main/doc.go
WebFetch agent constraints: https://raw.githubusercontent.com/LerianStudio/lib-streaming/main/AGENTS.md
WebFetch changelog: https://raw.githubusercontent.com/LerianStudio/lib-streaming/main/CHANGELOG.md
A candidate is a streamable business fact if and ONLY if ALL of:
account.created, not account.create)Candidates failing ANY of the four → REJECT with documented reason.
| Posture | Direct | Outbox | DLQ | Use when |
|---|---|---|---|---|
| CRITICAL | skip | always | on_routable_failure | Loss is correctness/compliance breach; atomic with DB write |
| IMPORTANT | direct | fallback_on_circuit_open | on_routable_failure | Direct in normal ops; survives broker outage |
| OBSERVATIONAL | direct | never | never | Analytics-grade; loss acceptable |
| CUSTOM | per-event | per-event | per-event | None above fits — requires ≥80 char justification |
| Gate | Name | Agent | Always? |
|---|---|---|---|
| 0 | Stack Detection | Orchestrator (grep + read) | Yes |
| 1 | Pass 1 — Survey | ring:codebase-explorer (single) | Yes |
| 2 | Pass 2 — Slice + Scope Fence | ring:codebase-explorer (single) | Yes |
| 3 | Pass 3 — Mark | ring:codebase-explorer (parallel, 1/segment) | Yes |
| 4 | Catalog Assembly + Validation | Orchestrator (deterministic) | Yes |
| 5 | Business Rendering | Orchestrator | Yes |
| 6 | PM Team Validation | User (PM team) — NEVER SKIPPABLE | Yes |
| 7 | Handoff Package | Orchestrator | Yes |
Gates execute sequentially. Pass 3 (Gate 3) parallelizes internally per segment.
Orchestrator executes directly. Detect in parallel:
1. Go version: grep "^go " go.mod | head -1
2. lib-streaming: grep "lib-streaming" go.mod
3. HTTP framework: grep -rn "gofiber/fiber\|labstack/echo\|gin-gonic" internal/ go.mod
4. gRPC server: grep -rn "grpc.NewServer" internal/
5. RabbitMQ command consumers: grep -rn "lib-commons/v5/commons/rabbitmq" internal/ # command-queue plumbing; eventable publish sites should migrate to lib-streaming
6. Scheduled jobs: grep -rn "robfig/cron\|time.NewTicker" internal/
7. Webhook receivers: grep -rn "webhook\|/hooks/" internal/
8. Worker patterns: grep -rn "commons.Launcher\|commons.App" internal/
9. Tenant source: grep -rn "tmcore.GetTenantIDContext\|GetTenantID" internal/
10. DDD layout: ls internal/services/ internal/domain/ 2>/dev/null
11. Database: grep -rn "jackc/pgx\|database/sql" go.mod
HARD GATE: If not Go → STOP. If tenant source undetectable → STOP and ask user.
Dispatch ring:codebase-explorer to produce docs/streaming/_pass1-survey.md with:
Include in dispatch: stack detection results, lib-streaming WebFetch URLs, constraint to NOT propose events.
Verification: File exists, all 6 sections present, ≥1 entry point, ≥1 aggregate, tenant source identified.
Dispatch ring:codebase-explorer with Pass 1 output to produce docs/streaming/_pass2-segments.md with:
Verification: File exists, ≥1 segment, fence table covers all candidates, ≥1 accepted candidate.
HARD GATE: If post-fence count = 0 across all segments → STOP and surface to user.
Before emitting any Task call, count the explorers you intend to launch in this turn.
All segment explorers leave in the SAME TURN, before reading any explorer output.
Forbidden sequences:
If you find yourself about to dispatch an explorer in a turn AFTER any explorer has already returned a result → STOP. You violated parallel dispatch. Report the violation and mark the gate INCOMPLETE rather than completing the trickle.
After the dispatch turn, verify all segment Task calls were emitted in that single turn. If fewer went out than the Gate 2 segment count, the gate did NOT execute correctly. Mark INCOMPLETE and surface the dispatch failure — do NOT silently continue with a partial pool.
Emit all scoped Task calls (the count established in the STOP-CHECK above) in a SINGLE TURN, as one atomic batch.
If your runtime exposes a multi_tool_use.parallel wrapper, use it to dispatch the complete pool in one wrapped invocation. This is the canonical fan-out mechanism on OpenAI-style tool envelopes and on certain Anthropic SDK consumers — naming it explicitly activates parallel emission on runtimes where trickle-dispatch is the default behavior.
If your runtime emits parallel tool_use blocks natively (Claude Code with Claude models), multi_tool_use.parallel may not be needed — but naming it is harmless and serves as an enforcement anchor.
The STOP-CHECK, anti-trickle, and self-verify guards above remain binding regardless of which mechanism your runtime uses.
For EACH segment from Gate 2, dispatch ring:codebase-explorer IN PARALLEL. Each outputs docs/streaming/_pass3-{segment_name}.json.
Per-segment JSON shape:
{
"segment": "{segment_name}",
"events": [{
"definition_key": "transaction.posted",
"resource_type": "transaction",
"event_type": "posted",
"schema_version": "1.0.0",
"data_content_type": "application/json",
"data_schema": "",
"is_system_event": false,
"description": "...",
"instrumentation_sites": [{"file": "...", "line_anchor": "...", "emission_timing": "post-commit", "current_function": "..."}],
"payload_sketch": {"field_name": "type"},
"tenant_source_at_site": "tmcore.GetTenantIDContext(ctx)",
"consumer_idempotency_key_candidate": "event_id",
"posture": "CRITICAL",
"posture_rationale": "...",
"delivery_policy": {"enabled": true, "direct": "skip", "outbox": "always", "dlq": "on_routable_failure"}
}]
}
Include in every dispatch: lib-streaming WebFetch URLs, scope fence rules, posture decision tree, naming convention (<resource>.<event> lowercase dotted).
Verification: One JSON file per segment, valid JSON, all required fields populated, no null/TBD, CUSTOM posture has ≥80 char rationale.
HARD GATE: Missing/malformed file → re-dispatch ONLY failing segments.
Orchestrator merges all _pass3-*.json events into docs/streaming/instrumentation-map.json. Validate:
definition_key unique, matches ^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$(resource_type, event_type, schema_version) triple uniquetenant_source_at_site non-emptydescription ≥40 chars, payload_sketch ≥1 fieldposture_rationale ≥80 charsdelivery_policy.enabled present and boolean (typically true); direct ∈ {"direct","skip"}, outbox ∈ {"never","fallback_on_circuit_open","always"}, dlq ∈ {"never","on_routable_failure"} — these are the lib-streaming mode strings the dev skill will emit verbatimValidation failures → re-dispatch failing segment's Pass 3 with correction notes. Do NOT manually edit JSON.
Orchestrator generates docs/streaming/event-catalog.md from validated JSON. Required sections:
Present checklist to PM team:
Response options:
APPROVED → proceed to Gate 7REVISE: <segment>: <change> → loops back to Pass 3 for that segmentREVISE: posture <key>: <new posture> → single-event posture changeREVISE: scope-fence: <change> → re-runs Pass 2 + Pass 3HARD GATE: Must not proceed to Gate 7 without explicit APPROVED.
Produce docs/streaming/handoff-to-skill2.md summarizing:
Skill: ring:instrumenting-streaming-eventsSave to docs/streaming/_state.json:
{
"skill": "mapping-streaming-events",
"service_name": "<from Gate 0>",
"current_gate": 0,
"gates": {"0": "PENDING", "1": "PENDING", "2": "PENDING", "3": "PENDING", "4": "PENDING", "5": "PENDING", "6": "PENDING_USER_APPROVAL", "7": "PENDING"},
"metrics": {"entry_points_inventoried": 0, "segments_identified": 0, "candidates_pre_scope_fence": 0, "events_after_scope_fence": 0, "events_critical": 0, "events_important": 0, "events_observational": 0, "events_custom": 0}
}
npx claudepluginhub p/lerianstudio-ring-pm-team-pm-teamAdopts or references Lerian's lib-streaming Go library for producer-only event publication (Kafka/SQS/RabbitMQ/EventBridge). Sweep mode detects DIY publishers; Reference mode catalogs the Builder/Emitter/Catalog facade.
Designs event-driven architectures: maps event flows, defines topic topologies, validates delivery guarantees, and produces event catalog documentation for Kafka, RabbitMQ, SQS, NATS, or Redis Streams.
Designs event-driven architecture — event schemas, pub/sub patterns, idempotency, ordering guarantees. Use when decoupling services, building notification systems, audit trails, or real-time features.