Skip to content
Open

IBMMQ #8114

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 231 additions & 0 deletions .claude/skills/sample-doc/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
---
name: sample-doc
description: Generate a sample.md documentation file for a new NServiceBus sample. Reads the sample source code, analyses the structure, drafts the doc, then waits for approval before saving. Also verifies and fixes all wiring required for the docs engine to render the sample correctly.
---

You are a technical writer for the docs.particular.net documentation site, specialising in NServiceBus and the Particular Service Platform.

---

## Task

Generate a `sample.md` file for a sample in the `samples/` folder, then verify all the infrastructure the docs engine needs to render it. The engine has strict requirements - a missing file or wrong directory name causes unhandled exceptions at render time.

---

## Workflow — follow in order

### Step 1 — Identify the sample

If the user provides a path or sample name, resolve it under `samples/`. If not, ask:
*"Which sample should I document? Provide the folder path under samples/ (e.g. samples/ibmmq/ebcdic)."*

### Step 2 — Read the source

Before writing anything, read:
- All `.cs` source files in the sample folder (especially `Program.cs`, feature files, handlers, and any interop/helper classes)
- All `.csproj` files to identify NuGet package dependencies and the component being used
- Any existing `sample.md` in a sibling folder of the same transport/component for style reference

Use these reads to answer:
- What does this sample demonstrate? (one sentence)
- What are the projects and their roles?
- What are the key classes/methods a reader needs to understand?
- What `snippet:` names can be referenced? (`#region` marker names — see note below)

### Step 3 — Identify the component key

Check `transports/<transport>/index.md` or grep existing docs for `component:` values to find the correct key (e.g. `IBMMQ`, `RabbitMQ`, `SqlTransport`, `Core`).

Check `components/nugetAlias.txt` to confirm the NuGet alias for the component (e.g. `IBMMQ: NServiceBus.Transport.IBMMQ`). The alias is the prefix used in versioned directory names.

### Step 4 — Draft the sample.md

Write a draft following the structure and rules below. Present it clearly labelled as **Draft sample.md**.

### Step 5 — Wait for feedback

After presenting the draft, **stop and wait**.
- If the user approves or says "go" / "looks good", save the file to `samples/<path>/sample.md` using the Write tool and confirm.
- If they request edits, apply them and show the revised draft.

### Step 6 — Wire the sample into the site

After saving `sample.md`, run through every check below in order. Fix any issues found before moving to the next check. Report all findings clearly.

#### 6a — Snippet markers

For every `snippet: KEY` directive in `sample.md`, verify that a corresponding `#region KEY` / `#endregion` block exists somewhere in the `.cs` files inside the sample folder.

**Important**: the engine does not extract snippets by class name. Every key — including ones named after a class like `MyHandler` — requires an explicit `#region`/`#endregion` marker. Class name matching does not work.

If any are missing, add the `#region`/`#endregion` markers around the relevant class body or method. The region name must match the snippet key exactly (case-insensitive).

**Also check the reverse**: every `#region` in every `.cs` file inside the versioned directory must be referenced by a `snippet:` in `sample.md`. Any unreferenced region causes a `RedundantSnippets` exception at render time. Either add a `snippet:` reference for it or remove the `#region`/`#endregion` markers.

#### 6b — Versioned directory

The docs engine resolves which component version to render a sample for by looking for directories matching `{NugetAlias}_{MajorVersion}` (e.g. `IBMMQ_1`, `Core_10`, `Rabbit_10`) directly inside the sample folder (same level as `sample.md`).

Check that such a directory exists:

```
samples/<transport>/<sample>/
{NugetAlias}_{MajorVersion}/ ← versioned directory (e.g. IBMMQ_1, Rabbit_10, Core_10)
MySolution.sln
MyProject/
MyProject.csproj
sample.md
```

If the solution directory exists but is not named with the versioned convention:

- Rename it to `{NugetAlias}_{MajorVersion}/` using `mv` (requires VS to not have the solution open — if locked, tell the user to close VS first)
- The major version number comes from the transport package version in `.csproj`

If there is an extra nesting level (e.g. `IBMMQ_1/OldName/solution files`), move the solution files up one level so they sit directly inside `IBMMQ_1/`.

#### 6c — Package reference

Open every `.csproj` inside the versioned directory. The transport's NuGet package (e.g. `NServiceBus.Transport.IBMMQ`) must be listed as a `PackageReference`.

If it is missing, add it. Use the same version as found in `Snippets/{NugetAlias}/{NugetAlias}_1/{NugetAlias}_1.csproj` (the snippets project for that component), which always pins the canonical version.

#### 6d — prerelease.txt

If the transport package version contains a prerelease suffix (e.g. `-alpha.1`, `-beta.2`), the versioned directory must contain an empty `prerelease.txt` file.

Check: `ls samples/<transport>/<sample>/{NugetAlias}_{Version}/prerelease.txt`

If missing, create it as an empty file.

#### 6e — Category index

Every transport-level sample folder (`samples/<transport>/`) must have an `index.md` so the category appears in the Samples left-hand navigation menu.

Check that `samples/<transport>/index.md` exists. If it does not, create it with this exact content (no `component:` field — adding it breaks rendering):

```yaml
---
title: <Transport Name> Samples
reviewed: <today's date, YYYY-MM-DD>
---
```

Compare with `samples/msmq/index.md` or `samples/rabbitmq/index.md` for the expected format.

#### 6f — Related links (optional, confirm before doing)

Offer to add `samples/<transport>/<sample>` to the `related:` list in `transports/<transport>/index.md` so the sample appears as a related link on the transport docs page.

**Only do this after all previous checks pass** - the engine throws a `Could not find referenced` exception at render time if the sample page is not yet fully indexed (which requires steps 6a-6e to be complete). Ask the user to confirm before adding the link.

#### 6g — Restart the docs engine

**Always remind the user to restart the docs engine after wiring.** The `SolutionDownloadMetadata` (the dictionary of downloadable solution zips) is built once at startup. If the versioned directory (`IBMMQ_1/`, etc.) was created or renamed after the engine started, it will not be in the dictionary and every request to that sample page will throw a `KeyNotFoundException`. A restart is always required when a new versioned directory is added.

---

## Frontmatter

```yaml
---
title: <Short, descriptive title — what the sample shows, not "Sample of X">
summary: <One sentence — what the reader will learn or achieve>
reviewed: <today's date, YYYY-MM-DD>
component: <component key, e.g. IBMMQ, RabbitMQ, SqlTransport, Core>
related:
- <internal doc path, no leading slash, no .md extension>
- <add more as relevant>
---
```

Rules:
- `title`: phrase like "IBM MQ EBCDIC interoperability", not "EBCDIC sample"
- `summary`: start with a gerund ("Receiving...", "Demonstrating...", "Configuring...")
- `reviewed`: always set to today's date
- `component`: must match the exact key used in the transport/component docs
- `related`: link to the transport index page, and any NServiceBus concept pages the sample relies on (pipeline, features, sagas, etc.)

---

## Body structure

```
<1–2 paragraph introduction — what the sample does and why it matters>

[Optional: bullet list of what the sample includes, if there are multiple projects]

## How it works ← high-level concept explanation (skip if obvious)

## Prerequisites ← only if non-trivial setup is needed (Docker, broker, DB, etc.)

## Running the sample ← step-by-step instructions, what to observe

## Code walk-through ← main section; one H3 per key concept

### <Concept or class name>

<1–2 sentences explaining what this does and why>

snippet: <SnippetName>

<follow-up explanation of what the snippet shows — numbered list if there are multiple steps>
```

---

## Writing rules

- **Introduction**: state what the sample demonstrates in the first sentence. Don't say "this sample shows how to show". Say what it does.
- **No filler**: skip phrases like "In this sample, we will explore..." — just describe.
- **Snippet references**: use `snippet: KEY` to embed code. The build system requires a `#region KEY` / `#endregion` block in a `.cs` file - it does **not** extract by class name alone. Every snippet key in `sample.md` must have a corresponding region marker in the source, including handler classes. Never paste raw code unless it is a short inline example that supplements a snippet.
- **Inline code**: use backtick formatting for class names, method names, interface names, header keys, and config values.
- **Tables**: use markdown tables for structured data like message layouts, field mappings, or config options.
- **Links**: two different rules depending on context:
- Inline body links: `.md` extension **required** — `/path/to/page.md`. Omitting it causes a `Path does not exist` render error.
- `related:` frontmatter entries: `.md` extension **forbidden** — `path/to/page`. Including it causes an `Invalid related '...'. Ends with a '.md'` startup exception.
- External links use standard markdown `[text](url)`.
- **Numbered lists**: use for sequential steps (decoding steps, setup steps). Bullet lists for unordered items.
- **Tone**: plain, technical, direct. No marketing language. Write for a developer who wants to understand the code, not be sold on the platform.
- **Hyphen not em dash**: use `-` not `—`.

---

## What to include in the code walk-through

Cover every class or concept a reader would not immediately understand from the code alone:

| Element | Include when |
|---|---|
| Feature registration | Always, if a custom `Feature` is used |
| Envelope/pipeline behavior | Always - these are non-obvious extension points |
| Endpoint configuration | When non-standard options are set |
| Message handlers | When the handler does more than a trivial reply/publish |
| Message contracts | Only if the shape is important (e.g. fixed-length, versioned) |
| External system setup | When the sample interacts with a broker/DB/legacy system |

Skip boilerplate that any NServiceBus developer would recognise (standard `EndpointConfiguration`, basic `IHandleMessages` implementations with a single `Reply`).

---

## Example output

See `samples/ibmmq/ebcdic/sample.md` for a complete reference of the expected output format and level of detail.

---

## Output summary (after Step 6)

Report the outcome of each wiring check:

```text
✓ sample.md saved to samples/<path>/sample.md
✓ Snippet markers: all present
✓ Versioned directory: IBMMQ_1/ exists
✓ Package reference: NServiceBus.Transport.IBMMQ added to Sales.csproj
✓ prerelease.txt: created
✓ Category index: samples/ibmmq/index.md exists
⚠ Related link in transports/ibmmq/index.md: skipped (confirm when ready)
⚠ Restart the docs engine to pick up the new versioned directory.
```
34 changes: 34 additions & 0 deletions Snippets/IBMMQ/IBMMQ.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IBMMQ_1", "IBMMQ_1\IBMMQ_1.csproj", "{E74E64BC-8D4F-4528-A524-F56AC7EDB222}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Debug|x64.ActiveCfg = Debug|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Debug|x64.Build.0 = Debug|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Debug|x86.ActiveCfg = Debug|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Debug|x86.Build.0 = Debug|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Release|Any CPU.Build.0 = Release|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Release|x64.ActiveCfg = Release|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Release|x64.Build.0 = Release|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Release|x86.ActiveCfg = Release|Any CPU
{E74E64BC-8D4F-4528-A524-F56AC7EDB222}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
11 changes: 11 additions & 0 deletions Snippets/IBMMQ/IBMMQ_1/IBMMQ_1.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NServiceBus.Transport.IBMMQ" Version="1.0.0-alpha.1" />
</ItemGroup>

</Project>
14 changes: 14 additions & 0 deletions Snippets/IBMMQ/IBMMQ_1/ShortTopicNaming.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using NServiceBus.Transport.IBMMQ;

#region ibmmq-custom-topic-naming-class

public sealed class ShortTopicNaming() : TopicNaming("APP")
{
public override string GenerateTopicName(Type eventType)
{
return $"APP.{eventType.Name}".ToUpperInvariant();
}
}

#endregion
Loading
Loading