-
Notifications
You must be signed in to change notification settings - Fork 358
Add Documentation Maintenance as GitHub Action #7294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
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
2accdbf
test: trigger docs workflow (revert before merge)
00cdc46
fix: clarify how to run al-docs in PR comment
a467840
fix: remove --allow-all-paths, copy skills into cwd instead
34eb5c7
fix: invoke al-docs audit skill instead of manual check
026e490
fix: show full copilot output in CI log, extract summary for PR comment
987926e
fix: skip plugin install, read skill files from cwd instead
b01f4ad
fix: trust plugin install folder via config.json instead of allow-all…
d5cacb7
fix: use PR review with REQUEST_CHANGES instead of comment
bebdfb0
fix: allow shell(git:*) for audit staleness check
a2cdb08
fix: allow only shell(git log:*) for audit staleness check
3335584
fix: scope git log to read-only
a9196eb
refactor: use --share for clean session export, ::group:: for CI logs
4db7068
simplify: remove second copilot call, use --share export directly
86b9996
refactor: use conditional copilot check for review decision
54ea8d1
fix: prompt copilot to output only the review body, no preamble
a3a74e9
test: touch DataSearch and DataArchive .al files, fix review prompt
88fa1e0
test: revert DataArchive/DataSearch, touch DataCorrectionFA, fix revi…
e16764a
Merge remote-tracking branch 'origin/main' into docs/documentation-ma…
d7cdfe8
fix: pin action dependencies to commit hashes
d18cfde
fix: skip docs review if already posted on the PR
3318e86
fix: check for existing review before checkout, skip entire job early
4b2ecb8
fix: add dismissal note to review comment
b316c91
Update and audit check for correctnenss
23cf2e8
Copy al-docs plugin from NAV with consistency fixes
410c11e
Replace AI-generated PR comment with deterministic template
2c691ed
Use constrained copilot call to extract coverage % from audit reports
44df4b1
Add workflow_dispatch with skip-dedup option
22f6bf4
Revert "Add workflow_dispatch with skip-dedup option"
309be6c
Add DOCS_SKIP_DEDUP variable to bypass existing review check
8abefd6
Rename variable to RUN_DOCS_CHECK_ON_EVERY_ITERATION, post as comment
3d375a0
Revert test-only changes, add DataSearch comment for workflow trigger
1662582
Remove test AL change from DataSearch app
b5f30f8
Rename plugin author from NAV Team to BC Application Team
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.