Wiki Audit
Wiki Audit
Section titled “Wiki Audit”atlcli audit wiki checks the pages of a synced docs directory for content that needs attention:
- pages nobody edited for months;
- orphans and broken internal links, and external links that no longer answer;
- pages with one contributor or no active one (bus factor);
- missing labels, restrictions, drafts, archived pages, heavily edited pages and folder problems.
It reads the local sync database, so it describes the last pull. It only reports. Changes happen only when you ask for them with --fix, as a plan you review first.
Every check is a TypeScript rule script that runs read-only in the script sandbox. The same rules file, baseline and report format as Jira Audit apply, and you can add or replace rules: see Custom Audit Rules.
Prerequisites
Section titled “Prerequisites”- The audit feature flag, shared with Jira Audit:
atlcli flag set audit true --global - A directory set up with
atlcli wiki docs initand pulled withatlcli wiki docs pull(see Sync) - A profile for
--fix,--include-remote,--refresh-usersandcontent/restricted; the other rules work offline
-
Pull, then audit with the built-in rules:
Terminal window cd docsatlcli wiki docs pullatlcli audit wikiWarnings and errors are listed, info findings are counted as quiet.
-
Make inactive contributors known (reads user status from Confluence):
Terminal window atlcli audit wiki --refresh-users -
Accept what is there today, so that only new findings show up:
Terminal window atlcli audit wiki --update-baseline -
Adjust the rules in the
confluence:section of.atlcli/audit-rules.yaml(see Configuration). -
Review and apply the proposed fixes:
Terminal window atlcli audit wiki --fix # shows the plan, changes nothingatlcli audit wiki --fix --apply # asks, then applies
Each rule has an id <category>/<name>. --list-rules lists them with their source; --print-rule <id> prints a rule’s script. The older check flags still select rules (last column).
| Rule | minimal / default / strict |
Finds | Older flag |
|---|---|---|---|
content/stale |
error 365 d / info 90 d, warning 180 d, error 365 d / as default | Not edited for that long | --stale-high/-medium/-low <months> |
links/orphan |
off / warning / warning | No incoming links and no parent | --orphans |
links/broken |
warning / warning / error | Broken internal links, one finding per link | --broken-links |
links/external |
off | Inventory of external URLs | --external-links |
links/external-broken |
off | External URLs that fail over HTTP | --check-external |
people/single-contributor |
off / info / warning | Only one contributor | --single-contributor |
people/inactive-contributors |
off / warning / warning | No active contributor left | --inactive-contributors |
content/missing-label |
off | A required label is missing (option label) |
--missing-label <label> |
content/restricted |
off / info / info | View or edit restrictions | --restricted |
content/draft |
off / info / warning | Unpublished drafts | --drafts |
content/archived |
off / info / info | Archived pages | --archived |
content/high-churn |
off | Many versions (thresholds count versions) | --high-churn <N> |
structure/folders |
warning | Empty folders, folders without index.md |
--folders |
content/unsynced |
off | In Confluence but not synced | --include-remote |
Selecting checks
Section titled “Selecting checks”- No check flag: the rules file’s
confluence:section, else the built-in profiledefault.audit.defaultChecksin~/.atlcli/config.json, if set, acts like the check flags. - Older check flags (
--orphans,--stale-high 12, …): only the named rules run, and every finding is listed (min level info).--stale-*months count as 30 days each and map to error (high), warning (medium) and info (low). --all: stale, orphans, broken links, both contributor rules, folders, restricted, drafts and archived, listed at min level info. The external-link rules are not included.--checks <list>: narrows the active rules to rule ids, categories (content,links,people,structure) or the older names. It turns rules off, never on.
links/external-broken makes an HTTP request to every external URL (5 at a time, 10 s timeout). content/unsynced and --include-remote list the space’s pages through the API.
content/restricted reads each page’s restrictions live, one request per page (8 at a time), because the sync database holds none. It needs a profile; without one the report is marked partial (restrictions (no profile to read them)). For who may view and edit across spaces, see Access Audit.
Three built-in rules propose fixes. --fix runs them for the listed findings only (not for quiet, legacy or baseline findings) and shows the plan:
| Rule | Fix |
|---|---|
content/stale |
Archives pages not edited for 730 days or more; adds a label to other error-level pages (--fix-label, or the rule option label; default needs-review) |
links/orphan |
Moves orphans not edited for a year to the trash |
content/missing-label |
Adds the missing labels |
atlcli audit wiki --fix # the planatlcli audit wiki --fix --apply # apply after a question (--yes skips it)atlcli audit wiki --fix --plan-out fixes.json # review, then: atlcli script apply fixes.jsonOnly synced pages are fixed. Confluence archives in the background, so an archived page can read as current for a moment. Deleting moves the page to the space trash.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
[dir], --dir |
path | . |
Synced directory to audit |
--rules |
file path | first rules file found | Rules file; its confluence: section is used |
--checks |
comma-separated | all active rules | Only these rules (ids, categories, older names) |
--all |
flag | off | The older “all checks” set, see Selecting checks |
--stale-high, --stale-medium, --stale-low |
months | — | Stale thresholds for error / warning / info |
--orphans, --broken-links, --single-contributor, --inactive-contributors, --external-links, --check-external, --restricted, --drafts, --archived, --folders |
flag | off | Run that rule |
--missing-label |
label | — | Run content/missing-label for this label |
--high-churn |
versions | — | Run content/high-churn from this many versions |
--label |
label | — | Only pages with this label |
--under-page |
page id | — | Only pages under this ancestor |
--exclude-label |
label | — | Leave out pages with this label |
--include-remote |
flag | off | Also pages in Confluence that are not synced |
--min-level |
info, warning, error |
rules minLevel |
List findings from this level |
--fail-on |
info, warning, error, never |
error |
Exit 2 when a listed finding reaches it |
--update-baseline |
flag | off | Accept every current finding into the baseline |
--prune-baseline |
flag | off | Remove baseline entries no finding matches |
--no-baseline |
flag | off | Ignore the baseline |
--markdown |
flag | off | Markdown report (a Jinja template; --print-template prints it) |
--template |
file path | — | Render the report with your own Jinja template; other templates in its directory can be included |
--output |
file path | stdout | Write the report to this file, replaced only once complete; parent directories are created. See Report files and formats |
--format |
md, json, jsonl, csv |
from the --output extension, else the text report |
Report format; without --output it goes to stdout |
--report |
file path | — | Same as --format md --output <file> |
--report-script |
rule id | last rule with one | Build the report with this rule’s report export |
--json |
flag | off | JSON report (schemaVersion: "3") |
--script |
file path, repeatable | — | Add a rule script |
--list-rules |
flag | off | List the rules with their source |
--print-rule |
rule id | — | Print a rule’s script |
--fix |
flag | off | Plan the fixes of the listed findings |
--apply, --yes |
flag | off | With --fix: apply the plan; --yes skips the question |
--plan-out |
file path | — | With --fix: save the plan for atlcli script apply |
--fix-label |
label | needs-review |
Label content/stale adds |
Maintenance options
Section titled “Maintenance options”| Option | Description |
|---|---|
--refresh-users |
Refresh users’ active/inactive status from the API (needs a profile) |
--rebuild-graph |
Re-extract links from the synced Markdown files |
--export-graph |
Print the link graph as JSON |
Run alone, --refresh-users and --rebuild-graph do only their job; add a check flag or --checks to audit in the same run.
Configuration
Section titled “Configuration”Rules come from the confluence: section of the first rules file found: --rules, ./.atlcli/audit-rules.yaml, ~/.atlcli/profiles/<profile>/audit-rules.yaml, ~/.atlcli/audit-rules.yaml. The grammar is the one of Audit Rules; a Jira section in the same file is independent.
confluence: extends: default # minimal | default | strict minLevel: warning rules: content/stale: { warning: 120d, error: 365d } content/missing-label: { severity: warning, label: [owner] } people/single-contributor: off scripts: - audit/owner-label.ts # relative to this file| Key | Type | Default | Description |
|---|---|---|---|
extends |
minimal, default, strict |
default |
Built-in profile to start from |
minLevel |
info, warning, error |
from the profile | Findings below are quiet |
rules |
map rule id → setting | — | Changes to the profile |
scripts |
list of paths | [] |
Rule scripts to load |
Baseline: .atlcli/audit-baseline.json in the synced directory, so it belongs to the space and works without a profile. Wiki audits have no legacy cutoff: old pages are what the stale rule is for.
Older settings in ~/.atlcli/config.json still work when no check flag is given:
{ "audit": { "defaultChecks": ["stale", "orphans", "broken-links"], "staleThresholds": { "high": 12, "medium": 6, "low": 3 } }}| Key | Type | Description |
|---|---|---|
defaultChecks |
list of older check names | Rules to run, like the check flags |
staleThresholds.high / .medium / .low |
months | Stale thresholds for error / warning / info |
Report files and formats
Section titled “Report files and formats”--output <file> writes the report to a file. The file is replaced only once the report is complete, so a failed run keeps the old file. stderr says Report written to <file> (<size>); with --json, stdout still gets the JSON report.
| Format | Content | Chosen by |
|---|---|---|
md |
Markdown, see below | --format md, --report <file>, or an --output file with any other extension |
json |
Exactly the --json report (schemaVersion: "3") |
--format json or .json |
jsonl |
One finding per line (JSON Lines) | --format jsonl, .jsonl or .ndjson |
csv |
One finding per row: severity,rule,key,summary,status,assignee,detail,ageDays,url; RFC 4180 quoting, CRLF. key is the page id, assignee the last editor |
--format csv or .csv |
The Markdown report comes from the first of:
--template <file>: your Jinja template. Other templates in its directory (.j2,.jinja,.md,.txt,.html) can be included.- A rule’s
reportexport:--report-script <id>, or the last rule that has one (see Custom Audit Rules). - The default template (
--markdown;--print-templateprints it).
--template, --markdown and --report-script only make Markdown: combining them with --format json, jsonl or csv is a usage error.
Reports stream: no format is built as one string in memory. Templates render in chunks and JSON is written one finding at a time. The findings themselves stay in memory for --fail-on and the baseline. A template error fails the audit; see Templating: streamed rendering.
# Minimal: a spreadsheet of the findingsatlcli audit wiki --output audit.csv
# Realistic: your team's Markdown report as a file, the findings as JSON Lines for a dashboardatlcli audit wiki --label documentation --template team.md.j2 --output reports/wiki-audit.mdatlcli audit wiki --label documentation --format jsonl > findings.jsonlExamples
Section titled “Examples”Minimal:
atlcli audit wikiWiki audit: 31 finding(s) — 0 error, 31 warning, 0 infoScope: space DOCSYRules: built-in profile default (extends default, min level warning)
Stale pages — content/stale (30) [warning] 623870077 Neue Seite zum Syncen (Ann) — Not edited for 9 months (last 2026-01-10). ...
Orphaned pages — links/orphan (1) [warning] 623936140 Scratch notes (Ann) — No incoming links and no parent.
Quiet: 188 finding(s) below warning (content/stale 68, people/single-contributor 120); --min-level info lists them.Advanced: one label, a strict profile, JSON for a dashboard, no exit code:
atlcli audit wiki --label documentation --rules team-rules.yaml --fail-on never --json{ "schemaVersion": "3", "product": "confluence", "generatedAt": "2026-10-25T21:26:21.028Z", "query": "space DOCSY label documentation", "rules": { "source": "/work/docs/team-rules.yaml", "extends": "strict", "minLevel": "info" }, "summary": { "total": 1, "bySeverity": { "error": 0, "warning": 1, "info": 0 }, "byRule": { "content/stale": 1 }, "suppressed": { "quiet": 0, "baseline": 0, "legacy": 0 } }, "findings": [ { "rule": "content/stale", "category": "content", "severity": "warning", "target": { "kind": "page", "key": "623870077", "url": "https://example.atlassian.net/wiki/pages/viewpage.action?pageId=623870077" }, "key": "623870077", "summary": "Neue Seite zum Syncen", "status": "current", "assignee": "Ann", "detail": "Not edited for 9 months (last 2026-01-10).", "ageDays": 288, "subject": "623870077", "fixable": true } ], "quiet": {}, "legacy": {}, "legacyDays": null, "staleBaselineEntries": 0, "partial": [], "diagnostics": [], "titles": { "content/stale": "Stale pages" }}assignee is the page’s last editor. subject is what the baseline records.
CI: fail on warnings in links, write the report as an artifact:
atlcli audit wiki --checks links --fail-on warning --report audit.mdEditor format
Section titled “Editor format”The docs status command reports on Confluence editor formats:
# View editor format breakdownatlcli wiki docs status ./docsOutput includes:
Editor format: new editor (v2): 25 pages legacy editor (v1): 3 pages unknown: 2 pages
Legacy/unknown editor pages: old-page.md imported-doc.md| Format | Description |
|---|---|
| v2 (new editor) | Pages using Confluence’s new editor with colored callouts |
| v1 (legacy) | Pages explicitly set to legacy editor (grey callouts) |
| unknown | Pages without editor property (typically older pages) |
Converting editor formats
Section titled “Converting editor formats”Convert pages between editor formats:
# Convert a single page to new editoratlcli wiki docs convert ./docs/old-page.md --to-new-editor
# Convert all pages in directory to new editor (preview first)atlcli wiki docs convert ./docs --to-new-editor --dry-run
# Apply conversion to all pagesatlcli wiki docs convert ./docs --to-new-editor --confirm
# Convert back to legacy editor if neededatlcli wiki docs convert ./docs/page.md --to-legacy-editor| Flag | Description |
|---|---|
--to-new-editor |
Convert to v2 (new editor) |
--to-legacy-editor |
Convert to v1 (legacy editor) |
--dry-run |
Preview changes without applying |
--confirm |
Required for directory-wide conversion |
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
| 0 | The audit ran; no listed finding reached --fail-on |
| 1 | No sync directory, a request failed, or the arguments or rules file were wrong |
| 2 | A listed finding reached --fail-on (default: an error) |
Troubleshooting
Section titled “Troubleshooting”--template, --markdown and --report-script make a Markdown report; drop --format or use --format md.
These options only produce Markdown. Drop --format, or write the other format in a second run.
No .atlcli directory found.
Run atlcli wiki docs init <dir> --space KEY and atlcli wiki docs pull first, or pass the directory.
User status never checked.
people/inactive-contributors needs user status. Run atlcli audit wiki --refresh-users.
The audit exits 2 in a script that used to pass.
Error-level findings now fail the run. Pass --fail-on never, or lower the rule in the rules file.
--fix fails with No active profile.
Fixes and rules that read through atlcli.* need a profile: --profile <name> or atlcli auth login.
An archived page still shows as current. Confluence archives in the background. Pull again after a moment.
External link checks take long.
Each URL gets one request, 5 at a time with a 10-second timeout. Leave --check-external for scheduled runs.
Partial: restrictions (no profile to read them) hit a limit.
content/restricted reads restrictions through the API. Log in (atlcli auth login) or pass --profile <name>.
Stale pages look wrong.
The audit reads lastModified from the last pull. Pull first.
Related topics
Section titled “Related topics”- Custom Audit Rules
- Audit Rules: rules-file grammar
- Jira Audit
- Access Audit
- Folders
- Sync and Storage
- Configuration