diff --git a/.agents/hooks/enforce-git-hygiene.sh b/.agents/hooks/enforce-git-hygiene.sh new file mode 100755 index 0000000000..d125e2835b --- /dev/null +++ b/.agents/hooks/enforce-git-hygiene.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# PreToolUse hook for the Bash tool. +# Blocks dangerous git patterns that cause CI failures. +set -euo pipefail + +input=$(cat) +command=$(echo "$input" | jq -r '.tool_input.command // empty') + +if [ -z "$command" ]; then + exit 0 +fi + +# Block 'git add .' / 'git add -A' / 'git add --all' +# These stage package-lock.json and other generated files. +if echo "$command" | grep -qE 'git\s+add\s+(\.|--all|-A)(\s|$|;)'; then + echo "BLOCKED: do not use 'git add .' / 'git add -A' / 'git add --all'. Stage files explicitly by name." >&2 + exit 2 +fi + +exit 0 diff --git a/.agents/hooks/enforce-vendored.sh b/.agents/hooks/enforce-vendored.sh new file mode 100755 index 0000000000..d7404d7c66 --- /dev/null +++ b/.agents/hooks/enforce-vendored.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# PreToolUse hook for Edit and Write tools. +# Blocks edits to vendored content that must be fixed upstream. +set -euo pipefail + +input=$(cat) +file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty') + +if [ -z "$file_path" ]; then + exit 0 +fi + +# Block edits to vendored Hugo modules. +if echo "$file_path" | grep -qE '/_vendor/'; then + echo "BLOCKED: _vendor/ is vendored from upstream Hugo modules. Fix in the source repo instead." >&2 + exit 2 +fi + +# Block edits to vendored CLI reference data. +if echo "$file_path" | grep -qE '/data/cli/'; then + echo "BLOCKED: data/cli/ is generated from upstream repos (docker/cli, docker/buildx, etc.). Fix in the source repo instead." >&2 + exit 2 +fi + +exit 0 diff --git a/.agents/settings.json b/.agents/settings.json new file mode 100644 index 0000000000..c4ca548159 --- /dev/null +++ b/.agents/settings.json @@ -0,0 +1,26 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "bash .agents/hooks/enforce-vendored.sh" + } + ], + "description": "Block edits to vendored content (_vendor/, data/cli/)" + }, + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "bash .agents/hooks/enforce-git-hygiene.sh" + } + ], + "description": "Block git add . and dangerous patterns" + } + ] + } +} diff --git a/.agents/skills/check-pr/SKILL.md b/.agents/skills/check-pr/SKILL.md new file mode 100644 index 0000000000..e0c9e5922a --- /dev/null +++ b/.agents/skills/check-pr/SKILL.md @@ -0,0 +1,90 @@ +--- +name: check-pr +description: > + Check a single PR's CI status, review comments, and requested changes. + Fix actionable failures and address feedback. "check PR 1234", "what's + the status of my PR", "address review comments on #500". +argument-hint: "" +context: fork +--- + +# Check PR + +Do one pass over PR **$ARGUMENTS**: check CI, read reviews, fix what's +actionable, report status. + +## 1. Gather PR state + +```bash +# Overall state +gh pr view $ARGUMENTS --repo docker/docs --json state,title,url,headRefName + +# CI checks +gh pr checks $ARGUMENTS --repo docker/docs --json name,state,detailsUrl + +# Top-level reviews +gh pr view $ARGUMENTS --repo docker/docs --json reviews,reviewDecision + +# Inline (line-level) comments — NOT included in the above +gh api repos/docker/docs/pulls/$ARGUMENTS/comments \ + --jq '[.[] | {id: .id, author: .user.login, body: .body, path: .path, line: .line}]' +``` + +Always check both the reviews endpoint and the inline comments endpoint. +A review with an empty body may still have line-level comments requiring +action. + +## 2. If merged + +Report the final state. No further action needed. + +## 3. If closed without merge + +Read the closing context to understand why: + +```bash +gh pr view $ARGUMENTS --repo docker/docs --json closedAt,comments \ + --jq '{closedAt, lastComment: .comments[-1].body}' +``` + +Report the reason. Common causes: rejected by maintainers, superseded by +another PR, closed by automation. + +## 4. If CI is failing + +- Read the failure details (follow `detailsUrl` if needed) +- Determine if the failure is in the PR's changed files or pre-existing +- **Actionable:** check out the branch, fix, commit, push + ```bash + git checkout + # fix the issue + git add + git commit -m "fix: " + git push + ``` +- **Pre-existing / upstream:** note it, do not block + +## 5. If review comments or changes requested + +- Read each unresolved comment +- Address feedback in a follow-up commit +- Push, then reply to each comment explaining what was done: + ```bash + gh api repos/docker/docs/pulls/$ARGUMENTS/comments \ + --method POST \ + --field in_reply_to= \ + --field body="" + ``` +- End every comment with a `Generated by [Claude Code](https://claude.com/claude-code)` footer +- Re-request review if changes were requested + +## 6. Report + +``` +## PR #$ARGUMENTS: + +**State:** <open|merged|closed> +**CI:** <passing|failing|pending> +**Review:** <approved|changes requested|pending> +**Action taken:** <what was done, or "none needed"> +``` diff --git a/.agents/skills/create-pr/SKILL.md b/.agents/skills/create-pr/SKILL.md new file mode 100644 index 0000000000..8c62760965 --- /dev/null +++ b/.agents/skills/create-pr/SKILL.md @@ -0,0 +1,123 @@ +--- +name: create-pr +description: > + Push the current branch and create a pull request against docker/docs. + Use after changes are committed and reviewed. "create a PR", "submit the + fix", "open a pull request for this". +--- + +# Create PR + +Push the branch and create a properly structured pull request. + +## 1. Verify the branch + +```bash +git log --oneline main..HEAD # confirm commits exist +git diff --quiet # confirm no unstaged changes +``` + +## 2. Push the branch + +Confirm origin points to your fork, not upstream: + +```bash +git remote get-url origin +``` + +Then push: + +```bash +git push -u origin <branch-name> +``` + +## 3. Create the PR + +Derive the fork owner dynamically: + +```bash +FORK_OWNER=$(git remote get-url origin | sed -E 's|.*[:/]([^/]+)/[^/]+(\.git)?$|\1|') +``` + +```bash +gh pr create --repo docker/docs \ + --head "${FORK_OWNER}:<branch-name>" \ + --title "<concise summary under 70 chars>" \ + --body "$(cat <<'EOF' +## Summary + +<1-2 sentences: what was wrong and what was changed> + +Closes #NNNN + +Generated by [Claude Code](https://claude.com/claude-code) +EOF +)" +``` + +Keep the body short. Reviewers need to know what changed and why — nothing +else. + +### Optional: Learnings section + +If while working on this PR you discovered something non-obvious about the +repo — a convention not documented in AGENTS.md, a gotcha that tripped you +up, a pattern that should be codified — add a Learnings section to the PR +body: + +```markdown +## Learnings + +- <what you learned and why it matters> +``` + +Add this section between the Summary and the `Closes` line. Only include +learnings that would help future contributors avoid the same issue. Do not +include things already documented in AGENTS.md or STYLE.md. + +The weekly PR learnings scanner reads these sections to surface recurring +patterns for the team to codify. + +## 4. Apply labels and request review + +Use the Issues API for labels — `gh pr edit --add-label` silently fails: + +```bash +gh api repos/docker/docs/issues/<pr-number>/labels \ + --method POST \ + --field 'labels[]=status/review' +``` + +Request review: + +```bash +gh pr edit <pr-number> --repo docker/docs --add-reviewer docker/docs-team +``` + +Verify the reviewer was assigned: + +```bash +gh pr view <pr-number> --repo docker/docs --json reviewRequests \ + --jq '.reviewRequests[].slug' +``` + +If the team doesn't appear, use the API directly: + +```bash +gh api repos/docker/docs/pulls/<pr-number>/requested_reviewers \ + --method POST --field 'team_reviewers[]=docs-team' +``` + +## 5. Report + +Print the PR URL and current CI state: + +```bash +gh pr view <pr-number> --repo docker/docs --json url,state +gh pr checks <pr-number> --repo docker/docs --json name,state +``` + +## Notes + +- Always use `Closes #NNNN` (not "Fixes") for GitHub auto-close linkage +- One issue, one branch, one PR — never combine diff --git a/.agents/skills/fix-issue/SKILL.md b/.agents/skills/fix-issue/SKILL.md new file mode 100644 index 0000000000..2767b5b121 --- /dev/null +++ b/.agents/skills/fix-issue/SKILL.md @@ -0,0 +1,83 @@ +--- +name: fix-issue +description: > + Fix a single GitHub issue end-to-end: triage, research, write the fix, + review, and create a PR. Use when asked to fix an issue: "fix issue 1234", + "resolve #500", "create a PR for issue 200". +argument-hint: "<issue-number>" +--- + +# Fix Issue + +Given GitHub issue **$ARGUMENTS**, decide what to do with it and either +close it or fix it. This skill orchestrates the composable skills — it owns +the decision tree, not the individual steps. + +## 1. Triage + +Invoke `/triage-issue $ARGUMENTS` to understand the issue and decide what +to do. This runs in a forked subagent and returns a verdict. + +## 2. Act on the triage result + +If triage says **close it** — comment with the reason and close: +```bash +gh issue close $ARGUMENTS --repo docker/docs \ + --comment "<one sentence explaining why> + +Generated by [Claude Code](https://claude.com/claude-code)" +``` +Done. + +If triage says **escalate upstream** — comment noting the repo and stop: +```bash +gh issue comment $ARGUMENTS --repo docker/docs \ + --body "This needs to be fixed in <upstream-repo>. + +Generated by [Claude Code](https://claude.com/claude-code)" +``` +Done. + +If triage says **leave it open** — comment explaining what was checked and +what's unclear. Do not close. +Done. + +End every issue comment with a `Generated by [Claude Code](https://claude.com/claude-code)` footer. + +If triage says **fix it** — proceed to step 3. + +## 3. Research + +Invoke `/research` to locate affected files, verify facts, and identify +the fix. The issue context carries over from triage. This runs inline — +findings stay in conversation context for the write step. + +If research reveals the issue is upstream or cannot be fixed (e.g. +unverifiable URLs), comment on the issue and stop. + +## 4. Write + +Invoke `/write` to create a branch, make the change, format, self-review, +and commit. + +## 5. Review + +Invoke `/review-changes` to check the diff for correctness, coherence, and +mechanical compliance. This runs in a forked subagent with fresh context. + +If issues are found, fix them and re-review until clean. + +## 6. Create PR + +Invoke `/create-pr` to push the branch and open a pull request. + +## 7. Return to main + +```bash +git checkout main +``` + +## 8. Report + +Summarize what happened: the issue number, what was done (closed, escalated, +fixed with a PR link), and why — in a sentence or two. diff --git a/.agents/skills/research/SKILL.md b/.agents/skills/research/SKILL.md new file mode 100644 index 0000000000..db9f98c3ff --- /dev/null +++ b/.agents/skills/research/SKILL.md @@ -0,0 +1,93 @@ +--- +name: research +description: > + Research a documentation topic — locate affected files, understand the + problem, identify what to change. Use when investigating an issue, a + question, or a topic before writing a fix. Triggers on: "research issue + 1234", "investigate what needs changing for #500", "what files are + affected by #200", "where is X documented", "is our docs page about Y + accurate", "look into how we document Z". +--- + +# Research + +Thoroughly investigate the topic at hand and produce a clear plan for +the fix. The goal is to identify exact files, named targets within those +files, and the verified content needed for the fix. + +## 1. Gather context + +If the input is a GitHub issue number, fetch it: + +```bash +gh issue view <number> --repo docker/docs \ + --json number,title,body,labels,comments +``` + +Otherwise, work from what was provided — a description, a URL, a question, +or prior conversation context. Identify the topic, affected feature, or +page to investigate. + +## 2. Locate affected files + +Search `content/` using the URL or topic from the issue. Remember the +`/manuals` prefix mapping when converting URLs to file paths. + +For each candidate file, read the relevant section to confirm it contains +the reported problem. + +## 3. Check vendored ownership + +Before planning any edit, verify the file is editable locally: + +- `_vendor/` — read-only, vendored via Hugo modules +- `data/cli/` — read-only, generated from upstream YAML +- `content/reference/cli/` — read-only, generated from `data/cli/` +- Everything else in `content/` — editable + +If the fix requires upstream changes, identify the upstream repo and note +it as out of scope. See the vendored content table in CLAUDE.md. + +## 4. Find related content + +Look for pages that may need updating alongside the primary fix: + +- Pages that link to the affected content +- Include files (`content/includes/`) referenced by the page +- Related pages in the same section describing the same feature + +## 5. Verify facts + +If the issue makes a factual claim about how a feature behaves, verify it. +Follow external links, read upstream source, check release notes. Do not +plan a fix based on an unverified claim. + +If the fix requires a replacement URL and that URL cannot be verified (e.g. +network restrictions), report it as a blocker rather than guessing. + +## 6. Check the live site (if needed) + +For URL or rendering issues, fetch the live page: + +``` +https://docs.docker.com/<path>/ +``` + +## 7. Report findings + +Summarize what you found — files to change, the specific problem in each, +what the fix should be, and any constraints. This context feeds directly +into the write step. + +Be specific: name the file, the section or element within it, and the +verified content needed. "Fix the broken link in networking.md" is not +specific enough. "In `compose/networking.md`, the 'Custom networks' section, +remove the note about `driver_opts` being ignored — this was fixed in +Compose 2.24" is. + +## Notes + +- Research quality bounds write quality. Vague research produces broad + changes; precise research produces minimal ones. +- Do not create standalone research files — findings stay in conversation + context for the write step. diff --git a/.agents/skills/review-changes/SKILL.md b/.agents/skills/review-changes/SKILL.md new file mode 100644 index 0000000000..f147883a44 --- /dev/null +++ b/.agents/skills/review-changes/SKILL.md @@ -0,0 +1,107 @@ +--- +name: review-changes +description: > + Review uncommitted or recently committed documentation changes for + correctness, coherence, and style compliance. Use before creating a PR + to catch issues. "review my changes", "review the diff", "check the fix + before submitting", "does this look right". +context: fork +model: opus +--- + +# Review Changes + +Evaluate whether the changes correctly and completely solve the stated +problem, without introducing new issues. Start with no assumptions — the +change may contain mistakes. Your job is to catch what the writer missed, +not to rubber-stamp the diff. + +## 1. Identify what changed + +Determine the scope of changes to review: + +```bash +# Uncommitted changes +git diff --name-only + +# Last commit +git diff --name-only HEAD~1 + +# Entire branch vs main +git diff --name-only main...HEAD +``` + +Pick the right comparison for what's being reviewed. If reviewing a branch, +use `main...HEAD` to see all changes since the branch diverged. + +## 2. Read each changed file in full + +Do not just read the diff. For every changed file, read the entire file to +understand the full context the change lives in. A diff can look correct in +isolation but contradict something earlier on the same page. + +Then read the diff for the detailed changes: + +```bash +# Adjust the comparison to match step 1 +git diff --unified=10 # uncommitted +git diff --unified=10 HEAD~1 # last commit +git diff --unified=10 main...HEAD # branch +``` + +## 3. Follow cross-references + +For each changed file, check what links to it and what it links to: + +- Search for other pages that reference the changed content (grep for the + filename, heading anchors, or key phrases) +- Read linked pages to verify the change doesn't create contradictions + across pages +- Check that anchor links in cross-references still match heading IDs + +A change that's correct on its own page can break the story told by a +related page. + +## 4. Verify factual accuracy + +Don't assume the change is factually correct just because it reads well. + +- If the change describes how a feature behaves, verify against upstream + docs or source code +- If the change includes a URL, check that it resolves +- If the change references a CLI flag, option, or API field, confirm it + exists + +## 5. Evaluate as a reader + +Consider someone landing on this page from a search result, with no prior +context: + +- Does the page make sense on its own? +- Is the changed section clear without having read the issue or diff? +- Would a reader be confused by anything the change introduces or leaves + out? + +## 6. Review code and template changes + +For non-Markdown changes (JS, HTML, CSS, Hugo templates): + +- Trace through the common execution path +- Trace through at least one edge case (no stored preference, Alpine fails + to load, first visit vs returning visitor) +- Ask whether the change could produce unexpected browser or runtime + behavior that no automated tool would catch + +## 7. Decision + +**Approve** if the change is correct, coherent, complete, and factually +accurate. + +**Request changes** if: +- The change does not correctly solve the stated problem +- There is a factual error or contradiction (on-page or cross-page) +- A cross-reference is broken or misleading +- A reader would be confused + +When requesting changes, be specific: quote the exact text that is wrong, +explain why, and suggest the correct fix. diff --git a/.claude/skills/testcontainers-guides-migrator/SKILL.md b/.agents/skills/testcontainers-guides-migrator/SKILL.md similarity index 100% rename from .claude/skills/testcontainers-guides-migrator/SKILL.md rename to .agents/skills/testcontainers-guides-migrator/SKILL.md diff --git a/.agents/skills/triage-issue/SKILL.md b/.agents/skills/triage-issue/SKILL.md new file mode 100644 index 0000000000..2f0465349d --- /dev/null +++ b/.agents/skills/triage-issue/SKILL.md @@ -0,0 +1,136 @@ +--- +name: triage-issue +description: > + Analyze a single GitHub issue for docker/docs — check whether the problem + still exists, determine a verdict, and report findings. Use when asked to + triage, assess, or review an issue, even if the user doesn't say "triage" + explicitly: "triage issue 1234", "is issue 500 still valid", "should we + close #200", "look at this issue", "what's going on with #200". +argument-hint: "<issue-number>" +context: fork +--- + +# Triage Issue + +Given GitHub issue **$ARGUMENTS** from docker/docs, figure out whether +it's still a real problem and say what should happen next. + +## 1. Fetch the issue + +```bash +gh issue view $ARGUMENTS --repo docker/docs \ + --json number,title,body,state,labels,createdAt,updatedAt,closedAt,assignees,author,comments +``` + +## 2. Understand the problem + +Read the issue body and all comments. Identify: + +- What is the reported problem? +- What content, URL, or file does it reference? +- Are there linked PRs? Check whether they were merged or closed without merge. +- Has anyone already proposed a fix or workaround in the comments? + +## 3. Follow URLs + +Find all `docs.docker.com` URLs in the issue body and comments. For each: + +- Fetch the URL to check if it still exists (404 = content removed or moved) +- Check whether the content still contains the problem described +- Note when the page was last updated relative to when the issue was filed + +For non-docs URLs (GitHub links, external references), fetch them too if +they are central to understanding the issue. + +## 4. Check the repository + +If the issue references specific files, content sections, or code: + +- Find and read the current version of that content +- Check whether the problem has been fixed, content moved, or file removed +- Remember the `/manuals` prefix mapping when looking up files + +## 5. Check for upstream ownership + +If the issue is about content in `_vendor/` or `data/cli/`, it cannot be +fixed here. Identify which upstream repo owns it (see the vendored content +table in CLAUDE.md). + +## 6. Decide and act + +After investigating, pick one of these verdicts and take the corresponding +action on the issue: + +- **Close it** — the problem is already fixed, the content no longer exists, + or the issue is too outdated to be useful. Close the issue with a comment + explaining why: + + ```bash + gh issue close $ARGUMENTS --repo docker/docs \ + --comment "Closing: <one-sentence reason>" + ``` + +- **Fix it** — the problem is real and fixable in this repo. Name the + file(s) and what needs to change. Label the issue `status/confirmed` and + remove `status/triage` if present: + + ```bash + gh api repos/docker/docs/issues/$ARGUMENTS/labels \ + --method POST --field 'labels[]=status/confirmed' + gh api repos/docker/docs/issues/$ARGUMENTS/labels/status%2Ftriage \ + --method DELETE || true + ``` + +- **Escalate upstream** — the problem is real but lives in vendored content. + Name the upstream repo. Label the issue `status/upstream` and remove + `status/triage` if present: + + ```bash + gh api repos/docker/docs/issues/$ARGUMENTS/labels \ + --method POST --field 'labels[]=status/upstream' + gh api repos/docker/docs/issues/$ARGUMENTS/labels/status%2Ftriage \ + --method DELETE || true + ``` + +- **Leave it open** — you can't determine the current state, or the issue + needs human judgment. Label the issue `status/needs-analysis`: + + ```bash + gh api repos/docker/docs/issues/$ARGUMENTS/labels \ + --method POST --field 'labels[]=status/needs-analysis' + gh api repos/docker/docs/issues/$ARGUMENTS/labels/status%2Ftriage \ + --method DELETE || true + ``` + +Don't overthink the classification. An old issue isn't stale if the problem +still exists. An upstream issue is still valid — it's just not fixable here. + +Also apply the most relevant `area/` label based on the content affected. +Available area labels: `area/accounts`, `area/admin`, `area/ai`, +`area/api`, `area/billing`, `area/build`, `area/build-cloud`, `area/cli`, +`area/compose`, `area/compose-spec`, `area/config`, `area/contrib`, +`area/copilot`, `area/desktop`, `area/dhi`, `area/engine`, +`area/enterprise`, `area/extensions`, `area/get-started`, `area/guides`, +`area/hub`, `area/install`, `area/networking`, `area/offload`, +`area/release-notes`, `area/samples`, `area/scout`, `area/security`, +`area/storage`, `area/subscription`, `area/swarm`, `area/ux`. Pick one +(or at most two if the issue clearly spans areas). Skip if none fit. + +```bash +gh api repos/docker/docs/issues/$ARGUMENTS/labels \ + --method POST --field 'labels[]=area/<name>' +``` + +## 7. Report + +Write a short summary: what the issue reports, what you found, and what +should happen next. Reference the specific files, URLs, or PRs that support +your conclusion. Skip metadata fields — the issue itself has the dates and +labels. Mention the action you took (closed, labeled, etc.). + +## Notes + +- A merged PR linked to an issue is strong evidence the issue is fixed +- A closed-without-merge PR means the issue is likely still open +- Do not narrate your process — produce the final report +- End every issue comment with a `Generated by [Claude Code](https://claude.com/claude-code)` footer diff --git a/.agents/skills/write/SKILL.md b/.agents/skills/write/SKILL.md new file mode 100644 index 0000000000..24f11e2b34 --- /dev/null +++ b/.agents/skills/write/SKILL.md @@ -0,0 +1,87 @@ +--- +name: write +description: > + Write a documentation fix on a branch. Makes the minimal change, formats, + self-reviews, and commits. Use after research has identified what to change. + "write the fix", "make the changes", "implement the fix for #1234". +hooks: + PostToolUse: + - matcher: "Edit|Write" + hooks: + - type: command + command: "bash ${CLAUDE_SKILL_DIR}/scripts/post-edit.sh" +--- + +# Write + +Make the minimal change that resolves the issue. Research has already +identified what to change — this skill handles the edit, formatting, +self-review, and commit. + +## 1. Create a branch + +```bash +git checkout -b fix/issue-<number>-<short-desc> main +``` + +Use a short kebab-case description derived from the issue title (3-5 words). + +## 2. Read then edit + +Always read each file before modifying it. Make the minimal change that +fixes the issue. Do not improve surrounding content, add comments, or +address adjacent problems. + +Follow the writing guidelines in CLAUDE.md, STYLE.md, and COMPONENTS.md. + +## 3. Front matter check + +Every content page requires `title`, `description`, and `keywords` in its +front matter. If any are missing from a file you touch, add them. + +## 4. Validate + +Prettier runs automatically after each edit via the PostToolUse hook. +Run lint manually after all edits are complete: + +```bash +${CLAUDE_SKILL_DIR}/scripts/lint.sh <changed-files> +``` + +The lint script runs markdownlint and vale on only the files you pass it, +so the output is scoped to your changes. Fix any errors it reports. + +## 5. Self-review + +Re-read each changed file: right file, right lines, change is complete, +front matter is present. Run `git diff` and verify only intended changes +are present. + +## 6. Commit + +Stage only the changed files: + +```bash +git add <files> +git diff --cached --name-only # verify — no package-lock.json or other noise +git commit -m "$(cat <<'EOF' +docs: <short description under 72 chars> (fixes #NNNN) + +<What was wrong: one sentence citing the specific problem.> +<What was changed: one sentence describing the exact edit.> + +Co-Authored-By: Claude <noreply@anthropic.com> +EOF +)" +``` + +The commit body is mandatory. A reviewer reading only the commit should +understand the problem and the fix without opening the issue. + +## Notes + +- Never edit `_vendor/` or `data/cli/` — these are vendored +- If a file doesn't exist, check for renames: + `git log --all --full-history -- "**/filename.md"` +- If the fix requires a URL that cannot be verified, stop and report a + blocker rather than guessing diff --git a/.agents/skills/write/scripts/lint.sh b/.agents/skills/write/scripts/lint.sh new file mode 100755 index 0000000000..4ccb1fd202 --- /dev/null +++ b/.agents/skills/write/scripts/lint.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Run markdownlint and vale on specific files. +# Usage: .agents/skills/write/scripts/lint.sh <file> [file...] +# +# Designed for agent workflows — scoped output, no repo-wide noise. +# For full repo validation, use: docker buildx bake validate +set -uo pipefail + +if [ $# -eq 0 ]; then + echo "Usage: $0 <file> [file...]" >&2 + exit 1 +fi + +exit_code=0 + +echo "=== markdownlint ===" +if ! npx markdownlint-cli "$@" 2>&1; then + exit_code=1 +fi + +echo "" +echo "=== vale ===" +if ! vale "$@" 2>&1; then + exit_code=1 +fi + +exit $exit_code diff --git a/.agents/skills/write/scripts/post-edit.sh b/.agents/skills/write/scripts/post-edit.sh new file mode 100755 index 0000000000..d5cb809554 --- /dev/null +++ b/.agents/skills/write/scripts/post-edit.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# PostToolUse hook for Edit/Write in the write skill. +# Auto-formats Markdown files with prettier after each edit. +set -euo pipefail + +input=$(cat) +file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty') + +[ -z "$file_path" ] && exit 0 +[[ "$file_path" != *.md ]] && exit 0 + +npx prettier --write "$file_path" 2>/dev/null diff --git a/.claude b/.claude new file mode 120000 index 0000000000..c0ca468566 --- /dev/null +++ b/.claude @@ -0,0 +1 @@ +.agents \ No newline at end of file diff --git a/.claude/skills/fix-issues/SKILL.md b/.claude/skills/fix-issues/SKILL.md deleted file mode 100644 index 0f73f96632..0000000000 --- a/.claude/skills/fix-issues/SKILL.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -name: fix-issues -description: > - Fix one or more GitHub issues by creating branches, writing fixes, and opening PRs. - Use this skill whenever the user provides GitHub issue numbers and wants them fixed, - or says things like "fix issue 1234", "address these issues", "create PRs for issues - 1234 and 5678". Triggers on any request involving GitHub issue numbers paired with - fixing, addressing, resolving, or creating PRs. Also triggers for "fix #1234" shorthand. ---- - -# Fix Issues - -Given one or more GitHub issue numbers from the docker/docs repository, fix each -issue end-to-end: analyze, branch, fix, commit, push, and open a PR. - -## Workflow - -### 1. Fetch all issues in parallel - -Use `gh issue view <number> --repo docker/docs --json title,body,labels` for each -issue number. Launch all fetches in parallel since they're independent. - -### 2. Fix each issue sequentially - -Process each issue one at a time in the main context. Do NOT use background -subagents for this — they can't get interactive Bash permission approval, which -blocks all git operations. Sequential processing in the main context is faster -than agents that stall on permissions. - -For each issue: - -#### a. Analyze - -Read the issue body to understand: -- Which file(s) need changes -- What the problem is -- What the fix should be - -#### b. Create a branch - -```bash -git checkout -b fix/issue-<number>-<short-description> main -``` - -Use a short kebab-case description derived from the issue title (3-5 words max). - -#### c. Read and fix - -- Read each affected file before editing -- Make the minimal change that addresses the issue -- Don't over-engineer or add unrequested improvements -- Follow the repository's STYLE.md and COMPONENTS.md guidelines - -#### d. Format - -Run prettier on every changed file: - -```bash -npx prettier --write <file> -``` - -#### e. Self-review - -Re-read the changed file to verify: -- The fix addresses the issue correctly -- No unintended changes were introduced -- Formatting looks correct - -#### f. Commit - -Stage only the changed files (not `git add -A`). - -#### g. Push and create PR - -```bash -git push -u origin fix/issue-<number>-<short-description> -``` - -Then create the PR: - -```bash -gh pr create --repo docker/docs \ - --title "<Short summary matching commit>" \ - --body "$(cat <<'EOF' -## Summary - -- <1-3 bullet points describing what changed and why> - -Closes #<issue-number> - -🤖 Generated with [Claude Code](https://claude.com/claude-code) -EOF -)" -``` - -#### h. Label and assign reviewers - -```bash -gh pr edit <pr-number> --repo docker/docs \ - --add-label "status/review" \ - --add-reviewer docker/docs-team -``` - -### 3. Switch back to main - -After all issues are processed: - -```bash -git checkout main -``` - -### 4. Report results - -Present a summary table: - -| Issue | PR | Change | -|-------|-----|--------| -| #N | #M | Brief description | - -## Important notes - -- Always work from `main` as the base for each branch -- Each issue gets its own branch and PR — don't combine issues -- If an issue references a file that doesn't exist, check for renames or - reorganization before giving up (files move around in this repo) -- Validation commands (`docker buildx bake lint vale`) are available but slow; - only run them if the user asks or the changes are complex enough to warrant it diff --git a/.claude/skills/process-issues/SKILL.md b/.claude/skills/process-issues/SKILL.md deleted file mode 100644 index 2b2eca394c..0000000000 --- a/.claude/skills/process-issues/SKILL.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -name: process-issues -description: > - Process a batch of GitHub issues end-to-end: fetch unlabeled issues, triage - them, apply labels, fix the actionable ones, and babysit the resulting PRs. - Use this skill when the user wants to work through the issue backlog - autonomously — e.g. "process issues", "work through the backlog", "run the - issue pipeline". Accepts an optional batch size: "process issues 5". - Pairs well with /loop for continuous background processing. ---- - -# Process Issues - -Fetches a batch of unprocessed GitHub issues, triages each one, labels it, -fixes the actionable ones, and babysits the resulting PRs. - -## Arguments - -- Batch size (optional, default 10): number of issues to process per run. - E.g. "process issues 5" or "process issues --batch 5". - -## Labels - -These labels track agent processing state. Create any that don't exist yet -with `gh label create --repo docker/docs <name> --color <hex>`. - -| Label | Meaning | -|-------|---------| -| `agent/triaged` | Agent has analyzed this issue; verdict in a comment | -| `agent/fix` | Agent has opened a PR for this issue | -| `agent/skip` | Triaged; not actionable (STALE, UPSTREAM, INDETERMINATE, CLOSEABLE_FIXED) | - -## Workflow - -### 1. Resolve fork username and fetch a batch - -Get the authenticated user's GitHub login — don't hardcode it: - -```bash -FORK_USER=$(gh api user --jq '.login') -``` - -Fetch up to N open issues that have none of the `agent/*` labels: - -```bash -gh issue list --repo docker/docs \ - --state open \ - --limit <N> \ - --json number,title,labels \ - --jq '[.[] | select( - ([.labels[].name] | map(startswith("agent/")) | any) | not - )]' -``` - -If there are no unprocessed issues, report that the backlog is clear and stop. - -### 2. Triage each issue - -Follow the full **triage-issues** skill workflow for all fetched issues, -running fetches in parallel. Produce a verdict for each: -OPEN, CLOSEABLE_FIXED, UPSTREAM, INDETERMINATE, or STALE. - -### 3. Label each issue immediately after verdict - -Apply `agent/triaged` to every issue regardless of verdict — it means "we -looked at it." Then apply a second label based on the outcome: - -```bash -# All issues — mark as triaged -gh issue edit <number> --repo docker/docs --add-label "agent/triaged" - -# Non-actionable (STALE, UPSTREAM, INDETERMINATE) -gh issue edit <number> --repo docker/docs --add-label "agent/skip" - -# Already resolved (CLOSEABLE_FIXED) — close with explanation -gh issue close <number> --repo docker/docs \ - --comment "Closing — <one sentence explaining what resolved it>." - -# Actionable (OPEN, no existing PR) — no extra label yet; agent/fix applied after PR is created -``` - -Leave a comment on every issue summarising the verdict and reasoning in one -sentence. Do this immediately — don't batch it for the end. - -### 4. Fix actionable issues - -For each issue with verdict OPEN and no existing open PR, follow the -**fix-issues** skill workflow. - -Skip issues where: -- An open PR already exists -- Verdict is anything other than OPEN -- The fix requires changes to `_vendor/` or `data/cli/` (upstream owned) - -After the PR is created, apply `agent/fix` to the issue: - -```bash -gh issue edit <number> --repo docker/docs --add-label "agent/fix" -``` - -### 5. Babysit PRs - -After opening PRs, schedule a recurring check with `/loop` so babysitting -continues asynchronously after the batch summary is reported: - -``` -/loop 5m babysit PRs <#N, #M, …> in docker/docs — check for failing checks, -new review comments, and requested changes; investigate and fix anything that -needs attention; stop looping once all PRs are merged or closed -``` - -At each check, for every open PR: - -- **Failing checks**: investigate the failure, fix the cause, force-push an - updated commit to the branch via the GitHub API -- **Review comments**: read them, address the feedback, push an update, reply - to the comment -- **All clear**: note it and move on - -Don't just report status — act on anything that needs attention. - -### 6. Report results - -``` -## Batch summary - -Processed: <N> issues -PRs opened: <n> -Skipped: <n> (STALE: n, UPSTREAM: n, INDETERMINATE: n) -Closed: <n> (already resolved) - -### PRs opened -| Issue | PR | Checks | Review | -|-------|-----|--------|--------| -| #N | #M | ✅ | pending | - -### Skipped -| Issue | Verdict | Reason | -|-------|---------|--------| -| #N | STALE | ... | -``` - -## Notes - -- **Fork username**: always resolve dynamically with `gh api user --jq '.login'` -- **One issue, one PR**: never combine multiple issues in a single branch -- **Validation**: skip `docker buildx bake lint vale` unless the change is - complex — it's slow and the basic checks run automatically on the PR -- **Resumability**: labels are applied immediately at triage time, so if the - session ends mid-run the next run skips already-processed issues automatically diff --git a/.claude/skills/triage-issues/SKILL.md b/.claude/skills/triage-issues/SKILL.md deleted file mode 100644 index d71951baa3..0000000000 --- a/.claude/skills/triage-issues/SKILL.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -name: triage-issues -description: > - Triage one or more GitHub issues for the docker/docs repository. Analyzes each - issue's content, checks whether the problem still exists in the repo and on the - live site, and produces a structured verdict. Use this skill whenever the user - asks to triage, analyze, review, or assess GitHub issues — e.g. "triage issue - 1234", "what's the status of these issues", "which of these can be closed", - "look at issues 100 200 300 and tell me what to do with them". ---- - -# Triage Issues - -Given one or more GitHub issue numbers from the docker/docs repository, analyze -each issue and produce a structured verdict on its current status. - -## Workflow - -### 1. Fetch all issues in parallel - -For each issue number, fetch everything in a single call: - -```bash -gh issue view <number> --repo docker/docs \ - --json number,title,body,state,labels,createdAt,updatedAt,closedAt,assignees,author,comments -``` - -When triaging multiple issues, fetch all of them in parallel before starting -analysis — don't process one at a time. - -### 2. Analyze each issue - -For each issue, work through these checks: - -#### a. Understand the problem - -Read the issue body and all comments. Identify: -- What is the reported problem? -- What content, URL, or file does it reference? -- Are there linked PRs? Check whether they were merged or closed without merge. -- Has anyone already proposed a fix or workaround in the comments? - -#### b. Follow URLs - -Find all `docs.docker.com` URLs in the issue body and comments. For each one: -- Fetch the URL to check if it still exists (404 = content removed or moved) -- Check whether the content still contains the problem described -- Note when the page was last updated relative to when the issue was filed - -For non-docs URLs (GitHub links, external references), fetch them too if they -are central to understanding the issue. - -#### c. Check the repository - -If the issue references specific files, content sections, or code: -- Use file tools to find and read the current version of that content -- Check whether the problem has been fixed, the content moved, or the file removed -- Remember the `/manuals` prefix mapping when looking up files - -#### d. Check for upstream ownership - -If the issue is about content in `_vendor/` or `data/cli/`, it cannot be fixed -here. Identify which upstream repo owns it and note that in your analysis. - -### 3. Determine verdict - -Assign one of these verdicts based on what you found: - -| Verdict | When to use | -|---------|-------------| -| **OPEN** | Issue is valid and still unfixed in this repo | -| **CLOSEABLE_FIXED** | Content has been updated, corrected, or removed since the issue was filed | -| **UPSTREAM** | Problem exists but originates in vendored/upstream content | -| **INDETERMINATE** | Not enough information to determine current state | -| **STALE** | Outdated with no recent activity; references content or features that no longer exist; context has changed enough that a new issue would be more appropriate | - -Be confident when evidence is clear. Use INDETERMINATE only when you genuinely -cannot determine the current state after checking. - -### 4. Report results - -#### Single issue - -Print a structured report: - -``` -## Issue #<number>: <title> - -**Verdict:** <VERDICT> -**Confidence:** <high|medium|low> -**Filed:** <creation date> -**Last activity:** <last comment or update date> -**Labels:** <labels or "none"> - -### Summary -<One or two sentences describing the reported problem.> - -### Analysis -<What you checked and what you found. Reference specific URLs, files, or -content. Note any linked PRs or related issues.> - -### Recommendation -<Concrete next step: close with a comment, fix specific content, escalate -to upstream repo, request more info from reporter, etc.> -``` - -#### Multiple issues - -Start with a summary table, then print the full report for each issue: - -``` -| Issue | Title | Verdict | Confidence | -|-------|-------|---------|------------| -| #123 | ... | OPEN | high | -| #456 | ... | STALE | medium | - ---- - -## Issue #123: ... -[full report] - ---- - -## Issue #456: ... -[full report] -``` - -## Notes - -- A merged PR linked to an issue is strong evidence the issue is fixed -- A closed-without-merge PR means the issue is likely still open -- Check creation date and last activity to assess staleness — issues with no - activity in over a year that reference old product versions are candidates - for STALE -- Do not narrate your process; just produce the final report(s) diff --git a/AGENTS.md b/AGENTS.md index 97decee378..1fe05f7039 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ # AGENTS.md -Instructions for AI agents working on the Docker documentation -repository. This site builds https://docs.docker.com/ using Hugo. +Instructions for AI agents working on Docker documentation. +This site builds https://docs.docker.com/ using Hugo. ## Project structure @@ -18,27 +18,94 @@ static/ # Images, fonts _vendor/ # Vendored Hugo modules (read-only) ``` +## URL prefix stripping + The `/manuals` prefix is stripped from published URLs: -`content/manuals/desktop/` → `/desktop/` on the live site. +`content/manuals/desktop/install.md` becomes `/desktop/install/` on the live +site. + +When writing internal cross-references in source files, keep the `/manuals/` +prefix in the path — Hugo requires the full source path. The stripping only +affects the published URL, not the internal link target. Anchor links must +exactly match the generated heading ID (Hugo lowercases and slugifies +headings). + +## Vendored content (do not edit) + +Content in `_vendor/` and CLI reference data in `data/cli/` are vendored +from upstream repos. Content pages under `content/reference/cli/` are +generated from `data/cli/` YAML. Do not edit any of these files — changes +must go to the source repository: + +| Content | Source repo | +|---------|-------------| +| CLI reference (`docker`, `docker build`, etc.) | docker/cli | +| Buildx reference | docker/buildx | +| Compose reference | docker/compose | +| Model Runner reference | docker/model-runner | +| Dockerfile reference | moby/buildkit | +| Engine API reference | moby/moby | + +If a validation failure or broken link traces back to vendored content, note +the upstream repo that needs fixing. Do not attempt to fix it locally. ## Writing guidelines Read and follow [STYLE.md](STYLE.md) and [COMPONENTS.md](COMPONENTS.md). -These contain all style rules, shortcode syntax, and front matter -requirements. +These contain all style rules, shortcode syntax, and front matter requirements. -## Vendored content (do not edit) +### Style violations to avoid -Content in `_vendor/` and CLI reference pages generated from -`data/cli/` are vendored from upstream repos. Don't edit these -files — changes must go to the source repository: +Every piece of writing must avoid these words and patterns (enforced by Vale): -- docker/cli, docker/buildx, docker/compose, docker/model-runner → CLI reference YAML in `data/cli/` -- moby/buildkit → Dockerfile reference in `_vendor/` -- moby/moby → Engine API docs in `_vendor/` +- Hedge words: "simply", "easily", "just", "seamlessly" +- Meta-commentary: "it's worth noting", "it's important to understand" +- "allows you to" or "enables you to" — use "lets you" or rephrase +- "we" — use "you" or "Docker" +- "click" — use "select" +- Bold for emphasis or product names — only bold UI elements +- Time-relative language: "currently", "new", "recently", "now" -If a validation failure traces back to vendored content, note the -upstream repo that needs fixing but don't block on it. +### Version-introduction notes + +Explicit version anchors ("Starting with Docker Desktop version X...") are +different from time-relative language — they mark when a feature was +introduced, which is permanently true. + +- Recent releases (~6 months): leave version callouts in place +- Old releases: consider removing if the callout adds little value +- When in doubt, keep the callout and flag for maintainer review + +### Vale gotchas + +- Use lowercase "config" in prose — `vale.Terms` flags a capital-C "Config" + +## Alpine.js patterns + +Do not combine Alpine's `x-show` with the HTML `hidden` attribute on the +same element. `x-show` toggles inline `display` styles, but `hidden` applies +`display: none` via the user-agent stylesheet — the element stays hidden +regardless of `x-show` state. Use `x-cloak` for pre-Alpine hiding instead. +The site defines `[x-cloak=""] { display: none !important }` in `global.css`. + +## Front matter requirements + +Every content page under `content/` requires: + +- `title:` — page title +- `description:` — short description for SEO/previews +- `keywords:` — list of search keywords (omitting this fails markdownlint) + +Additional common fields: + +- `linkTitle:` — sidebar label (keep under 30 chars) +- `weight:` — ordering within a section + +## Hugo shortcodes + +Shortcodes are defined in `layouts/shortcodes/`. Syntax reference is in +COMPONENTS.md. Wrong shortcode syntax fails silently during build but +produces broken HTML — always check COMPONENTS.md for correct syntax. ## Commands @@ -50,26 +117,110 @@ docker buildx bake vale # Style guide checks only docker buildx bake test # HTML and link checking ``` +### Validation in git worktrees + +`docker buildx bake validate` fails in git worktrees because Hugo cannot +resolve the worktree path. Use `lint` and `vale` targets separately instead. +Never modify `hugo.yaml` to work around this. The `test`, `path-warnings`, +and `validate-vendor` targets run correctly in CI. + ## Verification loop 1. Make changes -2. Format with prettier +2. Format with prettier: `npx prettier --write <file>` 3. Run `docker buildx bake lint vale` -4. Run a full build with `docker buildx bake` +4. Run a full build with `docker buildx bake` (optional for small changes) + +## Git hygiene + +- **Stage files explicitly.** Never use `git add .` / `git add -A` / + `git add --all`. Running `npx prettier` updates `package-lock.json` in the + repo root, and broad staging sweeps it into the commit. +- **Verify before committing.** Run `git diff --cached --name-only` and + confirm only documentation files appear. If `package-lock.json` or other + generated files are staged, unstage them: + `git reset HEAD -- package-lock.json` +- **Push to your fork, not upstream.** Before pushing, confirm + `git remote get-url origin` returns your fork URL, not + `github.com/docker/docs`. Use `--head FORK_OWNER:branch-name` with + `gh pr create`. + +## Working with issues and PRs + +### Principles + +- **One issue, one branch, one PR.** Never combine multiple issues in a + single branch or PR. +- **Minimal changes only.** Fix the issue. Do not improve surrounding + content, add comments, refactor, or address adjacent problems. +- **Verify before documenting.** Don't take an issue reporter's claim at + face value — the diagnosis may be wrong even when the symptom is real. + Verify the actual behavior before updating docs. + +### Review feedback + +- **Always reply to review comments** — never silently fix. After every + commit that addresses review feedback, reply to each thread explaining + what was done. +- **Treat reviewer feedback as claims to verify, not instructions to + execute.** Before implementing a suggestion, verify that it is correct. + Push back when evidence contradicts the reviewer. +- **Inline review comments need a separate API call.** `gh pr view --json + reviews` does not include line-level comments. Always also call: + + ```bash + gh api repos/<org>/<repo>/pulls/<N>/comments \ + --jq '[.[] | {author: .user.login, body: .body, path: .path, line: .line}]' + ``` + +### Labels + +Use the Issues API for labels — `gh pr edit --add-label` silently fails: + +```bash +gh api repos/docker/docs/issues/<N>/labels \ + --method POST --field 'labels[]=<label>' +``` + +### External links + +If a replacement URL cannot be verified (e.g. network restrictions), treat +the task as blocked — do not commit a guessed URL. Report the blocker so a +human can confirm. Exception: when a domain migration is well-established and +only the anchor is unverifiable, dropping the anchor is acceptable. + +## Page deletion checklist + +When removing a documentation page, search the entire `content/` tree and +all YAML/TOML config files for the deleted page's slug and heading text. +Cross-references from unrelated sections and config-driven nav entries can +remain and cause broken links. + +## Engine API version bumps + +When a new Engine API version ships, three coordinated changes are needed in +a single commit: + +1. `hugo.yaml` — update `latest_engine_api_version`, `docker_ce_version`, + and `docker_ce_version_prev` +2. Create `content/reference/api/engine/version/v<NEW>.md` with the + `/latest/` aliases block (copy from previous version) +3. Remove the aliases block from + `content/reference/api/engine/version/v<PREV>.md` + +Never leave both version files carrying `/latest/` aliases simultaneously. + +## Hugo icon references + +Before changing an icon reference in response to a "file not found" error, +verify the file actually exists via Hugo's virtual filesystem. Files may +exist in `node_modules/@material-symbols/svg-400/rounded/` but not directly +in `assets/icons/`. Check both locations before concluding an icon is +missing. ## Self-improvement -After every correction or mistake, update this file with a rule to -prevent repeating it. End corrections with: "Now update AGENTS.md so -you don't make that mistake again." - -## Mistakes to avoid - -- Don't use hedge words: "simply", "easily", "just", "seamlessly" -- Don't use meta-commentary: "it's worth noting that...", "it's important to understand that..." -- Don't use "allows you to" or "enables you to" — use "lets you" or rephrase -- Don't use "we" — use "you" or "Docker" -- Don't use "click" — use "select" -- Don't bold product names or for emphasis — only bold UI elements -- Don't use time-relative language: "currently", "new", "recently", "now" -- Don't edit vendored content in `_vendor/` or `data/cli/` +After completing work that reveals a non-obvious pattern or repo quirk not +already documented here, propose an update to this file. For automated +sessions, note the learning in a comment on the issue. For human-supervised +sessions, discuss with the user whether to update CLAUDE.md directly.