---
name: dld-audit-auto
description: Autonomous audit — detects drift, fixes issues, and opens a PR. Designed for scheduled/CI execution without human interaction.
compatibility: Requires Node.js 22+ and git.
metadata:
  dld-kit-version: "1.0.0"
---
<!-- Generated by dld-kit from templates/skills/dld-audit-auto/SKILL.md. Do not edit: updating the skills overwrites this file. -->

# /dld-audit-auto — Autonomous Audit & Fix

You are running an autonomous audit of the decision-code relationship. Unlike the interactive `/dld-audit`, you **detect issues and fix them** without asking for human input. Your output is a branch with fixes and a PR for review.

**This skill is designed for unattended execution** — scheduled runs, CI pipelines, or autonomous agent sessions. Do not ask questions or wait for user input.

## Commands

The commands below run the `dld` CLI bundled with the dld-common skill, and need Node.js 22+. `<skill-dir>` stands for the absolute path of this skill's directory. If `<skill-dir>/../dld-common/scripts/dld.mjs` does not exist, stop and tell the user to install the dld-common skill: `npx skills add jimutt/dld-kit --skill dld-common`.

This skill uses: `find-annotations`, `find-missing-amends`, `update-status`, `check-decision-edits`, `restore-decision-prose`, `regenerate-index`, `update-audit-state`.

## Prerequisites

1. Check that `dld.config.yaml` exists at the repo root. If not, exit with a clear error message.
2. Verify the working tree is clean (`git status --porcelain` should be empty). If not, exit — do not mix audit fixes with uncommitted work.

## Step 1: Create audit branch

```bash
git checkout -b dld/audit-$(date +%Y%m%d-%H%M%S)
```

## Step 2: Run the full audit

Perform all the same checks as `/dld-audit`:

### a) Collect annotations and decision records

```bash
node "<skill-dir>/../dld-common/scripts/dld.mjs" find-annotations
```

Its stderr also lists `Warning: <file>:<line>: not an annotation: <text>` for malformed annotations; keep those for check (7). Annotations use the `annotation_prefix` from `dld.config.yaml` (`@decision` by default).

Read all `DL-*.md` files in the records subdirectory (`decisions/records/`). Note each decision's ID, status, and code references.

### b) Detect issues

Check for all drift categories:

1. **Orphaned annotations** — annotations referencing non-existent decision IDs
2. **Annotations referencing non-accepted decisions** — code tied to deprecated or superseded decisions
3. **Stale references in decisions** — frontmatter references to files that no longer exist
4. **Unreferenced code changes** — annotated files modified since last audit (if previous audit state exists)
5. **Decisions without annotations** — accepted decisions with code references but no corresponding annotations in code
6. **Missing amendment relationships** — decisions whose body says they amend or supersede another decision that their frontmatter doesn't list
7. **Malformed annotations** — the `not an annotation` warnings from `find-annotations`

For check (4), if `decisions/.dld-state.yaml` exists with an `audit.commit_hash`, first verify reachability:
```bash
git cat-file -t <commit_hash> 2>/dev/null
```
If unreachable (e.g., after rebase or shallow clone), skip check (4) and note it in the PR description. If reachable, use `git diff --name-only <commit_hash>..HEAD` to find changed files and cross-reference with annotated files.

## Step 3: Fix issues

Apply fixes for each issue category. Use judgment on what can be safely fixed automatically vs. what should only be flagged in the PR description.

### Hard rule: never edit the body of an integrated decision

The narrative body of an accepted decision that is already on the base branch (Context, Decision, Rationale, Consequences, and any other prose sections) is **immutable**. An audit must never rewrite, refine, "clean up", or otherwise modify it. This holds whatever `decision_edits` in `dld.config.yaml` says: an unattended audit has nobody to ask.

The only changes an audit may make to an accepted decision are housekeeping updates to specific frontmatter fields — `references`, `amends`, `supersedes` and `status` — plus adding or fixing `@decision` annotations in code.

If drift suggests the substance of an accepted decision is no longer accurate, or that code has evolved beyond what the decision describes, **do not modify the original record**. Instead, create a **new decision** (status `accepted`, since the code already exists) that captures the new rationale and uses `supersedes` or `amends` to link back to the prior one. The original stays as-is to preserve the historical record of why things were done that way at the time.

### Auto-fixable (apply these directly):

**Orphaned annotations** — If the annotation has an obvious typo (e.g., `DL-01` where `DL-001` exists), fix it. If the referenced ID is completely unknown, remove the annotation and note the removal in the commit message.

**Stale references in decisions** — Remove file paths from the decision's `references` field that no longer exist. If the file was renamed/moved (detectable via `git log --follow --diff-filter=R`), update the path instead of removing. Keep any frontmatter fields you don't recognise.

**Annotations referencing superseded decisions** — Update the annotation to reference the superseding decision (read the `supersedes` field of the newer decision to find the chain). For deprecated decisions, remove the annotation.

**Annotations referencing amended decisions** — Do **not** rewrite or remove these annotations. The original decision is still active. Instead, note the amendment relationship in the PR description so reviewers can verify the code aligns with the amendment.

**Missing amendment relationships** — Run `node "<skill-dir>/../dld-common/scripts/dld.mjs" find-missing-amends`. Each line is `<source-id>:<referenced-id><TAB><tier>`, and only decisions changed since the last audit are checked, so re-runs stay focused on new work.

- `explicit`: the source decision's body says it amends or supersedes the referenced one, but the frontmatter doesn't list it. Read the sentence first: if it is negated ("never supersedes") or is about another decision, change nothing and list it under **Possible Missing Relationships**. Otherwise add the ID to `amends` or `supersedes`, whichever the sentence says; for `supersedes`, also run `node "<skill-dir>/../dld-common/scripts/dld.mjs" update-status DL-NNN superseded` on the referenced decision. List each fix under **Relationships Added — Review Required** in the PR.
- `cue`: a sentence in the Decision or Consequences section mentions the referenced decision with a change word. Change nothing. List these in the PR under **Possible Missing Relationships** for a reviewer to judge.

**Malformed annotations** — In code comments, rewrite each into valid annotations: one ID per annotation, exactly `<prefix>(DL-NNN)`, in canonical ID form (two IDs in one pair of parentheses become two annotations). If an ID does not exist, treat it as an orphaned annotation. In prose or example files (documentation, skill files, fixtures), where the wrong syntax may be deliberate, do not rewrite: list them in the PR description and suggest adding the paths to `annotation_exclude`.

**Decisions without annotations** — If an accepted decision has code references but no annotations, and the referenced files exist, add the missing `@decision(DL-NNN)` annotations to the referenced code locations.

### Best-effort fixes (apply, but flag prominently for review):

**Unreferenced code changes** — Annotated files that were modified since the last audit without a corresponding decision update. These represent changes made outside the DLD process. Try to resolve them:

1. **Check commit messages** — Read the commits that modified the file (`git log --oneline <last_audit_hash>..HEAD -- <file>`). Look for context about what changed and why.
2. **Look up referenced tickets** — If commit messages reference a ticket (e.g., Jira, Linear, GitHub issue), try to look it up using available tools (MCP servers, `gh` CLI, etc.) to gather additional context about the change.
3. **Assess the impact** — Read the current code and the associated decision(s). Determine whether:
   - The existing decision is still accurate (the change was compatible — no action needed)
   - The change introduced new or different behavior/rationale that is not fully captured by the existing decision (a **new decision** is needed)
4. **Create a new decision when needed** — Per the hard rule above, never edit the body of the existing accepted decision to absorb the new behavior. Instead, create a new decision using the standard scripts, mark it `accepted` (the code already exists), and link it to the prior decision via `supersedes` or `amends` as appropriate. Add `@decision` annotations to the new/changed code pointing at the new decision.

**Important:** These best-effort fixes are inferred, not authoritative. In the PR description, clearly mark each one under a **"Inferred Decisions — Review Required"** section. Explain what changed, what context was found (commit messages, tickets), and what new decision was created. Reviewers should prioritize checking these for accuracy.

### Check: no prose edits to decisions on the base branch

Before regenerating the index, confirm the hard rule held:

```bash
node "<skill-dir>/../dld-common/scripts/dld.mjs" check-decision-edits --uncommitted
```

If it lists any decision (exit code 3 under the default `decision_edits: block`; under other settings it still prints the list), put the prose back with `node "<skill-dir>/../dld-common/scripts/dld.mjs" restore-decision-prose --uncommitted DL-NNN ...`, which also restores `id` and `timestamp` and keeps other frontmatter changes. If the change was needed, record a new decision as described above instead.

## Step 4: Regenerate INDEX.md

If any decision records were modified:
```bash
node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index
```

## Step 5: Update audit state

```bash
node "<skill-dir>/../dld-common/scripts/dld.mjs" update-audit-state
```

## Step 6: Commit, push, and open PR

If there are changes (fixes were applied or audit state was updated):

```bash
git add -A
```

Commit with a descriptive message summarizing the fixes:

```bash
git commit -m "fix: resolve DLD audit drift

- [list each fix applied]
- Audit state updated"
```

Push and open a PR:

```bash
git push -u origin HEAD
```

Create a PR with the following structure:

```markdown
## DLD Audit — Automated Drift Fixes

**Audit date:** YYYY-MM-DD
**Issues found:** N
**Auto-fixed:** M
**Needs review:** K

### Fixes Applied

- [Description of each fix, grouped by category]

### Inferred Decisions — Review Required

These decisions were created or updated based on code changes made outside the DLD process. The rationale was inferred from commit messages and available ticket context. **Please verify accuracy.**

- **DL-NNN: [Title]** (new/updated) — [What changed in the code, what context was found, what was inferred]

### Relationships Added — Review Required

- **DL-NNN** now amends (or supersedes) **DL-XXX** — its body says "[the sentence]"

### Possible Missing Relationships

- **DL-NNN** → DL-XXX — "[the sentence]"

### Audit Summary

| Check | Result |
|-------|--------|
| Orphaned annotations | N found, M fixed |
| Stale references | N found, M fixed |
| Superseded/deprecated refs | N found, M fixed |
| Missing annotations | N found, M added |
| Missing relationships | N found, M added, K possible |
| Modified annotated files | N flagged for review |
```

If no issues were found (only audit state updated), still commit and push the state update but **do not open a PR**. Instead, exit with a message:

```
Audit complete. No drift detected. Audit state updated.
```
