Documentation: BC Release Note Generator
Generate a Business Central release note under docs/releasenotesmd/ plus the sibling .releasenote.json field map. The release note is the single stakeholder-facing artefact handed off to the client at deployment time, summarising what changed, why, how it was tested, and who approved it.
This skill is the documentation-bc counterpart to the legacy bc-release-note-generator skill. Unlike that legacy skill, it:
- Uses a canonical, language-neutral field map (
references/release-note-fields.json). - Emits a sibling JSON file alongside the markdown so the
md-to-docx-convertercan drive any Word template without re-parsing. - Hardcodes no company, client, publisher, or organisation values — they are all extracted from
app.jsonor asked from the user.
When to use
The user wants a release note for an approved change — typically after a CCN has been signed off, a spec has been implemented, or a branch is ready to be deployed.
Default output locations
- Release note markdown:
docs/releasenotesmd/RN-NNN-<kebab-title>.releasenote.md - Sibling JSON map:
docs/releasenotesmd/RN-NNN-<kebab-title>.releasenote.json
Never ask the user where to save the files.
Workflow
Follow these steps in order. Do not skip the workspace read or the completeness validation step.
Step 1 — Identify the source change
Ask the user via vscode_askQuestions (only if not already supplied in the request):
- Client name — the client receiving the release. No default.
- CCN number — change-control number, format
DSD-NNNN(or another house format if the workspace already uses one). If adocs/ccn/folder contains a relevant CCN, propose it. - Issue number — ticket / issue reference. No default.
- Prepared by — full name of the person preparing the document. No default; never invent.
Optionally, link the release note to an existing artefact in the workspace:
- A spec under
openspec/specs/(preferred — drives the Scope of Change section). - A CCN under
docs/ccn/. - A merged feature branch (drives Change Request Details via git log).
If the user references a SPEC-NNN or CCN-NNN, read the file and reuse its title and module as defaults for the release note title / module.
Step 2 — Allocate the next RN ID
- List files in
docs/releasenotesmd/matchingRN-*.releasenote.mdand any*.releasenote.mdwith anRN-NNNid in the frontmatter. - Parse the highest existing numeric ID and increment by 1; format as
RN-NNN(zero-padded to 3 digits). - If the folder does not exist or is empty, start at
RN-001.
Step 3 — Read workspace context
This step is mandatory — never invent identity fields.
- Read
app.jsonand capture:version→ release-noteversion(e.g.1.0.0.0).name→ default for the release-notetitlewhen the user has no explicit title.publisher→ default forreleasedBy.application,runtime→ drive Testing Setup (BC version requirements).dependencies→ drive Testing Setup (prerequisite extensions).idRanges→ only for cross-referencing object IDs already used.
- If
app.jsonis missing or any of the above keys are absent, stop and ask the user viavscode_askQuestionsrather than guessing. - Inventory the AL objects that ship with this release. Use
file_searchwithsrc/**/*.al. For each file, read the first ~50 lines to extract:- Object declaration (type + ID + name).
Captionproperty (user-facing functional name).PageTypefor pages.TableTypefor tables (e.g.CDS,Temporary).ToolTipproperties on fields (used in Testing Steps).- XML doc comments (
/// <summary>) on codeunits.
- Optionally check
git log --oneline -20 -- "src/**/*.al"for the most recent AL-related commits and use them in Change Request Details.
If the codebase is large, delegate this inventory step to the Explore subagent with thoroughness medium.
Step 4 — Resolve testing context
For each non-trivial page, table, or codeunit discovered in Step 3, decide what a reviewer must do to verify it works. Group the steps by object type:
- Permission set assignment.
- New / changed pages — open, verify visible fields and their ToolTips, create one record.
- New / changed page extensions — verify added group/fields.
- New / changed codeunits — describe the triggering scenario.
- New / changed reports, queries, XMLPorts — verify the output format.
If any object lacks a Caption or ToolTip, do not fabricate one — instead, list the object by its technical name and add an Open Item under Known Limitations.
Step 5 — Blind spot review
Before drafting, surface to the user via vscode_askQuestions any of the following that apply:
- Missing data migration / upgrade codeunit — if the release adds a new required field on an existing table without a default, ask whether an upgrade procedure is in scope.
- Missing permission set — if a new table is added with no
PermissionSetgrantingCreate / Modify / Delete, ask whether permissions are in scope for this release. - Missing translation — if
translations/*.xlffiles do not contain entries for new captions/tooltips, flag it. - Breaking change — if any existing object signature changed (field renamed/removed, page action renamed), flag as a breaking change and require the user to confirm whether downstream consumers were notified.
- Untested dependency — if
app.jsondeclares a new dependency, ask whether that dependency is available in the target tenant.
For every flagged item, offer three dispositions:
- Add to Change Request Details — visible to the client.
- Add to Known Limitations — visible to the client as a caveat.
- Out of scope for this release — recorded internally only; not surfaced to the client.
Do not proceed to Step 6 until every blind spot has a disposition.
Step 6 — Draft and write the release note
Build the filename: RN-NNN-<kebab-title>.releasenote.md.
<kebab-title>is the lowercase, hyphen-separated title (diacritics stripped, non-alphanumerics removed, collapsed hyphens).- Validate against
^RN-\d{3}-[a-z0-9]+(-[a-z0-9]+)*\.releasenote\.md$before writing.
Write the file at docs/releasenotesmd/<filename> using the template in references/release-note-template.md. The file MUST contain, in this exact order:
- YAML frontmatter:
---
id: RN-NNN
title: <Title>
version: <from app.json>
status: draft
clientName: <from user>
ccnNumber: <from user>
issueNumber: <from user>
releaseDate: <YYYY-MM-DD>
releasedBy: <from app.json publisher>
prepared_by: <author name>
module: <module from spec or CCN>
createdDate: <YYYY-MM-DD>
approvedDate: ""
--- # RN-NNN – <Title>H1.## 1. Release Summary— 2–4 sentence overview of what this release delivers and who is affected.## 2. Scope of Change— bullet list grouped by object type (Tables, Table Extensions, Pages, Page Extensions, Codeunits, Permission Sets, Reports, etc.), each line of the formObject Type ID "Object Name" — Caption-based functional description.## 3. Change Request Details— paragraph form, references the source CCN / SPEC / Issue. Includes a "Recent commits" subsection only if git history was used.## 4. Testing Setup— prerequisites table (BC runtime, application version, dependencies, permission sets, master data preconditions).## 5. Testing Steps— numbered checklist generated in Step 4, one section per area (Permissions / Pages / Page Extensions / Codeunits / Integrations).## 6. Known Limitations— bullet list. Use the literal word "None" if there are no limitations.## 7. Approvals— table with columnsRole | Name | Decision | Date | Signature. Leave the rows empty (signature is collected after delivery).
Every H2 in the generated markdown MUST be immediately followed by <!-- section-key: <CanonicalKey> --> on its own line — see references/release-note-fields.json for the canonical keys.
Step 7 — Emit the sibling JSON map
Alongside the markdown, write RN-NNN-<kebab-title>.releasenote.json with the shape:
{
"artifactType": "RELEASENOTE",
"id": "RN-NNN",
"language": "<bcp47-primary-subtag>",
"frontmatter": { "<yamlKey>": "<value>", ... },
"sections": { "<CanonicalKey>": "<markdown body>", ... }
}
- Keys (both in
frontmatterand insections) MUST come verbatim fromreferences/release-note-fields.json(frontmatterFields[].keyandsections[].key). - Values carry the content in the chosen target language.
- The JSON file is the canonical binding for
md-to-docx-convertertemplate placeholders — failure to emit it is a hard failure of the skill.
Step 8 — Validate completeness
Run through the validation checklist before reporting success. Every entry below MUST be populated and non-placeholder:
✅ id : RN-NNN
✅ title : non-empty, no "[…]" placeholder
✅ version : valid x.y.z(.w)
✅ status : "draft"
✅ clientName : non-empty
✅ ccnNumber : matches DSD-NNNN (or workspace convention)
✅ issueNumber : non-empty
✅ releaseDate : YYYY-MM-DD
✅ releasedBy : non-empty (from app.json publisher, not a hardcoded company)
✅ module : non-empty
✅ createdDate : YYYY-MM-DD
✅ Release Summary : ≥ 1 paragraph
✅ Scope of Change : ≥ 1 bullet
✅ Change Request Details: ≥ 1 paragraph
✅ Testing Setup : ≥ 1 row
✅ Testing Steps : ≥ 1 numbered step
✅ Known Limitations : "None" or ≥ 1 bullet
✅ Approvals : table header present
If any check fails, prompt the user for the missing information and re-run the validation. Do not write the JSON file until validation passes.
Step 9 — Report completion
After both files are written, report to the user:
- Path of the created markdown file (as a markdown link).
- Path of the sibling JSON file (as a markdown link).
- The allocated
RN-NNNid and the workspace version used. - The detected
releasedBy(fromapp.jsonpublisher) and the source spec / CCN if linked. - A 1-line summary of each section.
- Any items recorded under Known Limitations.
- The next recommended action — typically: review the markdown, set
status: approvedin the frontmatter, then run themd-to-docx-converterskill to produce the Word deliverable.
Heading & full-content translation rules
Every artefact produced by this skill MUST follow these rules. They exist so
the downstream documentation-bc-md-to-docx-converter pipeline (AXZ Word
templates) renders correctly in every supported language and so headings
never leak design-time tokens.
- No manual H2 numeric prefix. Write
## Business context, never## 2. Business context. The Word template's multilevel-list style numbers the H2s automatically; a manualN.prefix produces double numbering (e.g.2. 2. Business context) in the rendered docx. - No source-ID suffix in any heading. Drop trailing parentheticals
such as
(from US-NNN),(from US-<NNN>),(procedente de SPEC-001),(aus ARCH-NNN), etc. Trace links to source artefacts belong in the frontmatter or section body, not in the visible heading text. - Translate H3 subsection labels. Subsections keep their
N.Mprefix shape (e.g.### 3.1 …) but the label text MUST be translated into the target language declared in the frontmatterlanguage/localefield. - Translate the ENTIRE table. When a target language is set, every column header AND every cell value in every table MUST be in that language. No English fallbacks for cells, no mixed-language rows. Code identifiers (object names, field names, AL keywords) stay verbatim; surrounding prose translates.
- Regression guard. Before saving, scan the artefact for these
anti-patterns and fix them:
^## \d+(?:\.\d+)*\.\s(numeric-prefixed H2)\((?:from|procedente de|issu[e]? de|aus|da|de|uit)\s+[A-Z]+-\S+\)\s*$on any heading line (source-ID suffix)- English column headers when the document is non-English.
Origin: defects discovered while producing
CCN-001-driver-penalty-management. Tracked indocs/plans/PLAN-documentation-bc-heading-translation-fix.md(PLAN-DOC-BC-003).
Hard rules
- status MUST always be
drafton initial creation. - approvedDate MUST be empty on initial creation.
- The
prepared_byfrontmatter field is mandatory. It must contain the full name of the human user — never an agent role or agent name. If the author name is not evident from context (git user, previous artefacts, or explicit user input), ask the user before writing the file. - Never hardcode a company name, client name, or publisher anywhere in the template, field map, or generated output —
releasedBycomes fromapp.json(publisher) or the user. - Never invent or guess
clientName,ccnNumber, orissueNumber— they MUST come from the user. - Never invent AL object IDs or names — they MUST come from the actual files in
src/. - Never overwrite an existing release note. On collision, re-allocate the RN ID or pick a non-colliding slug.
- The sibling
.releasenote.jsonfile is mandatory — failure to emit it is a hard failure of the skill. - Every H2 in the markdown MUST be followed by its
<!-- section-key: ... -->anchor. - The Approvals table header MUST be present even when empty.
- If
app.jsonis missing or incomplete, stop and ask — do not fabricate version or publisher.
References
references/release-note-fields.json— canonical, language-neutral static field/section keys used to drive the mandatory JSON output and any downstreammd-to-docx-convertertemplate.references/release-note-template.md— full markdown skeleton with section headings, anchors, and{{Placeholder}}tokens.
Integration with other skills
- Upstream:
documentation-bc-ccn-generator(a CCN can be referenced byccnNumber),documentation-bc-technical-spec-generator(a spec can drive the Scope of Change). - Downstream:
md-to-docx-converterconsumes the sibling*.releasenote.jsonto populate a Word template (template hint:7_ReleaseNote_Template).
Downstream rendering dependency (mmdc)
The markdown produced by this skill is intended to be converted to .docx by the documentation-bc-md-to-docx-converter skill. Any Mermaid block in the output — whether emitted as an explicit <!-- DIAGRAM: mermaid ... --> marker or as a bare triple-backtick ```mermaid fence — is rendered to PNG by the Mermaid CLI (mmdc). When mmdc is unavailable the converter falls back to the diagram's plain-text content, so generated documents stay readable but lose their visual diagrams.
Install once per machine (Node.js required):
npm install -g @mermaid-js/mermaid-cli
mmdc --version
Inline  images in the generated markdown are also embedded into the docx body by the converter (resolved relative to the markdown file, then to repo root). Missing image files are left as literal text and produce a warning.
Pre-completion self-check — section-key anchors (MUST)
This step is mandatory before reporting the artefact as done. It enforces the language-neutral binding contract used by the documentation-bc-md-to-docx-converter.
After writing the markdown file, the skill MUST re-read it and verify the anchor contract:
- For every line beginning with
##in the body (not inside fenced code blocks, not in YAML frontmatter), the next non-blank line MUST be: ` ` <CanonicalKey>MUST be one of thesections[].keyvalues declared in the matchingreferences/*-fields.jsonschema (PascalCase, English, byte-stable across languages).- If any H2 is missing its anchor, or its anchor uses a key not present in the schema, the skill MUST fix the file and re-save before returning control to the user.
- The same canonical key MUST appear as a top-level key in the sibling
*.jsonoutput emitted by this skill — NEVER a translated derivative.
Recommended automation: run python .github/skills/shared/scripts/sync-section-keys.py --dry-run against the just-written file; if it reports any missing/mismatched anchor, fix the file in place, then re-run without --dry-run to apply.
Failure of this self-check is a hard failure of the skill — do not deliver the artefact until every H2 carries a valid schema-backed anchor.