Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
add7ca8
feat: add Documentation Maintenance GitHub Action
Mar 20, 2026
2accdbf
test: trigger docs workflow (revert before merge)
Mar 20, 2026
00cdc46
fix: clarify how to run al-docs in PR comment
Mar 20, 2026
a467840
fix: remove --allow-all-paths, copy skills into cwd instead
Mar 20, 2026
34eb5c7
fix: invoke al-docs audit skill instead of manual check
Mar 20, 2026
026e490
fix: show full copilot output in CI log, extract summary for PR comment
Mar 20, 2026
987926e
fix: skip plugin install, read skill files from cwd instead
Mar 20, 2026
b01f4ad
fix: trust plugin install folder via config.json instead of allow-all…
Mar 20, 2026
d5cacb7
fix: use PR review with REQUEST_CHANGES instead of comment
Mar 20, 2026
bebdfb0
fix: allow shell(git:*) for audit staleness check
Mar 20, 2026
a2cdb08
fix: allow only shell(git log:*) for audit staleness check
Mar 20, 2026
3335584
fix: scope git log to read-only
Mar 20, 2026
a9196eb
refactor: use --share for clean session export, ::group:: for CI logs
Mar 20, 2026
4db7068
simplify: remove second copilot call, use --share export directly
Mar 20, 2026
86b9996
refactor: use conditional copilot check for review decision
Mar 20, 2026
54ea8d1
fix: prompt copilot to output only the review body, no preamble
Mar 20, 2026
a3a74e9
test: touch DataSearch and DataArchive .al files, fix review prompt
Mar 20, 2026
88fa1e0
test: revert DataArchive/DataSearch, touch DataCorrectionFA, fix revi…
Mar 20, 2026
e16764a
Merge remote-tracking branch 'origin/main' into docs/documentation-ma…
Mar 20, 2026
d7cdfe8
fix: pin action dependencies to commit hashes
Mar 20, 2026
d18cfde
fix: skip docs review if already posted on the PR
Mar 20, 2026
3318e86
fix: check for existing review before checkout, skip entire job early
Mar 20, 2026
4b2ecb8
fix: add dismissal note to review comment
Mar 20, 2026
b316c91
Update and audit check for correctnenss
Mar 20, 2026
23cf2e8
Copy al-docs plugin from NAV with consistency fixes
Mar 23, 2026
410c11e
Replace AI-generated PR comment with deterministic template
Mar 23, 2026
2c691ed
Use constrained copilot call to extract coverage % from audit reports
Mar 23, 2026
44df4b1
Add workflow_dispatch with skip-dedup option
Mar 23, 2026
22f6bf4
Revert "Add workflow_dispatch with skip-dedup option"
Mar 23, 2026
309be6c
Add DOCS_SKIP_DEDUP variable to bypass existing review check
Mar 23, 2026
8abefd6
Rename variable to RUN_DOCS_CHECK_ON_EVERY_ITERATION, post as comment
Mar 23, 2026
3d375a0
Revert test-only changes, add DataSearch comment for workflow trigger
Mar 23, 2026
1662582
Remove test AL change from DataSearch app
Mar 23, 2026
b5f30f8
Rename plugin author from NAV Team to BC Application Team
Mar 23, 2026
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
151 changes: 151 additions & 0 deletions .github/workflows/DocumentationMaintenance.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: 'Documentation Maintenance'

on:
pull_request:
branches: ['main', 'releases/*', 'features/*']
paths: ['src/**/*.al']

concurrency:
group: docs-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
actions: read
contents: read
pull-requests: write

jobs:
AuditDocs:
if: github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name
runs-on: ubuntu-latest
timeout-minutes: 30
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GH_TOKEN }}
steps:
- name: Check for existing docs review
id: dedup
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "${{ vars.RUN_DOCS_CHECK_ON_EVERY_ITERATION }}" = "true" ]; then
echo "Dedup check disabled via RUN_DOCS_CHECK_ON_EVERY_ITERATION variable."
echo "skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi

EXISTING=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews \
--jq '[.[] | select(.body | startswith("AL Documentation Audit"))] | length' 2>/dev/null || echo "0")
if [ "$EXISTING" != "0" ]; then
echo "Docs review already posted, skipping."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: Checkout
if: steps.dedup.outputs.skip != 'true'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Node.js
if: steps.dedup.outputs.skip != 'true'
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0

- name: Install Copilot CLI
if: steps.dedup.outputs.skip != 'true'
run: npm install -g @github/copilot

- name: Install al-docs plugin
if: steps.dedup.outputs.skip != 'true'
run: |
copilot plugin install ./tools/al-docs-plugin
PLUGIN_DIR="$HOME/.copilot/installed-plugins"
mkdir -p ~/.copilot
echo "{\"trusted_folders\":[\"$PLUGIN_DIR\"]}" > ~/.copilot/config.json

- name: Find changed apps
if: steps.dedup.outputs.skip != 'true'
id: scope
run: |
MERGE_BASE=$(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }})
CHANGED=$(git diff --name-only "$MERGE_BASE"..${{ github.event.pull_request.head.sha }} -- '*.al')

if [ -z "$CHANGED" ]; then
echo "count=0" >> "$GITHUB_OUTPUT"
exit 0
fi

APPS=$(echo "$CHANGED" | while read -r f; do
d=$(dirname "$f")
while [ "$d" != "." ]; do
[ -f "$d/app.json" ] && echo "$d" && break
d=$(dirname "$d")
done
done | sort -u)

COUNT=$(echo "$APPS" | wc -l | tr -d ' ')
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
echo "apps<<EOF" >> "$GITHUB_OUTPUT"
echo "$APPS" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"

- name: Run al-docs audit
if: steps.dedup.outputs.skip != 'true' && steps.scope.outputs.count != '0'
id: audit
run: |
while IFS= read -r APP_PATH; do
[ -z "$APP_PATH" ] && continue
APP_NAME=$(jq -r '.name // "unknown"' "$APP_PATH/app.json" 2>/dev/null || echo "unknown")
REPORT_FILE="/tmp/audit-${APP_NAME// /-}.md"

echo "Auditing $APP_NAME at $APP_PATH..."

timeout 600 copilot -p \
"Run the al-docs skill in audit mode on the app at $APP_PATH. After the audit, summarize in 2-3 sentences: coverage percentage, what is missing." \
--model claude-opus-4.6 \
--no-ask-user \
--autopilot \
--allow-tool=read \
--allow-tool=glob \
--allow-tool=grep \
--allow-tool='shell(git log:read)' \
--share="$REPORT_FILE" || true

done <<< '${{ steps.scope.outputs.apps }}'

# Combine all audit reports
cat /tmp/audit-*.md > /tmp/full-report.md 2>/dev/null || true

- name: Request docs changes
if: steps.dedup.outputs.skip != 'true' && steps.scope.outputs.count != '0'
env:
GH_TOKEN: ${{ github.token }}
run: |
if copilot -p "Read /tmp/full-report.md. Does the audit show documentation gaps that need to be fixed? Reply only YES or NO." \
-s --no-ask-user --allow-tool=read | grep -qi "yes"; then

# Build comment with deterministic template -- copilot only extracts coverage %
BODY="AL Documentation Audit"$'\n\n'
BODY+="Documentation gaps were detected in the following apps:"$'\n\n'

for REPORT in /tmp/audit-*.md; do
[ -f "$REPORT" ] || continue
# App name from the report filename (set during audit step from app.json)
APP=$(basename "$REPORT" .md | sed 's/^audit-//')
COV=$(copilot -p "Read $REPORT. What is the documentation coverage percentage? Reply with ONLY the number followed by %, like 42%. Nothing else." \
-s --no-ask-user --allow-tool=read 2>&1 | grep -oE '[0-9]+%' | head -1)
[ "$COV" = "100%" ] && continue
BODY+="- **${APP}**: ${COV:-unknown} documentation coverage"$'\n'
done

BODY+=$'\n'"To generate documentation, run \`/al-docs init\` or \`/al-docs update\` using [GitHub Copilot CLI](https://github.com/github/copilot-cli) or [Claude Code](https://claude.com/claude-code)."$'\n'
BODY+="_This review is for awareness to help keep documentation in sync with code changes. It is okay to dismiss this request._"

gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews \
-f body="$BODY" \
-f event="COMMENT"
else
echo "No documentation gaps found."
fi
9 changes: 9 additions & 0 deletions tools/al-docs-plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "al-docs",
"version": "0.1.0",
"description": "AL codebase documentation generator - bootstrap, update, and audit hierarchical docs for Business Central AL apps",
"author": {
"name": "BC Application Team"
},
"keywords": ["al", "business-central", "documentation", "docs"]
}
94 changes: 94 additions & 0 deletions tools/al-docs-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# AL Docs plugin

AL codebase documentation generator that bootstraps, updates, and audits hierarchical docs for Business Central AL apps. Produces CLAUDE.md orientation files and AL-specific docs (data-model.md, business-logic.md, patterns.md) at app and subfolder levels.

**Version:** 0.1.0

## Skills

| Skill | Command | Description |
|-------|---------|-------------|
| AL Docs | `/al-docs` | Bootstrap, update, or audit AL codebase documentation |

## Usage

```
/al-docs # Bootstrap docs (same as /al-docs init)
/al-docs init # Bootstrap documentation for AL app or folder
/al-docs init "path/to/app" # Bootstrap docs for a specific path
/al-docs update # Incrementally refresh docs based on changes
/al-docs audit # Read-only gap analysis without writing files
```

## Modes

### 1. Init (`/al-docs init`)

Bootstraps a complete documentation hierarchy through five phases:

1. **Discovery** -- launches 3 parallel sub-agents to analyze the AL codebase:
- Agent 1: app structure, `app.json` metadata, object inventory by type and subfolder
- Agent 2: data model -- tables, relationships, enums, keys, conceptual model
- Agent 3: business logic, patterns, event architecture, subfolder scoring
2. **Documentation map** -- presents every file to create for user approval
3. **Exit plan mode** -- unlocks write access
4. **Generation** -- parallel sub-agents write docs grouped by scope
5. **Cross-referencing** -- verifies links and consistency

### 2. Update (`/al-docs update`)

Incrementally refreshes docs based on git changes:

1. **Detect changes** -- determines baseline and gets changed `.al` files
2. **Map changes** -- maps AL object types to affected doc files (table changes -> data-model.md, codeunit changes -> business-logic.md, etc.)
3. **Targeted regeneration** -- presents update plan for approval, then updates affected docs only
4. **Staleness report** -- summarizes changes and flags potentially stale sections

### 3. Audit (`/al-docs audit`)

Read-only gap analysis:

- Launches 3 parallel subagents to inventory objects, existing docs, and score subfolders
- Compares expected documentation against what exists
- Reports coverage percentage, missing files, and non-standard patterns
- Provides prioritized recommendations

## Generated doc types

| File | Purpose |
|------|---------|
| `CLAUDE.md` | Orientation: app purpose, dependencies, structure, key objects |
| `data-model.md` | What the app models, table relationships, key fields, enums |
| `business-logic.md` | Processing flows, decision points, error handling, key operations |
| `patterns.md` | Locally applied patterns (IsHandled, TryFunction, etc.) |

## Documentation levels

| Level | Location | Content |
|-------|----------|---------|
| App (has `app.json`) | `/CLAUDE.md`, `/docs/` | App-wide overview, full data model, cross-cutting logic |
| Subfolder (score 7+) | `/[subfolder]/docs/` | CLAUDE.md + at least one additional doc |
| Subfolder (score 4-6) | `/[subfolder]/docs/` | CLAUDE.md only |

## Subfolder scoring

Subfolders are scored 0-10 based on AL object count, table count, codeunit count, event presence, and extension objects:

- **MUST_DOCUMENT (7+)**: CLAUDE.md plus at least one additional file
- **SHOULD_DOCUMENT (4-6)**: CLAUDE.md only
- **OPTIONAL (1-3)**: Skipped

## Plugin structure

```
al-docs-plugin/
├── .claude-plugin/
│ └── plugin.json
├── README.md
└── skills/
└── al-docs/
├── SKILL.md # Router -- dispatches to the correct mode
├── al-docs-init.md # Mode 1: full bootstrap
├── al-docs-update.md # Mode 2: incremental update
└── al-docs-audit.md # Mode 3: read-only audit
```
103 changes: 103 additions & 0 deletions tools/al-docs-plugin/skills/al-docs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
name: al-docs
description: This skill should be used when the user asks to "document AL code", "generate docs for this app", "create documentation for this extension", "document this BC app", "set up docs for this AL project", "refresh my docs after code changes", "what documentation is missing", or wants to bootstrap, update, or audit documentation for a Business Central AL codebase. Generates hierarchical docs (CLAUDE.md, data-model.md, business-logic.md, extensibility.md, patterns.md) tailored to AL object types, table relationships, event architecture, and extension patterns.
allowed-tools: Read, Write, Edit, Glob, Grep, Bash(*)
argument-hint: "[init|update|audit] [path]"
---

# AL Documentation Generator

Generate, update, and audit hierarchical documentation for Business Central AL codebases. Produces documentation adapted to AL object types, table relationships, event-driven architecture, and extension patterns.

## Usage

```
/al-docs # Bootstrap docs (same as /al-docs init)
/al-docs init # Bootstrap documentation for AL app or folder
/al-docs init "path/to/app" # Bootstrap docs for a specific path
/al-docs update # Incrementally refresh docs based on changes
/al-docs audit # Read-only gap analysis without writing files
```

## Routing

Based on the argument provided, load and follow the appropriate mode file. Always read the full mode file before executing -- never run from memory.

### If argument starts with "init" (or no argument)

Read and follow `skills/al-docs/al-docs-init.md` from the plugin directory. Pass any remaining text as the target path.

### If argument starts with "update"

Read and follow `skills/al-docs/al-docs-update.md` from the plugin directory. Pass any remaining text as options (baseline commit, path filter).

### If argument starts with "audit"

Read and follow `skills/al-docs/al-docs-audit.md` from the plugin directory. Pass any remaining text as the target path.

## Mode files

| Mode | Command | File |
|------|---------|------|
| Init | `/al-docs init` | `skills/al-docs/al-docs-init.md` |
| Update | `/al-docs update` | `skills/al-docs/al-docs-update.md` |
| Audit | `/al-docs audit` | `skills/al-docs/al-docs-audit.md` |

## What gets generated

Documentation is hierarchical -- more general at the app level, more specific deeper in the tree.

### Doc types

| File | Purpose |
|------|---------|
| `CLAUDE.md` | Mental model: what this area does, how it works, and non-obvious things to know |
| `data-model.md` | How tables relate and why -- intent, design decisions, gotchas (not field lists). Always includes a mermaid ER diagram. |
| `business-logic.md` | Processing flows as narrative -- decision points, error handling. Includes mermaid flowcharts for processes with branching. |
| `extensibility.md` | Extension points, events, interfaces -- how to customize without modifying core code |
| `patterns.md` | Non-obvious coding patterns (including legacy patterns to avoid in new code) |

### Documentation levels

| Level | Location | Content |
|-------|----------|---------|
| App (has `app.json`) | `/CLAUDE.md`, `/docs/` | App-wide overview, full data model, cross-cutting logic, extensibility, and patterns |
| Subfolder at any depth (scored 7+) | `/[path]/docs/` | CLAUDE.md + at least one of data-model/business-logic/extensibility/patterns |
| Subfolder at any depth (scored 4-6) | `/[path]/docs/` | CLAUDE.md only |

### Scope detection

1. **Target has `app.json`** -- document the entire app as the project level
2. **Target is a folder without `app.json`** -- document that folder; if subfolders at any depth have enough substance, they get their own docs
3. **Recursive evaluation** -- subfolders are evaluated recursively; a subfolder's subfolder can be documented independently if it scores high enough
4. **Locality principle** -- deeper docs are more specific; higher docs are more general, pointing down to specifics

## AL object types and scoring

For the full list of AL object types, subfolder scoring criteria, and change-to-doc mapping, read `skills/al-docs/references/al-scoring.md`.

Summary of scoring classifications:

- **MUST_DOCUMENT (7+)**: CLAUDE.md + at least one of data-model/business-logic/extensibility/patterns
- **SHOULD_DOCUMENT (4-6)**: CLAUDE.md only
- **OPTIONAL (1-3)**: Skip

## Microsoft Docs MCP

During discovery, use the Microsoft Learn MCP tools (`microsoft_docs_search`, `microsoft_docs_fetch`, `microsoft_code_sample_search`) to research the feature area being documented. This provides context about the intended behavior, official terminology, and design rationale that may not be obvious from the source code alone.

- Search for the app's feature area (e.g., "Business Central Shopify connector", "Business Central inventory management") to understand what the feature is supposed to do
- Use this context to write better "How it works" and "Things to know" sections
- **Source code is the source of truth.** Microsoft docs may be outdated or describe planned behavior that differs from the implementation. When docs conflict with what the code actually does, trust the code. Note the discrepancy in documentation if it's meaningful (e.g., "the docs describe X, but the implementation does Y").

## Critical rules

1. **Always read the mode file** -- never attempt to run a mode from memory
2. **User approves before writing** -- present the documentation map or update plan first
3. **Based on real analysis** -- every statement must trace back to actual AL code read during discovery
4. **Preserve existing content** -- when updating, add/edit sections, never delete human-written content
5. **Locality** -- document as locally as possible, getting more general going up the tree
6. **No mechanical listings** -- never list fields, procedures, or AL objects that an LLM can read from code. Capture intent, relationships, gotchas, and design decisions.
7. **Concise over comprehensive** -- shorter docs with real knowledge beat longer docs that list everything
8. **Use Microsoft Docs MCP** (init mode) -- query Microsoft Learn during init discovery to understand feature intent, but always trust source code over docs when they conflict
9. **Formatting** -- sentence case headers, no em dashes (use `--`), blank line before lists
Loading
Loading