Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

\n```\n\nReference: [GitHub — About READMEs](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes) · [Keep a Changelog](https://keepachangelog.com/)\n\n---\n\n\n## docs/ as the Single Documentation Home\n\n**Impact: CRITICAL (One predictable home for everything beyond root files — no more \"where did we put that diagram?\")**\n\nEvery project needs one canonical place for documentation that isn't a conventional root file. `docs/` is the standard, recognized by GitHub Pages, MkDocs, Docusaurus, and most static-site generators. Without it, docs scatter across `wiki/`, `docs-internal/`, `notes/`, and inline `.md` files next to source.\n\n## Incorrect\n\n```\n❌ Docs scattered across the tree\n.\n├── README.md\n├── architecture-notes.md # at root\n├── deployment.md # at root\n├── app/Services/PaymentService.md # next to source\n├── notes/\n│ ├── design-meeting-2024.md\n│ └── refactor-ideas.md\n├── wiki/ # parallel docs folder\n│ ├── onboarding.md\n│ └── api.md\n└── internal-docs/\n └── runbook.md\n```\n\n**Problems:**\n- Same kind of content (deployment, runbooks) lives in 3 different folders\n- Static-site generators (MkDocs, Docusaurus, GitHub Pages) expect `docs/` and won't find the others\n- New contributors don't know where to put a new doc — and don't know where to find existing ones\n\n## Correct\n\n```\n✅ Everything non-root lives under docs/\n.\n├── README.md\n├── CHANGELOG.md\n├── LICENSE\n└── docs/\n ├── architecture/\n │ └── overview.md\n ├── adr/\n │ └── 0001-record-architecture-decisions.md\n ├── guides/\n │ ├── getting-started.md\n │ └── deployment.md\n ├── runbooks/\n │ └── incident-response.md\n └── archive/\n └── 2024/\n └── superseded-design.md\n```\n\n**Benefits:**\n- One predictable location — readers know where to look, contributors know where to add\n- Works out of the box with GitHub Pages (`/docs` source), MkDocs, Docusaurus\n- Source code stays uncluttered; no `.md` files next to `.php`/`.tsx` files\n\n## Exceptions\n\nA few `.md` files genuinely belong outside `docs/`:\n\n- **Root-level conventional files** — README, CHANGELOG, LICENSE, etc. (see `structure-root-files`)\n- **GitHub-specific files** — `.github/CODEOWNERS`, `.github/PULL_REQUEST_TEMPLATE.md`, `.github/ISSUE_TEMPLATE/*.md`\n- **Top-level subproject READMEs in monorepos** — `packages/auth/README.md` is fine; the package itself is a unit\n\nEverything else: in `docs/`.\n\n## Detection\n\n```bash\n# Find .md files that aren't in docs/ or one of the allowed locations\nfind . -name '*.md' -type f \\\n -not -path './docs/*' \\\n -not -path './.github/*' \\\n -not -path './node_modules/*' \\\n -not -path './vendor/*' \\\n -not -name 'README.md' \\\n -not -name 'CHANGELOG.md' \\\n -not -name 'CONTRIBUTING.md' \\\n -not -name 'SECURITY.md' \\\n -not -name 'CODE_OF_CONDUCT.md' \\\n -not -name 'AUTHORS.md' \\\n -not -name 'MAINTAINERS.md'\n```\n\nAny hit is a candidate for relocation into `docs/` (or deletion if it's junk).\n\nReference: [Diátaxis — documentation framework](https://diataxis.fr/) · [MkDocs](https://www.mkdocs.org/)\n\n---\n\n\n## docs/ Sub-folder Layout\n\n**Impact: HIGH (Different doc kinds have different audiences and lifecycles — separate them)**\n\nA flat `docs/` works for 5 files and falls apart at 20. Sub-folders by *purpose* (architecture, ADRs, guides, runbooks, archive) make docs scannable and let you apply different freshness/ownership rules per folder.\n\n## Incorrect\n\n```\n❌ Flat docs/ — everything mixed together\ndocs/\n├── overview.md\n├── deployment.md\n├── adr-001.md\n├── adr-002.md\n├── api.md\n├── incident-response.md\n├── data-model.md\n├── q3-launch-plan.md\n├── superseded-design.md\n└── onboarding.md\n```\n\n**Problems:**\n- Architecture, ADRs, guides, and runbooks all live in one bucket — no separation of concerns\n- Superseded docs (`superseded-design.md`) sit next to current docs — confusing\n- A reader looking for \"the runbook\" has to scan everything\n- Can't apply different rules (e.g., \"runbooks need an owner; archive doesn't\")\n\n## Correct\n\n```\n✅ Purpose-based sub-folders\ndocs/\n├── architecture/ # how the system is built (long-lived, slow-changing)\n│ ├── overview.md\n│ └── data-model.md\n├── adr/ # decisions made (append-only, numbered)\n│ ├── 0001-record-architecture-decisions.md\n│ └── 0002-choose-mysql-over-postgres.md\n├── guides/ # how-to for developers (medium-lived, task-oriented)\n│ ├── getting-started.md\n│ ├── deployment.md\n│ └── local-development.md\n├── runbooks/ # ops procedures (short-titled, action-focused)\n│ ├── deploy-production.md\n│ └── incident-response.md\n├── api/ # API references (often generated; OpenAPI/Swagger)\n│ └── openapi.yaml\n└── archive/ # superseded but kept for history\n ├── 2024/\n └── 2025/\n```\n\n**Benefits:**\n- Each folder has a clear purpose and audience\n- Archive is visually separated from current docs\n- Easy to apply per-folder rules (CODEOWNERS, freshness checks)\n- Maps naturally to [Diátaxis](https://diataxis.fr/) categories (tutorials/how-to/reference/explanation)\n\n## Diátaxis correspondence\n\n| Sub-folder | Diátaxis | Audience |\n|---|---|---|\n| `guides/getting-started.md` | Tutorial | First-time users |\n| `guides/deployment.md` | How-to | Engineers performing a task |\n| `architecture/` | Explanation | Engineers building understanding |\n| `api/` | Reference | Engineers looking up specifics |\n| `adr/` | Decision record | Engineers asking \"why?\" |\n| `runbooks/` | How-to (ops) | On-call engineers |\n\n## Add folders as needed\n\n- **`docs/security/`** — threat models, security architecture, audit reports\n- **`docs/onboarding/`** — new-hire orientation, codebase tour\n- **`docs/proposals/`** — RFCs / design proposals (before they become ADRs)\n- **`docs/meeting-notes/`** — only if you'll actually maintain them; otherwise use the issue tracker\n\nDon't pre-create empty folders. Add them when you have at least two docs that belong inside.\n\nReference: [Diátaxis](https://diataxis.fr/) · [adr.github.io](https://adr.github.io/)\n\n---\n\n\n## UPPERCASE Naming for Root-Level Conventional Files\n\n**Impact: CRITICAL (Hosting platforms, tools, and readers expect specific filenames at root — case matters)**\n\nGitHub, GitLab, Bitbucket, npm, Packagist, and most static-site generators look for **specific filenames in specific cases** at the repo root. `Readme.md` is not the same as `README.md` to a case-sensitive filesystem, and on case-sensitive CI runners (Linux containers) a mismatch breaks tools that auto-render the file.\n\n## Incorrect\n\n```\n❌ Inconsistent / wrong casing\n.\n├── Readme.md # GitHub renders, but tools that grep \"README\" miss it\n├── Changelog.md # Keep-a-Changelog tooling expects CHANGELOG.md\n├── license.md # Should be LICENSE (no extension by convention)\n├── Contributing.md # GitHub's \"How to contribute\" UI looks for CONTRIBUTING.md\n└── security.md # GitHub security advisories look for SECURITY.md\n```\n\n**Problems:**\n- GitHub case-sensitively matches `SECURITY.md` for the Security Advisories tab; `security.md` won't link\n- Many CI tools (e.g., `markdownlint` rule MD041) and license detectors look up the exact uppercase name\n- Linux filesystems treat `README.md` and `Readme.md` as distinct files — switching between editor casings creates phantom duplicates in git\n\n## Correct\n\n```\n✅ Conventional UPPERCASE names at root\n.\n├── README.md # uppercase, .md extension\n├── CHANGELOG.md\n├── LICENSE # no .md extension (long-standing convention)\n├── CONTRIBUTING.md\n├── SECURITY.md\n├── CODE_OF_CONDUCT.md # underscores, not hyphens\n└── AUTHORS.md # or MAINTAINERS.md\n```\n\n**Benefits:**\n- GitHub auto-detects each file for its respective tab/feature (Security, Contributing, Code of Conduct)\n- License-detection tools (GitHub Linguist, choosealicense.com) recognize `LICENSE` without extension\n- Case-sensitive CI environments behave the same as your dev machine\n\n## The `LICENSE` extension convention\n\n`LICENSE` (no extension) is the long-standing convention from open-source culture. GitHub auto-detects the license type from the file content regardless of extension, but `LICENSE` without extension is the dominant pattern. `LICENSE.md` and `LICENSE.txt` also work; pick one and be consistent across your repos.\n\n## Detection\n\n```bash\n# Find mis-cased conventional root files\nls *.md LICENSE* 2>/dev/null | \\\n awk 'BEGIN{IGNORECASE=1}\n /^(readme|changelog|contributing|security|code[-_]of[-_]conduct|authors|maintainers)\\.md$/ \\\n && $0 !~ /^(README|CHANGELOG|CONTRIBUTING|SECURITY|CODE_OF_CONDUCT|AUTHORS|MAINTAINERS)\\.md$/ \\\n { print \"MIS-CASED: \" $0 }'\n\n# Or simply:\nls Readme.md Changelog.md Contributing.md Security.md 2>/dev/null\n```\n\nReference: [GitHub — Adding a security policy](https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository) · [Keep a Changelog — naming](https://keepachangelog.com/)\n\n---\n\n\n## kebab-case for Files Under docs/\n\n**Impact: CRITICAL (URLs, search, and grep all behave better with lowercase-hyphenated names)**\n\nFiles under `docs/` get linked, served as URLs by static-site generators, and grepped daily. `kebab-case` (lowercase, hyphen-separated) avoids case-sensitivity bugs, generates clean URLs (`docs/deployment-guide.md` → `/deployment-guide`), and is the dominant convention across documentation sites.\n\n## Incorrect\n\n```\n❌ Mixed cases, spaces, underscores, PascalCase\ndocs/\n├── DeploymentGuide.md # PascalCase\n├── deployment_guide.md # snake_case\n├── Deployment Guide.md # spaces — break URLs\n├── Architecture Overview.md # spaces + Title Case\n├── api-Reference.md # mixed case\n├── data_model.MD # uppercase extension\n└── Onboarding.md # initial capital\n```\n\n**Problems:**\n- Mixed conventions force readers to guess each filename\n- Spaces in filenames produce `%20`-encoded URLs that are hard to type and look broken\n- Case mismatches between branches cause \"file not found\" on Linux CI while working on macOS/Windows\n- Static-site generators usually lowercase URLs anyway, so `DeploymentGuide.md` and `deployment-guide.md` collide\n\n## Correct\n\n```\n✅ Consistent kebab-case throughout docs/\ndocs/\n├── architecture/\n│ ├── overview.md\n│ ├── data-model.md\n│ └── service-boundaries.md\n├── guides/\n│ ├── deployment-guide.md\n│ ├── local-development.md\n│ ├── getting-started.md\n│ └── api-reference.md\n└── runbooks/\n ├── incident-response.md\n └── deploy-production.md\n```\n\n**Benefits:**\n- Predictable: anyone can guess the filename from the topic\n- URL-friendly: `/docs/guides/deployment-guide` reads naturally\n- Case-safe: lowercase eliminates case-sensitivity differences across OSes\n- Greppable: `grep -rn 'deployment-guide' docs/` always finds the file\n\n## Conventions\n\n- **All lowercase** — `deployment-guide.md`, not `Deployment-Guide.md`\n- **Hyphens, not underscores** — `data-model.md`, not `data_model.md`\n- **No spaces** — ever\n- **`.md` extension lowercase** — `.md`, not `.MD`\n- **Keep names short** — 2–4 words ideal; if you need 6, the doc may be doing too much\n- **Use nouns or noun-phrases** — `deployment-guide.md`, not `how-to-deploy.md` (the folder structure already conveys the verb)\n\n## Detection\n\n```bash\n# Find files in docs/ with bad casing or characters\nfind docs/ -name '*.md' | grep -E '[A-Z]|[[:space:]]|_'\n```\n\nAny hit is a candidate for renaming via `git mv`. Add a markdownlint rule or a CI grep to prevent regression.\n\nReference: [Diátaxis — naming](https://diataxis.fr/) · [Markdownlint](https://github.com/DavidAnson/markdownlint)\n\n---\n\n\n## ADR File Naming — Numbered Prefix\n\n**Impact: HIGH (ADRs are append-only history; numbering makes order, references, and freshness obvious)**\n\nArchitecture Decision Records (ADRs) are an append-only log of \"why we chose X\". A numbered prefix (`0001-...`, `0002-...`) makes chronology explicit, lets you say \"see ADR-0007\" in a PR, and groups all ADRs together when sorted alphabetically. Four-digit padding handles up to 9,999 ADRs without re-sorting.\n\n## Incorrect\n\n```\n❌ Inconsistent or missing numbering\ndocs/adr/\n├── record-architecture-decisions.md # no number\n├── 2-choose-mysql.md # single digit, sorts after 19\n├── ADR-3-inertia-for-spa.md # extra prefix\n├── 0004-use-redis-cache.md\n├── adopt-tailwind.md # no number\n└── 10-restructure-services.md # missing zero-padding\n```\n\n**Problems:**\n- Listing `docs/adr/` sorts in unpredictable order (`10-...` comes before `2-...`)\n- \"See ADR 3\" — but `ADR-3-inertia-for-spa.md` has the wrong prefix shape\n- Some have numbers, some don't; you can't tell which were written first\n\n## Correct\n\n```\n✅ Four-digit zero-padded prefix, kebab-case body\ndocs/adr/\n├── 0001-record-architecture-decisions.md # the meta-ADR — \"we will record decisions\"\n├── 0002-choose-mysql-over-postgres.md\n├── 0003-adopt-inertia-for-spa.md\n├── 0004-use-redis-for-session-store.md\n├── 0005-monorepo-package-layout.md\n├── ...\n└── 0042-rate-limit-public-api.md\n```\n\n**Benefits:**\n- Lexicographic sort = chronological sort, always\n- Compact references in PRs and code comments: \"ADR-0007\", \"see #0007\"\n- Four digits handle a decade of decisions without renumbering\n- New ADRs always append at the end — clear that history is immutable\n\n## Naming components\n\n```\n0007-rate-limit-public-api.md\n└┬─┘ └───────┬──────────────┘\n │ │\n │ └── Short description of the decision (kebab-case, 3–7 words)\n └── 4-digit zero-padded sequential number\n```\n\n- **Number** — sequentially assigned, never reused, never re-ordered\n- **Description** — present-tense verb where applicable; matches the ADR title\n\n## Template\n\nUse [Michael Nygard's template](https://github.com/joelparkerhenderson/architecture-decision-record) (or `adr-tools` CLI). Each ADR has:\n\n```markdown\n# 0007. Rate-limit the public API\n\nDate: 2026-05-16\nStatus: Accepted\n\n## Context\nWhat forces are at play?\n\n## Decision\nWhat did we decide?\n\n## Consequences\nWhat becomes easier? Harder?\n```\n\n## Tooling\n\n```bash\n# adr-tools CLI (https://github.com/npryce/adr-tools)\nbrew install adr-tools\nadr init docs/adr\nadr new \"Rate-limit the public API\" # auto-creates 0007-rate-limit-the-public-api.md\n```\n\n## Status field — proposed → accepted → superseded\n\nADR status is part of the *content*, not the filename. Don't rename `0007-...` to `0007-SUPERSEDED-...`. Update the `Status:` line and link to the superseding ADR:\n\n```markdown\nStatus: Superseded by ADR-0019\n```\n\nReference: [adr.github.io](https://adr.github.io/) · [Michael Nygard's original post](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) · [adr-tools](https://github.com/npryce/adr-tools)\n\n---\n\n\n## Naming Anti-Patterns to Reject\n\n**Impact: HIGH (Junk names accumulate fast — once you accept one, the floodgates open)**\n\nA docs folder degrades one bad name at a time. `MyNotes.md` makes `JohnsThoughts.md` feel acceptable, and within a year you can't tell what's real documentation and what's someone's scratchpad. Reject these patterns at PR review time.\n\n## The anti-patterns\n\n### 1. Dates in filenames\n\n```\n❌ deployment-2025-09-14.md\n❌ notes-2024-q3.md\n❌ 2026-03-meeting.md\n```\n\n**Why bad:** dates make readers wonder which version is current. Use the `Last modified` git timestamp + a `Last verified:` line inside the doc instead.\n\n**Allowed exception:** archive folders may include the year: `docs/archive/2024/launch-plan.md`.\n\n### 2. First-person / owner names\n\n```\n❌ MyNotes.md\n❌ JohnsArchitectureThoughts.md\n❌ Asyraf-deployment-draft.md\n```\n\n**Why bad:** docs belong to the project, not a person. If only one person can maintain it, it's not documentation — it's a private note. Use the issue tracker or a personal scratchpad.\n\n### 3. Draft / temp / version markers\n\n```\n❌ deployment-DRAFT.md\n❌ architecture-FINAL.md\n❌ architecture-FINAL-v2.md (the FINAL-v2 paradox)\n❌ deployment-OLD.md\n❌ tmp-notes.md\n❌ test-doc.md\n```\n\n**Why bad:** git history is the source of truth for \"draft vs final\" — that's what branches and PRs are for. \"FINAL-v2\" almost always means \"we never deleted the old one\".\n\n### 4. Mixed-purpose / vague names\n\n```\n❌ misc.md\n❌ stuff.md\n❌ notes.md (at root)\n❌ documentation.md (the whole project's docs in one file)\n❌ general.md\n```\n\n**Why bad:** if you can't name the doc precisely, it doesn't have a clear purpose. Either split it into focused docs or delete it.\n\n### 5. AI-plan / status / summary files\n\n```\n❌ PLAN.md\n❌ IMPLEMENTATION-PLAN.md\n❌ REFACTOR-PLAN.md\n❌ IMPLEMENTATION-SUMMARY.md\n❌ COMPLETED.md\n❌ NEXT-STEPS.md\n❌ TODO.md\n```\n\n**Why bad:** these are agent-generated transient state, not documentation. The work either landed (history is in git/PRs) or it didn't (tracking belongs in the issue tracker). See `cleanup-ai-junk`.\n\n### 6. Numbered without ADR semantics\n\n```\n❌ doc-1.md, doc-2.md, doc-3.md (numbers without meaning)\n❌ chapter-1.md, chapter-2.md (this is a book, not a docs folder)\n```\n\n**Why bad:** numbering implies order, but these have no append-only / decision-record semantics. Either use ADRs (`docs/adr/0001-...`) or use descriptive names.\n\n## Correct — what to use instead\n\n| Bad | Good |\n|---|---|\n| `MyArchitectureNotes.md` | `docs/architecture/overview.md` |\n| `deployment-2025-09.md` | `docs/guides/deployment.md` (with internal `Last verified:` line) |\n| `deployment-DRAFT.md` | Open a PR; keep the draft on a branch |\n| `notes.md` | Either a focused doc OR delete and use the issue tracker |\n| `PLAN.md` | A linked GitHub issue or project board |\n| `architecture-FINAL-v2.md` | `docs/architecture/overview.md` + git history |\n\n## Detection\n\n```bash\n# Names containing dates, draft markers, first-person, or AI-junk patterns\nfind docs/ -name '*.md' | grep -Ein \\\n '/(DRAFT|FINAL|OLD|TMP|TEMP|TODO|PLAN|SUMMARY|NEXT-STEPS|My[A-Z][a-z]|[12][0-9]{3}-[0-9]{2}-[0-9]{2}|v[0-9]+\\.md)'\n```\n\nReference: [Diátaxis](https://diataxis.fr/) · [Documentation System — naming conventions](https://docs.divio.com/documentation-system/)\n\n---\n\n\n## README — Required Content\n\n**Impact: CRITICAL (First impression for every reader; missing or stale README is the #1 onboarding blocker)**\n\nThe README is the front door. Every new contributor, every dependency review, every \"is this project maintained?\" check starts here. A weak README means each onboarding repeats the same questions in Slack.\n\n## Required sections (in order)\n\n````markdown\n# Project Name\n\nOne-sentence description: what does this do, for whom.\n\n[Badges optional — build, version, license]\n\n## Overview\n\n2–3 sentences expanding on the one-liner. Who uses this, what problem it solves.\n\n## Requirements\n\n- PHP 8.3+ / Node 22+\n- MySQL 8.0+ / Redis (if used)\n- Any other prerequisites\n\n## Installation\n\n```bash\ngit clone …\ncomposer install\nnpm install\ncp .env.example .env\nphp artisan key:generate\nphp artisan migrate\n```\n\n## Usage\n\nHow to run it locally:\n\n```bash\nphp artisan serve # backend\nnpm run dev # frontend\n```\n\nHow to run tests:\n\n```bash\nphp artisan test\nnpm test\n```\n\n## Documentation\n\n- [Architecture overview](docs/architecture/overview.md)\n- [Deployment guide](docs/guides/deployment.md)\n- [API reference](docs/api/openapi.yaml)\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\n[MIT](LICENSE) (or whichever)\n````\n\n## Incorrect\n\n```markdown\n❌ Sparse README missing essentials\n# my-app\n\nA web app.\n\n## Setup\n\nRun npm install. Should work.\n```\n\n**Problems:**\n- No description of *what* the app does or *for whom*\n- No requirements (which Node version? what database?)\n- \"Should work\" — when it doesn't, the reader has no context\n- No path to architecture docs, deployment, contributing\n- No license — many companies' legal review will block adoption\n\n## What to include vs leave out\n\n**Include:**\n- One-sentence description\n- Requirements (with versions)\n- Install / run / test commands\n- Links to deeper docs (`docs/`)\n- License\n\n**Don't include:**\n- A full API reference (link to `docs/api/`)\n- Detailed architecture (link to `docs/architecture/overview.md`)\n- Release history (that's `CHANGELOG.md`)\n- Long FAQs (`docs/guides/troubleshooting.md`)\n\nA README that's longer than one screen is doing too much. Split it.\n\n## Verification\n\nAdd a `Last verified: YYYY-MM-DD` line at the top of installation. Once a quarter, walk through the steps from scratch on a clean checkout and update the date.\n\n```markdown\n## Installation\n\n_Last verified on a clean checkout: 2026-04-12_\n\ngit clone …\n…\n```\n\n## Detection\n\n```bash\n# Does README exist and have minimum required sections?\ntest -f README.md || echo \"MISSING README.md\"\nfor h in 'Overview' 'Requirements' 'Installation' 'Usage' 'License'; do\n grep -q \"^## $h\" README.md || echo \"MISSING SECTION: ## $h\"\ndone\n```\n\nReference: [Make a README](https://www.makeareadme.com/) · [GitHub — About READMEs](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes)\n\n---\n\n\n## CHANGELOG — Keep-a-Changelog Format\n\n**Impact: HIGH (Release history readers can actually parse; the alternative is reading every commit)**\n\nEvery project past its first release needs a CHANGELOG that humans can read in 30 seconds. \"What changed in v3.2?\" should not require `git log v3.1..v3.2`. The [Keep a Changelog](https://keepachangelog.com/) format is the de-facto standard: one section per version, grouped by change type, newest first.\n\n## Required structure\n\n```markdown\n# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/).\n\n## [Unreleased]\n\n### Added\n- Bullet for each new feature in flight\n\n## [1.3.0] - 2026-05-10\n\n### Added\n- Bulk user export endpoint (#412)\n- MySQL 8.4 support; bumped CI to test against it\n\n### Changed\n- Password hashing default switched to argon2id (existing bcrypt hashes still validated and re-hashed on login)\n\n### Fixed\n- Stripe webhook idempotency key handling (#398, #401)\n\n### Security\n- Bumped guzzlehttp/guzzle to 7.9.2 (CVE-2024-XXXX)\n\n## [1.2.1] - 2026-04-22\n\n### Fixed\n- Cursor pagination skipping the last row of each page (#387)\n\n## [1.2.0] - 2026-04-01\n\n### Added\n- Initial release of public REST API\n\n[Unreleased]: https://github.com/org/repo/compare/v1.3.0...HEAD\n[1.3.0]: https://github.com/org/repo/compare/v1.2.1...v1.3.0\n[1.2.1]: https://github.com/org/repo/compare/v1.2.0...v1.2.1\n[1.2.0]: https://github.com/org/repo/releases/tag/v1.2.0\n```\n\n## Change-type buckets (use these)\n\n| Bucket | When to use |\n|---|---|\n| **Added** | New features or capabilities |\n| **Changed** | Changes to existing functionality |\n| **Deprecated** | Soon-to-be-removed features (announce here, then remove next major) |\n| **Removed** | Removed features |\n| **Fixed** | Bug fixes |\n| **Security** | CVE patches, vulnerability fixes |\n\nDrop unused buckets from a given release (don't include empty `### Added` if there were no additions).\n\n## Incorrect\n\n```markdown\n❌ Free-form CHANGELOG that nobody can scan\n\n# Changes\n\nv1.3 — May 2026 — lots of stuff, see commits for details\nv1.2 — April 2026 — bugfixes and improvements\nv1.1 — March 2026 — added cool feature\nv1.0 — Feb 2026 — initial release\n```\n\n**Problems:**\n- \"Lots of stuff\" tells the reader nothing\n- No buckets, so security fixes mix with cosmetic changes\n- No dates in ISO format (international ambiguity)\n- No links to commits, PRs, or version tags\n\n## CHANGELOG-in-the-PR discipline\n\nA CHANGELOG entry should land in **the same PR** as the change it describes — not after the fact, not \"later in a batch\". This is the only way to keep it accurate.\n\nEnforce with CI:\n\n```yaml\n# .github/workflows/changelog.yml\n- name: CHANGELOG entry required for non-trivial changes\n run: |\n if git diff --name-only origin/main...HEAD | grep -qvE '^(\\.github/|docs/|tests?/|README\\.md|\\.gitignore)

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

; then\n git diff origin/main...HEAD -- CHANGELOG.md | grep -q '^+' \\\n || { echo \"Add a CHANGELOG.md entry\"; exit 1; }\n fi\n```\n\nSkip for docs-only, test-only, and trivial PRs (use a label or PR title prefix).\n\n## When to start a CHANGELOG\n\n- **Library / package** — from the very first published version\n- **Application** — from the first deployment to production OR the first release tagged with `v`\n- **Internal tool** — when more than one team consumes it\n\nFor pre-1.0 projects in flux, [Keep a Changelog](https://keepachangelog.com/en/1.1.0/#how) explicitly endorses starting with `0.x.y` and using `[Unreleased]` heavily.\n\nReference: [Keep a Changelog](https://keepachangelog.com/) · [Semantic Versioning](https://semver.org/)\n\n---\n\n\n## LICENSE — Required Legal Clarity\n\n**Impact: CRITICAL (Without a license, code is technically un-reusable; many companies' legal review blocks adoption outright)**\n\nA repository without a LICENSE file means **all rights reserved** by default in most jurisdictions — nobody can legally use, copy, or modify the code, even if it's public. Every serious open-source project has one, and most companies' legal teams reject internal adoption of unlicensed dependencies.\n\n## Required\n\n```\nLICENSE # at repo root, no extension by convention\n```\n\nContents: the full text of the license you've chosen. For MIT, that's the standard ~20-line text with `\u003cyear>` and `\u003ccopyright holder>` filled in.\n\n## Choosing a license\n\nUse [choosealicense.com](https://choosealicense.com/) — it asks 2–3 questions and recommends one. Common picks:\n\n| License | When | Notes |\n|---|---|---|\n| **MIT** | Permissive; you want maximum adoption | Short, well-known, allows commercial use |\n| **Apache-2.0** | Permissive + explicit patent grant | Slightly longer; common for company-backed OSS |\n| **AGPL-3.0** | Copyleft + network-use clause | SaaS deployments must share modifications |\n| **BSL** / **Elastic 2.0** | Source-available, time-delayed open | \"Open-ish\" — verify it suits your goal |\n| **Proprietary** | Closed-source | Add `LICENSE` saying \"All rights reserved\" + contact for commercial terms |\n\nFor internal-only repos, still include a `LICENSE` file stating the company holds copyright and the code is for internal use only. This removes ambiguity for departing employees and contractors.\n\n## Incorrect\n\n```\n❌ No LICENSE file at all\n.\n├── README.md\n├── CHANGELOG.md\n└── src/...\n```\n\n```\n❌ Wrong placement / naming\n.\n├── docs/license.txt (should be at root, named LICENSE)\n└── LICENSE.MD (works but unconventional case)\n```\n\n```\n❌ \"MIT\" mentioned only in README, no file\nREADME:\n> ## License\n> MIT\n```\n\n(GitHub will not detect this as MIT licensed; license badges and tooling will fail.)\n\n## Correct\n\n```\n✅ LICENSE at root\n.\n├── LICENSE ← the full license text\n├── README.md\n└── src/...\n```\n\nREADME links to it:\n\n```markdown\n## License\n\n[MIT](LICENSE)\n```\n\n## License consistency in package manifests\n\nKeep the manifest field in sync with the LICENSE file:\n\n```json\n// package.json\n{\n \"license\": \"MIT\"\n}\n```\n\n```json\n// composer.json\n{\n \"license\": \"MIT\"\n}\n```\n\nMismatch (LICENSE says MIT, `package.json` says Apache-2.0) trips up `npm audit`, license-scanning tools, and human reviewers.\n\n## Detection\n\n```bash\ntest -f LICENSE -o -f LICENSE.md -o -f LICENSE.txt || echo \"MISSING LICENSE\"\n\n# Compare LICENSE to package.json (rough sanity check)\nLICENSE_NAME=$(grep -oE '(MIT|Apache|BSD|GPL|ISC|MPL)' LICENSE | head -1)\nPKG_LICENSE=$(node -e \"console.log(require('./package.json').license)\" 2>/dev/null)\necho \"LICENSE file: $LICENSE_NAME, package.json: $PKG_LICENSE\"\n```\n\nReference: [Choose a License](https://choosealicense.com/) · [GitHub — Licensing a repository](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository) · [SPDX license list](https://spdx.org/licenses/)\n\n---\n\n\n## CONTRIBUTING — When and What\n\n**Impact: MEDIUM (Sets the rules of engagement; without it, every PR is a guess)**\n\nA `CONTRIBUTING.md` tells outside (or new-to-the-team) contributors how to participate: which branch to target, commit message style, test expectations, and how PRs get reviewed. GitHub auto-links to it whenever someone opens an issue or PR.\n\n## When required\n\n- **Open-source projects** — required from day one\n- **Internal projects with external collaborators** (consultants, contractors) — recommended\n- **Solo or single-team internal projects** — optional; an `.md` heading inside the README's \"Development\" section is fine for small teams\n\nIf the project is closed-source and only your team contributes, skip `CONTRIBUTING.md` and put the same information in the README.\n\n## Recommended structure\n\n```markdown\n# Contributing\n\nThanks for considering a contribution!\n\n## Branching\n\n- `main` is protected; PRs only\n- Feature branches: `feat/\u003cshort-desc>`\n- Bug fixes: `fix/\u003cshort-desc>`\n\n## Commit messages\n\nWe follow [Conventional Commits](https://www.conventionalcommits.org/):\n\n```\nfeat(auth): add MFA enrollment endpoint\nfix(checkout): handle expired payment intent\ndocs(readme): clarify install steps\n```\n\n## Development setup\n\n```bash\ngit clone …\ncomposer install && npm install\ncp .env.example .env\nphp artisan migrate\n```\n\n## Running tests\n\n```bash\nphp artisan test # PHP / Laravel\nnpm test # JS / TS\n```\n\nAll tests must pass; new features need new tests.\n\n## Pull request checklist\n\n- [ ] CHANGELOG.md updated (under `[Unreleased]`)\n- [ ] Tests added / updated\n- [ ] `composer.lock` / `package-lock.json` committed if deps changed\n- [ ] Linked to a GitHub issue (if applicable)\n\n## Review process\n\n- One maintainer approval required\n- CI must pass (`tests`, `lint`, `markdown`)\n- Squash-merge — keep `main` history linear\n\n## Code of Conduct\n\nThis project follows the [Contributor Covenant](CODE_OF_CONDUCT.md).\n```\n\n## Incorrect\n\n```markdown\n❌ Vague CONTRIBUTING that says \"just open a PR\"\n\n# Contributing\n\nOpen a PR. We'll review.\n```\n\n**Problems:**\n- No branch policy → reviewer has to comment on every PR\n- No test expectation → contributors merge without running them\n- No commit style → CHANGELOG generation is harder, history is noisy\n\n## Detection\n\n```bash\n# Does CONTRIBUTING exist where needed?\nif [ -f \"package.json\" ] && grep -q '\"private\"' package.json; then\n : # internal, optional\nelse\n test -f CONTRIBUTING.md || echo \"MISSING CONTRIBUTING.md (recommended for non-private projects)\"\nfi\n```\n\nReference: [GitHub — Setting guidelines for repository contributors](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors) · [Conventional Commits](https://www.conventionalcommits.org/)\n\n---\n\n\n## SECURITY.md — Vulnerability Reporting Policy\n\n**Impact: HIGH (Without a clear policy, researchers go public; with one, they email you privately)**\n\nA `SECURITY.md` tells security researchers how to report a vulnerability to you privately. Without it, well-intentioned researchers either open a public GitHub issue (instant CVE) or give up and never report. GitHub's \"Security\" tab links directly to this file.\n\n## When required\n\n- **Any internet-facing service** — required\n- **Open-source libraries** — required from first public release\n- **Internal-only projects** — optional, but useful for clarity even internally\n- **Closed-source applications used by customers** — recommended\n\n## Recommended structure\n\n```markdown\n# Security Policy\n\n## Supported versions\n\nWe provide security updates for the following versions:\n\n| Version | Supported |\n|---------|-----------|\n| 3.x | ✅ |\n| 2.x | ✅ (until 2026-12) |\n| 1.x | ❌ (EOL) |\n\n## Reporting a vulnerability\n\n**Please do not file a public GitHub issue for security vulnerabilities.**\n\nEmail us at **[email protected]** with:\n- A description of the issue\n- Steps to reproduce\n- Affected versions (if known)\n- Any proof-of-concept code (optional)\n\nWe acknowledge reports within **2 business days** and aim to issue a fix within **30 days** for high/critical severity.\n\nIf you prefer encrypted communication, our PGP key is at \u003chttps://example.com/security.asc>.\n\n## Disclosure timeline\n\n- Day 0: We receive your report and acknowledge\n- Day 1–14: Triage, reproduce, develop fix\n- Day 15–30: Release patched version, notify users\n- Day 30+: Public disclosure (we'll credit you unless you prefer anonymity)\n\n## Safe harbor\n\nWe will not pursue legal action against researchers who:\n- Make a good-faith effort to follow this policy\n- Avoid privacy violations, service disruption, or destruction of data\n- Give us reasonable time to remediate before public disclosure\n```\n\n## Incorrect\n\n```markdown\n❌ No SECURITY.md at all on a public web app\n\n(No mechanism for reporting; researchers either guess an email or go public.)\n```\n\n```markdown\n❌ \"Just open an issue\"\n\n# Security\n\nIf you find a security issue, please open a GitHub issue.\n```\n\n(This guarantees public disclosure of vulnerabilities — the worst possible default.)\n\n## Use GitHub's built-in private reporting\n\nGitHub supports **Private Security Advisories** — researchers can report via the Security tab without exposing the issue publicly. Enable in Settings → Security → \"Private vulnerability reporting\". Reference it in `SECURITY.md`:\n\n```markdown\nYou may also use **GitHub's private vulnerability reporting**:\n\u003chttps://github.com/org/repo/security/advisories/new>\n```\n\n## Detection\n\n```bash\n# Internet-facing? Public repo? — should have SECURITY.md\ntest -f SECURITY.md || test -f .github/SECURITY.md \\\n || echo \"MISSING SECURITY.md (required for public/internet-facing projects)\"\n```\n\nReference: [GitHub — Adding a security policy](https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository) · [GitHub Private Vulnerability Reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) · [Disclose.io safe-harbor template](https://github.com/disclose/disclose)\n\n---\n\n\n## Conciseness — Cut Bloat\n\n**Impact: HIGH (Long docs aren't more informative — they're more skipped. The real risk is signal-to-noise.)**\n\nA README that's 400 lines doesn't tell the reader more — it tells them less, because they stop reading after the first screen. Good docs say what's needed and stop. Length is a cost, not a virtue.\n\n## Bloat patterns to cut\n\n### 1. Opening boilerplate\n\n```markdown\n❌ # Deployment Guide\n\n This document describes the deployment process for our application.\n It is intended to be read by engineers who are responsible for\n deploying the application to production. Before reading this document,\n you should be familiar with the basics of the application architecture.\n\n Without further ado, let's dive into the deployment process.\n\n ## Overview\n ...\n```\n\n```markdown\n✅ # Deployment Guide\n\n ## Overview\n ...\n```\n\nThe reader knows it's a deployment guide — the H1 says so. They know it's for engineers — they're reading the engineering docs. Five paragraphs of \"this document describes\" is five paragraphs of throat-clearing.\n\n### 2. Restating what code or commands already say\n\n````markdown\n❌ ## Installation\n\n To install the application, you first need to clone the repository\n from GitHub. After cloning the repository, navigate into the\n newly-created directory. Then, install the PHP dependencies by\n running composer install. Once that completes, install the\n JavaScript dependencies by running npm install. Finally, copy the\n example environment file to a new .env file.\n\n ```bash\n git clone …\n cd …\n composer install\n npm install\n cp .env.example .env\n ```\n````\n\n````markdown\n✅ ## Installation\n\n ```bash\n git clone …\n cd …\n composer install\n npm install\n cp .env.example .env\n ```\n````\n\nThe commands are self-explanatory. Don't narrate them.\n\n### 3. Repetition across sections\n\nA README that says the same thing in the \"Overview\", then again in \"Description\", then again in \"About this project\". Pick one.\n\n### 4. \"As mentioned above\" / \"as we'll see later\"\n\nIf something needs cross-referencing, link to it. Inline meta-commentary about the document's structure is noise.\n\n### 5. Over-explaining basics\n\n```markdown\n❌ Run `composer install`. Composer is a dependency manager for PHP\n that allows you to declare the libraries your project depends on\n and manages (install/update) them for you. To learn more about\n Composer, visit https://getcomposer.org.\n```\n\nIf the reader is on the install page of your project, they know what Composer is (or they have one click to learn). Don't teach the basics inline.\n\n## Rough length targets\n\nThese are heuristics — focused docs can be longer if they have to be, and short docs can be too long if they're padded.\n\n| Doc | Target |\n|---|---|\n| README | \u003c 200 lines (link to deeper docs for detail) |\n| Single guide / runbook | \u003c 300 lines |\n| Single ADR | \u003c 150 lines (decision + context + consequences) |\n| Architecture overview | \u003c 500 lines (split into multiple docs if longer) |\n\nIf you hit these and the content is genuinely necessary, split into multiple focused docs rather than letting one file balloon.\n\n## The \"delete a paragraph\" exercise\n\nBefore publishing a doc, try this: pick any paragraph and ask, \"if I deleted this, would a reader miss anything important?\" If the answer is no — or \"they'd just need to read the next paragraph more carefully\" — delete it.\n\nMost docs improve after a 20–30% cut.\n\n## Detection\n\n```bash\n# Long markdown files — review for bloat\nfind docs/ -name '*.md' -exec wc -l {} \\; | sort -rn | head -20\n\n# Docs with low signal: many lines containing \"this document\", \"you should\", \"let's\"\n# Note: grep -c counts matching LINES (one filler line = 1, even with multiple phrases).\ngrep -rEcH \"(this document|you should|let'?s (dive|look|start))\" --include='*.md' docs/ \\\n | awk -F: '{ if ($2 > 5) print \"BLOATY: \" $1 \" (\" $2 \" matching lines)\" }'\n```\n\nReference: [Strunk & White — Omit Needless Words](https://en.wikipedia.org/wiki/The_Elements_of_Style) · [Diátaxis — \"less is more\"](https://diataxis.fr/) · Internal: [`quality-ai-slop`](quality-ai-slop.md) (AI-style filler patterns)\n\n---\n\n\n## AI Slop in Documentation Content\n\n**Impact: HIGH (AI-generated content has telltale fingerprints that erode docs quality file-by-file)**\n\n[`cleanup-ai-junk`](cleanup-ai-junk.md) covers whole files agents leave behind. This rule covers **content inside otherwise-legitimate docs** — a README, an architecture overview, a runbook — that has been bulked up with AI-style filler. The patterns are recognizable; once you see them, you can't unsee them.\n\n## Slop fingerprints in content\n\n### 1. Filler phrases — delete on sight\n\n```\n\"It's worth noting that...\"\n\"It's important to understand...\"\n\"In summary...\" / \"To summarize...\"\n\"Let's dive into...\" / \"Let's take a look at...\"\n\"As we'll see...\" / \"As mentioned earlier...\"\n\"That being said...\" / \"Having said that...\"\n\"At its core...\" / \"Essentially...\"\n\"In essence...\" / \"Fundamentally...\"\n\"It goes without saying...\"\n\"Needless to say...\" (if it's needless to say, don't say it)\n```\n\nThese phrases add words without adding meaning. A human technical writer doesn't sprinkle \"in essence\" through a deployment guide.\n\n### 2. Bullet-list explosions\n\n```markdown\n❌ ## Why we chose Redis\n\n Redis was chosen for the following reasons:\n\n - **Performance** — Redis is very fast\n - **Reliability** — Redis is highly reliable\n - **Scalability** — Redis can scale to large workloads\n - **Community** — Redis has a large and active community\n - **Documentation** — Redis is well-documented\n - **Ecosystem** — Redis has a rich ecosystem of tools\n - **Maturity** — Redis is a mature and proven technology\n - **Versatility** — Redis supports many use cases\n```\n\nEight bullets, zero specifics. Replace with one paragraph that says *what we actually needed* (sub-millisecond GET latency + pub-sub for session invalidation) and *why Redis fit* (battle-tested at our scale; team has prod experience). The version with bullets *looks* informative; the prose version *is* informative.\n\n### 3. Closing sign-offs\n\n```markdown\n❌ ## Conclusion\n\n I hope this guide has been helpful in setting up your development\n environment. If you have any questions, feel free to reach out to\n the team. Happy coding! 🎉\n```\n\nDocumentation isn't a blog post. Cut the goodbye.\n\n### 4. Past-tense narration where present tense fits\n\n```markdown\n❌ When we started this project, we considered using Laravel because\n it offered a good balance of features. We decided that Laravel was\n the right choice. We chose MySQL as our database because of its\n maturity.\n```\n\n```markdown\n✅ The application uses Laravel for its mature ecosystem and MySQL\n for its operational track record. See [ADR-0002](adr/0002-...) for\n the full decision context.\n```\n\nThe \"we did X\" narrative belongs in commit messages or ADRs, not in the current architecture doc.\n\n### 5. Generic praise / hedging\n\n```\n\"leveraging industry best practices\"\n\"following modern development standards\"\n\"a robust and scalable solution\"\n\"a powerful and flexible framework\"\n\"state-of-the-art technology\"\n```\n\nThese phrases are zero-content. If \"industry best practices\" matters, name the specific practice (e.g., \"OWASP ASVS Level 2\", \"12-factor app\", \"Conventional Commits\"). If \"scalable\" matters, name the actual numbers (e.g., \"10k req/s with p95 \u003c 200ms\").\n\n### 6. Markdown formatting overuse\n\n- **Random words bolded for no reason**\n- Excessive heading nesting (`####` after only one `##`)\n- Bullet lists where prose flows better\n- Tables for two items\n\n### 7. The 100-line answer to a 3-line question\n\nA \"How do I run tests?\" section that takes 100 lines to say `php artisan test`.\n\n## Detection\n\n```bash\n# Filler-phrase fingerprints\ngrep -rEi --include='*.md' \\\n \"it's worth noting|let's (dive|take a look|start)|it goes without saying|needless to say|that being said|having said that|at its core|in essence|fundamentally\" \\\n docs/ README.md\n\n# Closing sign-offs in docs\ngrep -rEi --include='*.md' \\\n \"happy coding|hope this (was )?help|if you have any questions|feel free to reach out\" \\\n docs/ README.md\n\n# Generic praise (likely AI-slop)\ngrep -rEi --include='*.md' \\\n \"industry best practices|state-of-the-art|robust and scalable|powerful and flexible\" \\\n docs/ README.md\n\n# Many emojis = often AI-generated (humans rarely emoji-pepper technical docs).\n# `grep -roE` prints one match per emoji occurrence (not per line), so the\n# per-file count below is the true emoji count.\nfind docs/ README.md -name '*.md' -type f 2>/dev/null | while read f; do\n N=$(grep -oE '🤖|✨|🚀|🎉|💡|⚡|📝|🔥|🎯|💪' \"$f\" | wc -l | tr -d ' ')\n if [ \"$N\" -gt 5 ]; then\n echo \"MANY EMOJIS ($N): $f\"\n fi\ndone\n```\n\n## How to triage a slop-flagged doc\n\n1. **Read the whole doc end to end** — is the content valid under the slop?\n2. **If valid** — rewrite the prose: cut filler, name specifics, prefer present tense\n3. **If hollow** — the slop was hiding that the doc has no real content. Either rewrite from scratch with real content, or delete.\n\n**Never** rewrite docs *with another LLM* without close review — you'll just produce different slop. Use the model to draft, then read every line as a human and cut ruthlessly.\n\nReference: Internal: [`cleanup-ai-junk`](cleanup-ai-junk.md) (junk files) · [`quality-conciseness`](quality-conciseness.md) (general bloat) · [On Bullshit (Frankfurt)](https://en.wikipedia.org/wiki/On_Bullshit) — yes, really\n\n---\n\n\n## Heading Hierarchy — H1 Once, No Skipped Levels\n\n**Impact: HIGH (Broken heading hierarchy breaks readability, accessibility, and auto-generated TOCs)**\n\nEach markdown file has exactly one H1 (the document title), and lower levels go in sequence (H2 → H3 → H4) without skipping. Auto-generated TOCs, screen readers, and GitHub's outline view all rely on this. A file with three H1s or with H1 → H3 jumps reads as broken to humans and as malformed to tools.\n\n## Rules\n\n1. **Exactly one H1 per file** — it's the title\n2. **No skipping levels** — H2 can be followed by H2 or H3, but not H4\n3. **Don't use bold instead of a heading** — `**Important:**` doesn't show up in TOCs\n4. **Don't use H1 inside a doc** — once you've used `#`, use only `##` and below\n5. **Headings should be descriptive, not generic** — `## Configuration` not `## Section 2`\n\n## Incorrect\n\n### Multiple H1s\n\n```markdown\n# Deployment Guide\n\n## Overview\n\nSome overview content.\n\n# Configuration\n\nSome configuration content. ← second H1; should be ##\n\n# Troubleshooting\n\n… ← third H1\n```\n\nThe doc has three \"top-level\" sections in markdown's eyes; GitHub will treat each H1 as a candidate document title. The TOC will be flat and confused.\n\n### Skipped levels\n\n```markdown\n# Architecture Overview\n\n## Components ← H2\n\n#### Authentication ← H4 (skipped H3)\n\nSome content about auth.\n\n#### Authorization ← H4 still\n\n#### Sessions ← H4 still\n```\n\nThe reader expects \"Components\" to have direct sub-sections; instead it has sub-sub-sections. Auto-generated outlines look broken.\n\n### Bold-as-heading\n\n````markdown\n## Setup\n\n**Prerequisites:** ← bold, not heading\n\n- Node 22\n- MySQL 8\n\n**Install:** ← bold, not heading\n\n```bash\nnpm install\n```\n````\n\n\"Prerequisites\" and \"Install\" don't appear in the TOC; readers scanning headings miss them.\n\n## Correct\n\n```markdown\n# Deployment Guide ← exactly one H1, matching the file's purpose\n\n## Overview\n\nBrief overview content.\n\n## Configuration\n\n### Environment variables ← H3 under H2\n\n### Secrets storage\n\n## Troubleshooting\n\n### Build failures ← H3 under H2\n\n### Deploy timeouts\n```\n\nSequential, predictable, scannable.\n\n## Heading content\n\n### Use sentence case\n\n```\n✓ ## Setting up a development environment\n✗ ## Setting Up A Development Environment (title case is awkward to read)\n✗ ## SETTING UP A DEVELOPMENT ENVIRONMENT (shouting)\n```\n\n### Be specific\n\n```\n✓ ## Resetting a user's password from the admin panel\n✗ ## Password reset (which kind? from where?)\n```\n\n### Match how readers search\n\nThe TOC of a 1000-line doc is its index. Headings should answer \"what would I search for?\", not be cute.\n\n## Detection\n\n```bash\n# Files with more than one H1 (set -e safe — uses if/then instead of && chain)\nfind docs/ -name '*.md' -print0 | while IFS= read -r -d '' f; do\n H1_COUNT=$(grep -cE '^# [^#]' \"$f\" || true)\n if [ \"$H1_COUNT\" -gt 1 ]; then\n echo \"MULTIPLE H1s ($H1_COUNT): $f\"\n fi\ndone\n\n# Skipped heading levels (markdownlint rule MD001)\nnpx markdownlint-cli2 'docs/**/*.md' 'README.md'\n# (Configure .markdownlint.json: { \"MD001\": true })\n```\n\n`markdownlint`'s rule MD001 (`heading-increment`) catches skipped levels automatically; MD025 (`single-h1`) catches multiple H1s. Enable both.\n\nReference: [Markdownlint rules MD001 / MD025](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md) · [WAI-ARIA Heading Levels](https://www.w3.org/WAI/tutorials/page-structure/headings/)\n\n---\n\n\n## Code Blocks — Language Tags and Copy-Pasteability\n\n**Impact: MEDIUM (Untagged code blocks lose syntax highlighting; broken commands waste reader time)**\n\nA code block without a language tag renders as monospace text with no syntax highlighting. A \"command\" that contains an unexplained placeholder (`\u003cyour-token-here>`) or a typo wastes every future reader's time. Code blocks in docs are contracts: if you paste them, they should work.\n\n## Rules\n\n### 1. Every code block has a language tag\n\n````markdown\n❌ ```\n php artisan migrate\n ```\n\n✅ ```bash\n php artisan migrate\n ```\n````\n\nCommon tags:\n\n| Tag | Use for |\n|---|---|\n| `bash` / `sh` | Shell commands, terminals |\n| `php` | PHP code |\n| `js` / `ts` / `tsx` | JavaScript / TypeScript / TSX |\n| `json` | JSON config |\n| `yaml` / `yml` | YAML (CI configs, etc.) |\n| `sql` | SQL queries |\n| `markdown` / `md` | Nested markdown examples (use 4-backtick outer fence) |\n| `diff` | Patches / before-after |\n| `text` or no tag | Genuinely plain text only |\n\n### 2. Commands you intend to be copy-pasted must actually run\n\n```bash\n❌ git clone \u003cyour-repo-url> # placeholder; reader has to figure out what\n❌ npm install your-package # 'your-package' isn't a real package\n\n✅ git clone [email protected]:your-org/your-repo.git\n✅ npm install # no args — installs from package.json\n```\n\nIf a placeholder is unavoidable (real credentials, secret URL), surround it with a comment that makes the substitution obvious:\n\n```bash\n✅ # Replace YOUR_API_TOKEN with the token from Settings → API\n curl -H \"Authorization: Bearer YOUR_API_TOKEN\" https://api.example.com/...\n```\n\n### 3. Multi-line commands use proper line continuation\n\n```bash\n❌ git commit -m \"feat: add user export\"\n --no-verify\n\n✅ git commit -m \"feat: add user export\" \\\n --no-verify\n```\n\nWithout the backslash, the second line is a separate command (and will fail).\n\n### 4. Don't include ` project-docs — Skillopedia or `>` prompts in copy-pasteable blocks\n\n```bash\n❌ $ npm install\n $ npm run dev\n\n✅ npm install\n npm run dev\n```\n\nThe ` project-docs — Skillopedia is fine if you're showing input/output together (where output lines have no ` project-docs — Skillopedia ), but for copy-paste-friendly blocks, omit the prompt.\n\n### 5. Show output separately from input\n\n````markdown\n❌ ```bash\n $ php artisan about\n Laravel ............. 11.0\n PHP ................. 8.3\n ```\n\n✅ ```bash\n php artisan about\n ```\n\n Output:\n\n ```\n Laravel ............. 11.0\n PHP ................. 8.3\n ```\n````\n\nThis way the reader can copy the command without dragging output along.\n\n### 6. Test the commands\n\nBefore publishing a guide, run every command in it from scratch (clean shell, clean checkout). The number of bugs you find on the first run is sobering.\n\n## Detection\n\n```bash\n# Code blocks with no language tag\nfor f in $(find docs/ README.md -name '*.md' 2>/dev/null); do\n awk -v file=\"$f\" '\n /^```$/ && !in_code { print file \":\" NR \": untagged code block\"; in_code=1; next }\n /^```/ && !in_code { in_code=1; next }\n /^```$/ && in_code { in_code=0 }\n ' \"$f\"\ndone\n\n# Markdownlint rule MD040 (fenced-code-language) catches this automatically\nnpx markdownlint-cli2 --config '.markdownlint.json' '**/*.md'\n```\n\nAdd to `.markdownlint.json`:\n\n```json\n{\n \"MD040\": true // fenced-code-language — language tags required\n}\n```\n\nReference: [Markdownlint MD040](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md040) · [GitHub — Syntax highlighting](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks)\n\n---\n\n\n## Links — Descriptive Text and No Broken URLs\n\n**Impact: MEDIUM (\"Click here\" is unscannable and inaccessible; broken links erode trust in the whole doc set)**\n\nTwo failure modes degrade docs quickly: link text that doesn't describe where the link goes (\"click here\", \"this link\") and links that 404 because the target moved. Both are easy to detect and fix; left unchecked, both compound until readers stop trusting the docs.\n\n## Rule 1 — Descriptive link text\n\n```markdown\n❌ For deployment instructions, click [here](docs/guides/deployment.md).\n❌ See [this page](docs/guides/deployment.md) for deployment.\n❌ Read more about deployment [here](docs/guides/deployment.md).\n\n✅ See the [deployment guide](docs/guides/deployment.md).\n✅ Deployment instructions are in [docs/guides/deployment.md](docs/guides/deployment.md).\n✅ For details, see the [deployment guide](docs/guides/deployment.md).\n```\n\nWhy descriptive text matters:\n\n1. **Scannability** — a reader skimming the doc sees \"deployment guide\", not \"click here\"\n2. **Accessibility** — screen readers can announce just the link text in isolation; \"click here\" out of context is meaningless\n3. **Search** — \"deployment guide\" is a useful keyword; \"click here\" is not\n\n### Quick test\n\nRead just the link text out loud (ignore the surrounding sentence). Does it tell you where you'd land? If yes, it's good. If \"click here\" / \"this link\" / \"more\", rewrite.\n\n## Rule 2 — Use relative paths for in-repo links\n\n```markdown\n❌ See https://github.com/your-org/your-repo/blob/main/docs/architecture/overview.md\n✅ See [docs/architecture/overview.md](docs/architecture/overview.md)\n```\n\nRelative paths:\n- Work on any fork or mirror\n- Don't break when the repo is renamed or moved\n- Resolve correctly when docs are served via MkDocs / GitHub Pages\n\nUse absolute URLs only for **off-repo** targets (external sites, other repos).\n\n## Rule 3 — Check links don't 404\n\nBroken links happen when files are renamed, moved, or deleted without updating their referrers. The fix is automation: a link checker in CI.\n\n```yaml\n# .github/workflows/check-links.yml\nname: Check links\non:\n pull_request:\n paths: ['**/*.md']\n schedule: [{ cron: '0 9 * * MON' }] # weekly catches link-rot\n\njobs:\n lychee:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: lycheeverse/lychee-action@v2\n with:\n args: --no-progress --exclude-mail './**/*.md' README.md\n fail: true\n```\n\n[Lychee](https://github.com/lycheeverse/lychee) is the recommended tool: fast (Rust), supports config files, exits non-zero on broken links so CI fails the PR.\n\nFor per-file checks during local development:\n\n```bash\nnpx markdown-link-check docs/guides/deployment.md\n```\n\n## Rule 4 — Don't link to the current file\n\n```markdown\n❌ See [this section](#installation) for installation. (when \"this section\" IS the installation section)\n```\n\nSelf-references are usually filler. The reader is already there.\n\n## Rule 5 — Use reference-style links for repeated targets\n\nWhen the same URL appears 3+ times in a doc, use reference-style:\n\n```markdown\nSome prose linking to the [Laravel docs][laravel].\n\nSome more prose also linking to the [Laravel docs][laravel] later on.\n\nYet more prose, third time linking to the [Laravel docs][laravel].\n\n[laravel]: https://laravel.com/docs/11.x\n```\n\nThe doc stays readable in source form, and updating the URL in one place updates all occurrences.\n\n## Detection\n\n```bash\n# Non-descriptive link text\ngrep -rEnH '\\[(click here|here|this|this link|read more|more|link)\\]\\(' \\\n --include='*.md' docs/ README.md\n\n# Absolute URLs to your own repo (should be relative)\n# EDIT: replace with your actual GitHub org/repo before running.\nORG_REPO=\"your-org/your-repo\"\ngrep -rEnH \"github\\.com/$ORG_REPO/(blob|tree)/\" --include='*.md' .\n\n# Run link checker locally\nnpx lychee --no-progress './**/*.md' README.md\n```\n\nReference: [Lychee](https://github.com/lycheeverse/lychee) · [markdown-link-check](https://github.com/tcort/markdown-link-check) · [WCAG 2.4.4 — Link Purpose](https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context.html)\n\n---\n\n\n## AI-Generated Junk Files\n\n**Impact: HIGH (Agents create transient plan/summary files that masquerade as documentation)**\n\nWhen you let coding agents work on a project, they tend to leave behind plan files, status summaries, and \"next steps\" notes that look like documentation but are actually transient working memory. After a few sprints, these accumulate as dozens of `PLAN.md`/`SUMMARY.md` files that nobody owns and nobody reads.\n\n## How to recognize AI junk\n\n### 1. By filename\n\nCommon patterns generated by AI agents:\n\n```\nPLAN.md IMPLEMENTATION-PLAN.md\nTODO.md IMPLEMENTATION-SUMMARY.md\nNEXT-STEPS.md REFACTOR-PLAN.md\nCOMPLETED.md REFACTOR-NOTES.md\nPROGRESS.md CHANGES.md (when CHANGELOG.md already exists)\nTASK-LIST.md SESSION-NOTES.md\nCONTEXT.md DECISIONS.md (when docs/adr/ exists)\nWORK-LOG.md DEBUG-NOTES.md\n```\n\n### 2. By content fingerprint\n\nAI-generated docs often have telltale signatures:\n\n- Trailing line: **\"🤖 Generated with Claude Code\"** / **\"Generated by GitHub Copilot\"** / **\"Created by Codex\"**\n- Phrases like **\"I have completed the following tasks\"** or **\"Here is a summary of the changes\"**\n- Bullet lists in past tense describing what was *just* done\n- \"Next steps:\" sections that were never actioned\n- Repeated boilerplate intros (\"This document describes...\")\n\n### 3. By context\n\n- File has 1 commit (created by the agent, never edited by a human)\n- Last commit message: `\"Add PLAN.md\"` / `\"Update progress\"` / `\"Add session summary\"`\n- Located at root of the repo (instead of properly organized under `docs/`)\n- Date in the filename: `notes-2025-09-14.md`, `plan-q3-2025.md`\n\n## Incorrect — what they look like\n\n```markdown\n❌ PLAN.md (committed by an agent, 8 months ago, never edited since)\n\n# Implementation Plan\n\nI'll be implementing the new user export feature in the following steps:\n\n## Phase 1: Schema (Day 1)\n- [x] Add export_jobs table\n- [x] Add foreign key to users\n- [x] Write migration\n\n## Phase 2: Service layer (Day 2-3)\n- [x] Create UserExportService\n- [ ] Add streaming support\n\n## Next steps\n- Discuss approach with team\n- Get sign-off before deployment\n\n🤖 Generated with Claude Code\n```\n\n**Problems:**\n- This was tracking work-in-progress; the work shipped 8 months ago\n- The unchecked item is either obsolete (already done in a later PR) or forgotten\n- File pollutes the repo's root and gets indexed by GitHub search\n- Future agents read it and treat it as authoritative current state\n\n## Correct — how to triage\n\nFor each candidate junk file, ask:\n\n1. **Is the work in this file complete?** → Delete. Work history is in git/PRs.\n2. **Is the work *abandoned*?** → Delete. (If you want to remember it, open an issue.)\n3. **Is the work ongoing?** → Move it to the issue tracker, not a markdown file in the repo.\n4. **Does it contain unique knowledge** (architecture decision, gotcha, runbook)? → Refactor into a proper doc:\n - Architecture decision → `docs/adr/NNNN-...md`\n - Gotcha / how-to → `docs/guides/...md`\n - Ops procedure → `docs/runbooks/...md`\n\n**Never auto-delete.** Always surface for the user's approval first.\n\n## Detection\n\n```bash\n# Filename patterns\nfind . -maxdepth 3 -type f -name '*.md' \\\n -not -path './node_modules/*' -not -path './vendor/*' \\\n | grep -Ei \\\n '(PLAN|TODO|SUMMARY|PROGRESS|NEXT-STEPS|COMPLETED|TASK-LIST|SESSION-NOTES|WORK-LOG|REFACTOR-NOTES|DEBUG-NOTES|CONTEXT|CHANGES)\\.md

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

\n\n# Content fingerprints — AI footer signatures\ngrep -rln -E '🤖 (Generated|Created) with|Co-Authored-By: Claude|Generated by (Copilot|Codex|Cursor)' \\\n --include='*.md' .\n\n# Markdown files committed by an agent and never edited by a human (single commit on the file).\n# Useful for catching transient plan/summary files that landed and were forgotten.\nfind . -name '*.md' -not -path './node_modules/*' -not -path './vendor/*' | while read f; do\n COMMITS=$(git log --oneline -- \"$f\" | wc -l | tr -d ' ')\n [ \"$COMMITS\" = \"1\" ] && echo \"SINGLE-COMMIT (likely auto-generated): $f\"\ndone\n```\n\n## Prevention\n\n- **Tell agents not to create plan files**: in your `CLAUDE.md` / `AGENTS.md` / `cursor.json`, instruct: \"Do not create PLAN.md, TODO.md, or progress-tracking markdown files in the repo. Use the conversation context or the issue tracker.\"\n- **CI gate**: block PRs that add `PLAN.md` / `TODO.md` / `SUMMARY.md` / etc. at root\n- **Periodic audit**: every quarter, run the detection above\n\n```yaml\n# .github/workflows/no-agent-junk.yml\n- name: Block AI-junk filenames\n run: |\n NEW=$(git diff --name-only --diff-filter=A origin/main...HEAD)\n BAD=$(echo \"$NEW\" | grep -Ei '^(PLAN|TODO|SUMMARY|PROGRESS|NEXT-STEPS)\\.md

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

)\n test -z \"$BAD\" || { echo \"Don't commit transient plan/summary files: $BAD\"; exit 1; }\n```\n\nReference: [Diátaxis — \"what documentation is not\"](https://diataxis.fr/) · [Internal: cleanup-orphans, cleanup-empty-stubs]\n\n---\n\n\n## Duplicate Documentation\n\n**Impact: HIGH (Two copies of the same doc inevitably drift; readers can't tell which is current)**\n\nDuplicate docs are the documentation equivalent of code duplication: every update has to happen in N places, and after the first inconsistency, no reader can tell which version is authoritative. Common causes are agents creating a \"v2\" of an existing doc, refactors that didn't delete the predecessor, and forks that drift.\n\n## How duplicates appear\n\n### 1. Near-identical content under different filenames\n\n```\ndocs/architecture.md\ndocs/architecture-overview.md\ndocs/system-design.md\n```\n\nThree files, ~70% overlapping content, last updated by different authors at different times.\n\n### 2. README sections duplicated in docs/\n\n```\nREADME.md # has \"## Installation\" with 30 lines\ndocs/guides/getting-started.md # has its own install section, slightly different\ndocs/guides/setup.md # also has install steps, outdated\n```\n\n### 3. \"Updated\" doc next to the old one\n\n```\ndocs/deployment.md\ndocs/deployment-v2.md\ndocs/deployment-NEW.md\n```\n\nAlways pick one; one of them is lying.\n\n### 4. ADR vs design doc covering the same decision\n\n```\ndocs/adr/0007-choose-mysql.md\ndocs/architecture/database-choice.md\n```\n\nThe ADR is the canonical record; the design doc duplicates it. Either delete the design doc or replace it with a one-line pointer to the ADR.\n\n## Incorrect\n\n```\n❌ Three near-duplicates of \"how to deploy\"\nREADME.md → ## Deployment (15 lines)\ndocs/guides/deployment.md (80 lines, slightly different commands)\ndocs/runbooks/deploy-production.md (60 lines, mostly the same as guides/deployment.md but with prod-specific notes)\n```\n\n**Problems:**\n- A new engineer asks \"how do I deploy?\" — gets three different answers\n- A change to deploy command must update all three (and usually doesn't)\n- Each copy ages at a different rate\n\n## Correct\n\n```\n✅ One canonical source; others link to it\n\nREADME.md\n ## Deployment\n See [docs/runbooks/deploy-production.md](docs/runbooks/deploy-production.md).\n\ndocs/guides/deployment.md ← DELETED, content merged into runbook\n\ndocs/runbooks/deploy-production.md ← the authoritative source\n # Deploy to production\n …full procedure…\n```\n\n**Benefits:**\n- One place to change\n- Readers reach the canonical version regardless of where they started\n- Future \"is this current?\" question has one file to check\n\n## Resolution strategies\n\nWhen you find duplicates:\n\n1. **Pick a winner** — the most current/most detailed/best-located version\n2. **Merge unique content** from the others into the winner\n3. **Delete the losers** — or replace them with one-line pointers if their location was useful (e.g., README → docs/runbooks)\n4. **Add a CI check** to prevent re-introduction (markdownlint can flag similar headings across files)\n\n## Detection\n\n```bash\n# Find near-duplicate markdown files (jscpd can do this)\nnpx jscpd --languages markdown --min-lines 20 docs/ README.md\n\n# Or compare line-by-line for high similarity (bash; needs globstar for **)\nshopt -s globstar nullglob\nfor a in docs/**/*.md; do\n for b in docs/**/*.md; do\n [[ \"$a\" \u003c \"$b\" ]] || continue\n SIM=$(diff \u003c(sort \"$a\") \u003c(sort \"$b\") | wc -l)\n LINES=$(wc -l \u003c \"$a\")\n if [ \"$LINES\" -gt 50 ] && [ \"$SIM\" -lt $((LINES / 4)) ]; then\n echo \"SIMILAR: $a \u003c-> $b (diff: $SIM lines)\"\n fi\n done\ndone\n\n# Files with the same H1 title\ngrep -rh '^# ' docs/ | sort | uniq -d\n```\n\nReference: [Diátaxis — \"one job per document\"](https://diataxis.fr/) · [jscpd](https://github.com/kucherenko/jscpd)\n\n---\n\n\n## Orphaned Documentation\n\n**Impact: MEDIUM (Docs nobody links to are docs nobody finds — they age into landmines)**\n\nAn orphan is a markdown file that no other file links to. It exists on disk, gets indexed by `git grep`, but nobody navigates to it via the normal docs flow. Orphans are usually either: drafts that were abandoned, docs whose linker was deleted, or junk that was never properly integrated.\n\n## How to spot orphans\n\nThe simplest definition: **for every `.md` file in `docs/`, at least one *other* `.md` file should link to it**, directly or transitively from README.md.\n\nFiles that pass:\n- `README.md` links to `docs/architecture/overview.md` ✓\n- `docs/architecture/overview.md` links to `docs/architecture/data-model.md` ✓\n- `docs/adr/0001-...md` is linked from `docs/adr/README.md` (or implicitly from numbering) ✓\n\nFiles that *fail*:\n- `docs/old-onboarding-notes.md` — no inbound link\n- `docs/api-thoughts.md` — was linked from a deleted README section\n- `docs/guides/payments-deep-dive.md` — written 2 years ago, never linked\n\n## Incorrect\n\n```\n❌ Orphan piles up while linked docs stay current\ndocs/\n├── architecture/\n│ └── overview.md ← linked from README\n├── guides/\n│ ├── deployment.md ← linked\n│ ├── getting-started.md ← linked\n│ ├── old-onboarding.md ← ORPHAN (no inbound links)\n│ ├── api-thoughts.md ← ORPHAN\n│ └── payments-deep-dive.md ← ORPHAN\n└── archive/\n └── ...\n```\n\n**Problems:**\n- Orphans show up in `grep` results and confuse readers who land on them\n- Search engines (GitHub search, MkDocs site search) surface them\n- Future \"is this still relevant?\" question has no obvious answer\n\n## Correct — resolution options\n\nFor each orphan:\n\n1. **Link it** — if the content is current and useful, add an inbound link from the closest hub (README, `docs/README.md`, the relevant guide). Then it's no longer an orphan.\n2. **Archive it** — if it was once useful but no longer is, move to `docs/archive/\u003cyear>/`. The archive folder is *intentionally* unlinked from the main docs.\n3. **Delete it** — if it's a draft, near-duplicate, or AI junk, delete. Git history preserves it.\n\n**Never auto-delete.** Always surface for the user's approval.\n\n## Detection\n\n```bash\n# Find all .md files in docs/, then check whether any other .md links to them.\n# Uses two grep passes (one per pattern) since -F (fixed-string) doesn't support\n# alternation; bare paths with `.` characters would mis-trigger with -E.\nfind docs/ -name '*.md' -type f | while read f; do\n base=$(basename \"$f\" .md)\n rel=${f#./} # path without leading ./\n REFS=$( { grep -rlF \"$rel\" --include='*.md' \\\n --exclude-dir=node_modules --exclude-dir=vendor . ;\n grep -rlF \"$base\" --include='*.md' \\\n --exclude-dir=node_modules --exclude-dir=vendor . ; } \\\n | sort -u | grep -v \"^$f$\" )\n [ -z \"$REFS\" ] && echo \"ORPHAN: $f\"\ndone\n```\n\nTools that do this better:\n\n- **lychee** — checks for broken links; combined with a \"list all referenced files\" pass, can surface orphans\n- **markdown-link-check** — per-file link checker\n- **MkDocs / Docusaurus** — build-time warnings for unreferenced pages (with strict mode)\n\n## Archived docs are *intentionally* orphans\n\n`docs/archive/` is the one place where being unlinked is correct. The archive holds superseded content for historical reference; it should NOT be linked from current docs (otherwise readers might follow a stale link). Detection should exclude `docs/archive/`.\n\n```bash\nfind docs/ -name '*.md' -not -path 'docs/archive/*' …\n```\n\nReference: [lychee](https://github.com/lycheeverse/lychee) · [markdown-link-check](https://github.com/tcort/markdown-link-check)\n\n---\n\n\n## Empty Stubs and Placeholder Files\n\n**Impact: MEDIUM (Stubs make the docs tree look complete when it isn't — false confidence)**\n\nAn empty stub is a markdown file that exists but contains nothing useful: just a heading, just \"TBD\", just a placeholder paragraph. Stubs are dangerous because they make the docs index look populated while delivering nothing — readers click expecting content and get an empty page, eroding trust in the entire docs.\n\n## How to recognize stubs\n\n### 1. Files with only a heading\n\n```markdown\n# Deployment Guide\n\n```\n\n(One line, then nothing. 100% useless.)\n\n### 2. Files with \"TBD\" / \"Coming soon\" / placeholder content\n\n```markdown\n# API Reference\n\nTBD — will document this later.\n```\n\n```markdown\n# Architecture Overview\n\nThis document describes the architecture of the system.\n\n(coming soon)\n```\n\n### 3. Files with only Lorem ipsum / template content\n\n```markdown\n# Title\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit...\n```\n\n### 4. Files with the rule-template unchanged\n\n(A bigger problem in skill repos like this one) — an `.md` file containing the literal `## Rule Title Here` template heading that was never edited.\n\n### 5. Tiny files (\u003c 30 lines, almost no information)\n\nA 12-line `architecture.md` that just says \"We use Laravel and React\" is a stub even if it doesn't say \"TBD\".\n\n## Incorrect\n\n```markdown\n❌ A stub that pretends to be documentation\ndocs/guides/deployment.md (the entire file):\n\n# Deployment\n\nTBD\n```\n\n**Problems:**\n- A reader clicking this link gets nothing\n- The file's existence implies the topic is documented when it isn't\n- The \"TBD\" was written 18 months ago and forgotten\n\n## Correct — three options\n\n1. **Fill it in** — if you're going to write the doc, just write it (or at least cover the basics in 50+ lines). Don't create an empty file as a \"reminder\".\n\n2. **Delete it** — if there's no plan to fill it in, delete. Reduce false expectations.\n\n3. **Promote to an issue** — if you genuinely want to track \"we need to write this someday\", open a GitHub issue. The issue is for tracking; the markdown file is for content. Don't conflate them.\n\n```markdown\n✅ Replace with content OR delete\ndocs/guides/deployment.md (replacement):\n\n# Deployment\n\nProduction deploys happen automatically when a tag is pushed to `main`:\n\n1. Run tests locally: `php artisan test`\n2. Bump the version in `composer.json`\n3. Update CHANGELOG.md under `[Unreleased]`\n4. Tag: `git tag v1.2.3 && git push --tags`\n5. CI builds, tests, and deploys to production within 10 minutes\n…etc, with real content.\n```\n\n## Detection\n\n```bash\n# Files under 30 lines (likely stubs)\nfind docs/ -name '*.md' -type f -exec wc -l {} \\; | \\\n awk '$1 \u003c 30 { print }' | sort -n\n\n# Files containing \"TBD\" / \"Coming soon\" / \"Lorem\"\ngrep -rlEi '\\b(TBD|Coming soon|Lorem ipsum|placeholder|to be (done|written|filled))\\b' \\\n --include='*.md' docs/\n\n# Files matching the rule-template heading (in this skills repo)\ngrep -rln '^## Rule Title Here

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

--include='*.md' rules/\n\n# Files with only a title and no content\nfor f in $(find docs/ -name '*.md'); do\n CONTENT_LINES=$(grep -cvE '^(#|$)' \"$f\")\n [ \"$CONTENT_LINES\" -lt 3 ] && echo \"NEAR-EMPTY: $f\"\ndone\n```\n\n## Prevention: don't create empty files\n\nA common anti-pattern is creating the docs/ skeleton up-front: `touch docs/architecture.md docs/deployment.md docs/api.md`. This produces nothing but stubs.\n\nInstead: create each doc file *when you have content for it*. The folder structure can be empty until then.\n\nReference: [Diátaxis — \"documentation is not a TODO list\"](https://diataxis.fr/)\n\n---\n\n\n## Freshness Dates on Architecture Docs\n\n**Impact: MEDIUM (Without a freshness signal, every reader has to guess if the doc is still accurate)**\n\nAn architecture doc from 2022 might still be accurate or might be wildly out of date — without an explicit \"Last verified\" line, the reader can't tell. Adding a freshness date converts a doc from \"trust at your own risk\" to \"verified accurate as of YYYY-MM-DD\".\n\n## What needs a freshness date\n\n| Doc type | Freshness needed? |\n|---|---|\n| `docs/architecture/*` | **Yes** — these describe how the system *currently* works |\n| `docs/guides/*` | **Yes** — setup steps change |\n| `docs/runbooks/*` | **Yes** — procedures must be verified |\n| `docs/api/*` | If hand-written; not if auto-generated from code |\n| `docs/adr/*` | **No** — ADRs are dated by design (Date: field); they describe a past decision |\n| `docs/archive/*` | **No** — archived = frozen in time |\n| `README.md` | The \"Installation\" section — yes |\n\n## Correct\n\n```markdown\n# Architecture Overview\n\n_Last verified on a fresh checkout: 2026-04-12 by @asyraf_\n\n## System layout\n\nThe application is a Laravel monolith with an Inertia.js + React frontend …\n```\n\nOr as a table at the top:\n\n```markdown\n| Field | Value |\n|---|---|\n| Last verified | 2026-04-12 |\n| Verified by | @asyraf |\n| Owner | @org/platform |\n```\n\n## Incorrect\n\n```markdown\n❌ No freshness signal anywhere\n# Architecture Overview\n\nThe application uses MySQL for orders and Redis for sessions.\n…\n```\n\n(Was that true in 2022? Still true today? You'd have to ask.)\n\n## When to refresh\n\nSet a cadence per doc-type:\n\n- **Architecture docs** — quarterly (or when a major change ships)\n- **Runbooks** — verify by *executing* them quarterly (a \"walk-through\" exercise)\n- **Setup / install guides** — verify by running them on a clean checkout quarterly\n- **API references** — when they're hand-written, every time a public endpoint changes\n\nThe freshness date is part of the doc's content; updating it is part of the verification work, not separate paperwork.\n\n## Detection\n\n```bash\n# Find architecture / guide / runbook docs without a \"Last verified\" line\nfor f in docs/architecture/*.md docs/guides/*.md docs/runbooks/*.md; do\n grep -qE '_?Last verified' \"$f\" || echo \"NO FRESHNESS DATE: $f\"\ndone\n\n# Find docs whose \"Last verified\" date is > 12 months ago.\n# Compute threshold in bash (BSD awk on macOS doesn't have strftime/systime).\nTHRESHOLD=$(date -v-12m +%F 2>/dev/null || date -d '12 months ago' +%F)\ngrep -rEoH 'Last verified[: ]+[0-9]{4}-[0-9]{2}-[0-9]{2}' docs/ \\\n | awk -F: -v t=\"$THRESHOLD\" '$NF \u003c t { print \"STALE: \" $0 }'\n```\n\n## CI: prompt-rather-than-block\n\nDon't block PRs on freshness dates — that creates noise. Instead, prompt with a weekly digest:\n\n```yaml\n# .github/workflows/docs-freshness.yml — runs weekly\n- name: List stale docs\n run: |\n THRESHOLD=$(date -d '12 months ago' +%F)\n grep -rEoH 'Last verified[: ]+[0-9]{4}-[0-9]{2}-[0-9]{2}' docs/ \\\n | awk -F'verified[: ]+' -v t=\"$THRESHOLD\" '$2 \u003c t { print }' \\\n | tee stale-docs.txt\n # Open a GitHub issue listing stale docs (or post to Slack)\n```\n\nReference: [Diátaxis — keeping documentation maintained](https://diataxis.fr/) · Internal: `docs-outdated-architecture` rule in the [technical-debt](../../technical-debt) skill covers detection of stale content\n\n---\n\n\n## Archive Workflow for Superseded Docs\n\n**Impact: MEDIUM (Keep history without polluting current docs; deletion is reversible via git, but archive is more discoverable)**\n\nSome docs become wrong over time but contain context that's still useful: old architecture diagrams, prior deployment procedures, RFCs that were rejected. Don't delete them (you lose discoverability) and don't leave them in the current docs (you mislead readers). Move them to `docs/archive/\u003cyear>/` with a clear archive note.\n\n## The archive folder\n\n```\ndocs/archive/\n├── 2024/\n│ ├── q3-launch-plan.md\n│ ├── old-architecture-overview.md ← superseded by docs/architecture/overview.md\n│ └── deprecated-deployment.md\n└── 2025/\n ├── mysql-8-upgrade-plan.md ← upgrade completed; plan kept for history\n └── rejected-graphql-rfc.md\n```\n\nRules for the archive:\n\n1. **Sub-foldered by year** — keeps it scannable as the archive grows\n2. **Not linked from current docs** — being unlinked is the point (see `cleanup-orphans` for the exception)\n3. **First line of archived doc states why** — see the template below\n\n## Archive note (required at top of every archived doc)\n\n```markdown\n> **Archived 2025-11-03.** Superseded by [docs/architecture/overview.md](../../architecture/overview.md).\n> Kept for historical context only. Do not follow procedures or rely on facts in this doc.\n\n# Old Architecture Overview\n\n(Original content follows unchanged.)\n…\n```\n\nThe archive note has three jobs:\n- **Date** — when this was moved (not when it was written)\n- **Superseded by** — link to the current canonical version (if one exists)\n- **Warning** — explicit \"don't use this\" so misreaders self-correct\n\n## Incorrect\n\n```\n❌ Renaming instead of archiving\ndocs/architecture-OLD.md (clutters current docs/)\ndocs/architecture-archive.md (still in current docs/)\ndocs/old-deployment-DEPRECATED.md\n```\n\n(These are just renamed-in-place; readers and tools still find them mixed with current docs.)\n\n```\n❌ Outright deletion of unique context\n$ git rm docs/mysql-8-upgrade-plan.md # plan that documented WHY the upgrade was painful\n```\n\n(Git history preserves the file, but nobody will think to look for it via `git log`.)\n\n## Correct\n\n```bash\n# Move to archive with a one-line PR message\nmkdir -p docs/archive/2025\ngit mv docs/architecture-overview.md docs/archive/2025/old-architecture-overview.md\n$EDITOR docs/archive/2025/old-architecture-overview.md # add the archive note at top\ngit commit -m \"docs: archive old architecture overview (superseded by new overview)\"\n```\n\nThe new canonical doc (`docs/architecture/overview.md`) was either created in a separate PR or in the same PR as the archive move.\n\n## When to archive vs delete\n\n| Situation | Action |\n|---|---|\n| Old doc replaced by a new one, but the old contains historical decisions / context | Archive |\n| Migration plan that succeeded — the plan itself documents trade-offs | Archive |\n| Rejected RFC — the rejection rationale is useful history | Archive |\n| AI-generated plan file that was never relevant to begin with | Delete (see `cleanup-ai-junk`) |\n| Empty stub | Delete (see `cleanup-empty-stubs`) |\n| Near-duplicate of another doc with no unique content | Delete (see `cleanup-duplicates`) |\n| Personal notes / scratchpad | Delete |\n\n**Tip:** when in doubt, archive rather than delete. Deletion is final-feeling for readers (git history exists but is invisible); archive keeps the file visible to anyone browsing.\n\n## Detection — what's in the archive that shouldn't be\n\n```bash\n# Archived docs missing the archive note (bash; needs globstar for **)\nshopt -s globstar nullglob\nfor f in docs/archive/**/*.md; do\n head -3 \"$f\" | grep -q 'Archived' || echo \"MISSING ARCHIVE NOTE: $f\"\ndone\n\n# Archived docs linked from CURRENT docs (mistake — current docs should link to current docs).\n# Filter on the source-file path, not on the matched line.\nfind docs/ -name '*.md' -not -path 'docs/archive/*' -print0 | \\\n xargs -0 grep -lE 'docs/archive/'\n```\n\nReference: Diátaxis · [Internal: cleanup-orphans (archive is the intentional-orphan exception)]\n\n---\n\n\n## ADR Process — Proposed → Accepted → Superseded\n\n**Impact: HIGH (Architecture decisions need a paper trail; without it, the same debates repeat every year)**\n\nADRs (Architecture Decision Records) document **why** a non-trivial technical decision was made. The format is append-only with explicit status transitions, so future readers can trace the reasoning *and* see whether each decision is still in force.\n\n## What deserves an ADR\n\nWrite an ADR when:\n\n- **The decision is hard to reverse** — choice of database, framework, monorepo vs polyrepo\n- **It will be questioned later** — \"why did we pick MySQL over Postgres?\"\n- **There are real trade-offs** — multiple defensible options\n- **It affects code structure** for years to come\n\nDon't write an ADR for:\n\n- Small implementation choices (which loop pattern, which formatting)\n- Decisions documented better elsewhere (security policies → `SECURITY.md`)\n- Tactical config (which lint rule severity)\n\nA good rule of thumb: if you can explain the choice in a commit message, you don't need an ADR.\n\n## Status lifecycle\n\n```\nproposed → accepted → superseded\n ↘ rejected\n ↘ deprecated\n```\n\n| Status | Meaning |\n|---|---|\n| **Proposed** | Drafted, under team review |\n| **Accepted** | Active — this is what we do |\n| **Rejected** | Considered and dismissed (the reasoning is still useful) |\n| **Deprecated** | No longer the recommended approach, but legacy code may still follow it |\n| **Superseded by NNNN** | Replaced by a later ADR — link to it |\n\nStatus is part of the **content**, not the filename. Don't rename `0007-...` to `0007-DEPRECATED-...`. Update the `Status:` line.\n\n## ADR template (Michael Nygard's, lightly extended)\n\n```markdown\n# 0007. Rate-limit the public API\n\n| Field | Value |\n|---------|-------|\n| Date | 2026-05-16 |\n| Status | Accepted |\n| Owner | @org/platform |\n\n## Context\n\nWhat forces are at play? What problem are we solving? What constraints exist?\n\n(2–4 paragraphs)\n\n## Decision\n\nWhat did we decide to do? Be specific and concrete.\n\n(1–2 paragraphs)\n\n## Consequences\n\nWhat becomes easier as a result? What becomes harder? Any follow-on work?\n\n- Easier: …\n- Harder: …\n- Follow-up: …\n\n## Alternatives considered\n\n- **Option A** — rejected because …\n- **Option B** — rejected because …\n- **Status quo (do nothing)** — rejected because …\n\n## References\n\n- Linked issue/PR: #1234\n- Related ADRs: 0003, 0009\n```\n\n## When superseding an ADR\n\n1. **Write a new ADR** (e.g., `0019`) that describes the new decision\n2. **Update the old ADR's status** to `Superseded by ADR-0019` and link to it\n3. **Do NOT delete or rewrite the old ADR** — it remains as historical record\n\n```markdown\n# 0007. Rate-limit the public API\n\n| Field | Value |\n|---------|-------|\n| Date | 2026-05-16 |\n| Status | Superseded by [ADR-0019](0019-tiered-rate-limit-with-redis.md) |\n| Owner | @org/platform |\n\n(Original content unchanged below.)\n…\n```\n\n## Incorrect\n\n```\n❌ Decisions live only in Slack and PR descriptions\n\n(Team picks Redis over Memcached in a Slack thread; 6 months later, three new\nengineers ask why, and one of them re-litigates the decision.)\n```\n\n```\n❌ Status managed via filename renames\n\ndocs/adr/0007-rate-limit-DEPRECATED.md # makes the file harder to reference\ndocs/adr/0007-rate-limit-SUPERSEDED.md # breaks links from older code/PRs\n```\n\n```\n❌ Deleting superseded ADRs\n\ngit rm docs/adr/0007-... # erases the \"why we used to do it differently\"\n```\n\n## Detection\n\n```bash\n# ADRs without an explicit status line (covers both prose `Status: ...` and table `| Status | ... |`)\nfor f in docs/adr/[0-9]*.md; do\n grep -qiE '^(Status[:* ]|\\| *Status)' \"$f\" || echo \"NO STATUS: $f\"\ndone\n\n# ADRs claiming Superseded without a link (handles both prose and table-format)\ngrep -lEi '(^|\\| *)Status[:|* ]+.*Superseded' docs/adr/*.md | while read f; do\n grep -Ei 'Superseded by[: ]+\\[?[Aa][Dd][Rr]-?[0-9]+' \"$f\" >/dev/null \\\n || echo \"MISSING SUPERSEDED LINK: $f\"\ndone\n```\n\nReference: [adr.github.io](https://adr.github.io/) · [Michael Nygard's original post](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) · [adr-tools CLI](https://github.com/npryce/adr-tools)\n\n---\n\n\n## CHANGELOG-in-the-PR Discipline\n\n**Impact: HIGH (CHANGELOG entries written after the fact are always wrong; do it with the change)**\n\nA CHANGELOG only stays accurate if entries are written **in the same PR as the change**. The instant you defer to \"we'll batch this before release\", entries get missed, misattributed, or rewritten from memory inaccurately. The discipline is: every non-trivial PR adds a line under `[Unreleased]`.\n\n## The rule\n\nFor every PR that is **not** purely:\n- documentation-only\n- test-only\n- internal refactor (no behavior change visible to users)\n- trivial dependency bump (no breaking changes)\n\n…the PR must include a CHANGELOG entry under `[Unreleased]`.\n\n## Incorrect\n\n```\n❌ PR description: \"We'll add the CHANGELOG entry before the release\"\n\n(Three months later: 47 PRs have shipped, half of them touched user-visible\nbehavior, the release manager is reconstructing CHANGELOG entries from\nPR titles, and three changes are missing entirely.)\n```\n\n```\n❌ Generic CHANGELOG entries\n\n## [Unreleased]\n### Changed\n- Various improvements and bug fixes\n- Updated dependencies\n- Refactored internals\n```\n\n(Tells the reader nothing actionable. May as well be empty.)\n\n## Correct — entry written in the PR that ships the change\n\n```markdown\n## [Unreleased]\n\n### Added\n- Bulk user export endpoint (#412): `GET /api/users/export?format=csv` streams\n up to 1M rows without buffering.\n\n### Changed\n- Password hashing default switched from bcrypt to argon2id for new users.\n Existing bcrypt hashes are still accepted and re-hashed transparently on\n next successful login. (#421)\n\n### Fixed\n- Cursor pagination dropping the last row of each page (#387).\n```\n\nEach entry tells the user **what changed** and **why they might care**.\n\n## CI enforcement\n\nBlock PRs that don't add a CHANGELOG entry, with a skip-label for trivial PRs:\n\n```yaml\n# .github/workflows/changelog.yml\nname: CHANGELOG check\non: pull_request\n\njobs:\n changelog:\n runs-on: ubuntu-latest\n if: \"!contains(github.event.pull_request.labels.*.name, 'skip-changelog')\"\n steps:\n - uses: actions/checkout@v4\n with: { fetch-depth: 0 }\n - name: Require CHANGELOG entry\n run: |\n # Skip if PR only touches docs/tests/CI\n CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)\n IGNORE='^(\\.github/|docs/|tests?/|README\\.md|\\.gitignore|CHANGELOG\\.md)'\n NON_TRIVIAL=$(echo \"$CHANGED\" | grep -vE \"$IGNORE\" || true)\n if [ -n \"$NON_TRIVIAL\" ]; then\n CHANGELOG_DIFF=$(git diff origin/${{ github.base_ref }}...HEAD -- CHANGELOG.md)\n echo \"$CHANGELOG_DIFF\" | grep -qE '^\\+[^+]' \\\n || { echo \"::error::Add a CHANGELOG.md entry under [Unreleased] (or label 'skip-changelog')\"; exit 1; }\n fi\n```\n\nThe `skip-changelog` label is for genuine exceptions (docs-only, test-only). Don't abuse it.\n\n## On release: cut [Unreleased]\n\nWhen releasing a new version:\n\n1. Rename `[Unreleased]` to `[1.4.0] - 2026-05-16`\n2. Add a fresh empty `## [Unreleased]` heading on top\n3. Update the compare-link footnotes:\n\n```markdown\n[Unreleased]: https://github.com/org/repo/compare/v1.4.0...HEAD\n[1.4.0]: https://github.com/org/repo/compare/v1.3.0...v1.4.0\n```\n\n4. Commit with `chore(release): v1.4.0`\n5. Tag: `git tag v1.4.0`\n\n## Entry-writing tips\n\n- **Subject of the sentence is what changed**, not \"we\": \"Added X\" not \"We added X\"\n- **One PR = one bullet** in most cases; only split if the PR ships two distinct changes\n- **Link the PR or issue** in parentheses at the end\n- **Don't include refactors that aren't user-visible** — they belong in commit messages, not CHANGELOG\n\nReference: [Keep a Changelog](https://keepachangelog.com/) · [Conventional Commits → CHANGELOG generation](https://github.com/conventional-changelog/conventional-changelog) (if you prefer auto-generated)\n\n---\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":95825,"content_sha256":"2fa31b689f38cac44eeee40bb69dc2a94ead55c88836f0b8c081b267ad34b20e"},{"filename":"metadata.json","content":"{\n \"name\": \"Project Documentation\",\n \"version\": \"1.0.0\",\n \"description\": \"End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Covers folder structure, naming conventions, essential files, freshness, and cleanup of accumulated AI-generated junk. Supports bootstrap, audit, and reference modes.\",\n \"framework\": \"PHP / Laravel + Node / TypeScript / React\",\n \"license\": \"MIT\",\n \"author\": {\n \"name\": \"Agent Skills Contributors\",\n \"url\": \"https://github.com/AsyrafHussin/agent-skills\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AsyrafHussin/agent-skills\"\n },\n \"keywords\": [\n \"documentation\",\n \"docs\",\n \"markdown\",\n \"readme\",\n \"changelog\",\n \"adr\",\n \"architecture-decision-record\",\n \"project-setup\",\n \"bootstrap\",\n \"audit\",\n \"cleanup\",\n \"naming-conventions\",\n \"folder-structure\",\n \"lifecycle\",\n \"diataxis\",\n \"ai-slop\",\n \"conciseness\",\n \"content-quality\"\n ],\n \"categories\": [\n {\n \"id\": \"structure\",\n \"name\": \"Structure\",\n \"priority\": \"CRITICAL\",\n \"description\": \"Folder layout: root-level files, docs/ subfolders (architecture, adr, guides, runbooks, archive)\",\n \"ruleCount\": 3\n },\n {\n \"id\": \"naming\",\n \"name\": \"Naming\",\n \"priority\": \"CRITICAL\",\n \"description\": \"UPPERCASE root files, kebab-case in docs/, numbered ADRs, anti-patterns to avoid\",\n \"ruleCount\": 4\n },\n {\n \"id\": \"essential-files\",\n \"name\": \"Essential Files\",\n \"priority\": \"HIGH\",\n \"description\": \"README, CHANGELOG, LICENSE, CONTRIBUTING, SECURITY\",\n \"ruleCount\": 5\n },\n {\n \"id\": \"quality\",\n \"name\": \"Quality\",\n \"priority\": \"HIGH\",\n \"description\": \"Conciseness, AI-slop detection in content, heading hierarchy, code blocks, links\",\n \"ruleCount\": 5\n },\n {\n \"id\": \"cleanup\",\n \"name\": \"Cleanup\",\n \"priority\": \"HIGH\",\n \"description\": \"AI-generated junk files, duplicates, orphans, empty stubs\",\n \"ruleCount\": 4\n },\n {\n \"id\": \"lifecycle\",\n \"name\": \"Lifecycle\",\n \"priority\": \"MEDIUM\",\n \"description\": \"Freshness dates, archive workflow, ADR process, CHANGELOG discipline\",\n \"ruleCount\": 4\n }\n ],\n \"references\": [\n {\n \"title\": \"Keep a Changelog\",\n \"url\": \"https://keepachangelog.com/\",\n \"type\": \"guide\"\n },\n {\n \"title\": \"Semantic Versioning\",\n \"url\": \"https://semver.org/\",\n \"type\": \"reference\"\n },\n {\n \"title\": \"Architecture Decision Records (ADR)\",\n \"url\": \"https://adr.github.io/\",\n \"type\": \"guide\"\n },\n {\n \"title\": \"Diátaxis — documentation framework\",\n \"url\": \"https://diataxis.fr/\",\n \"type\": \"guide\"\n },\n {\n \"title\": \"Choose a License\",\n \"url\": \"https://choosealicense.com/\",\n \"type\": \"tool\"\n },\n {\n \"title\": \"Markdownlint\",\n \"url\": \"https://github.com/DavidAnson/markdownlint\",\n \"type\": \"tool\"\n },\n {\n \"title\": \"Lychee — link checker\",\n \"url\": \"https://github.com/lycheeverse/lychee\",\n \"type\": \"tool\"\n }\n ],\n \"tags\": [\n \"documentation\",\n \"project-setup\",\n \"markdown\",\n \"readme\",\n \"changelog\",\n \"adr\",\n \"cleanup\",\n \"conventions\",\n \"lifecycle\"\n ],\n \"lastUpdated\": \"2026-05-17\",\n \"rulesTotal\": 25\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":3358,"content_sha256":"18bfd4ec044111c47ec663990eec22ba8f26e1ca180e1083ca149f4f7f2632fd"},{"filename":"README.md","content":"# Project Documentation\n\nEnd-to-end documentation lifecycle for **PHP/Laravel and Node/TypeScript/React** projects. Covers folder structure, naming conventions, essential files, freshness, and cleanup of AI-generated junk. Supports bootstrap, audit, and reference modes.\n\n**Version:** 1.0.0\n\n## Overview\n\n- Bootstrap a new project's docs (what's needed + scaffolded templates)\n- Audit existing markdown files: keep / update / archive / delete / move\n- Detect AI-generated plan/summary junk (PLAN.md, IMPLEMENTATION-SUMMARY.md, etc.)\n- Enforce folder structure (`docs/architecture/`, `docs/adr/`, `docs/guides/`, `docs/runbooks/`, `docs/archive/`)\n- Enforce naming (`UPPERCASE.md` for root files, `kebab-case.md` for docs, numbered ADRs)\n- 25 rules across 6 categories\n\n## Categories\n\n### 1. Structure (CRITICAL)\nWhere docs live: root vs `docs/`; recommended sub-folder layout.\n\n### 2. Naming (CRITICAL)\n`UPPERCASE.md` for root, `kebab-case.md` for `docs/*`, numbered ADRs, no dates / no draft markers.\n\n### 3. Essential Files (HIGH)\nThe minimum every project needs: README, CHANGELOG, LICENSE — plus CONTRIBUTING and SECURITY for projects that warrant them.\n\n### 4. Quality (HIGH)\nConciseness over bloat, AI-slop fingerprints in prose, heading hierarchy, copy-pasteable code blocks, descriptive non-broken links.\n\n### 5. Cleanup (HIGH)\nDetect AI junk files, duplicates, orphans, and empty stubs; surface for triage without auto-deleting.\n\n### 6. Lifecycle (MEDIUM)\nFreshness dates, archive workflow, ADR process, CHANGELOG-on-PR discipline.\n\n## Usage\n\n```\nSet up docs for this Laravel project\nAudit the markdown files in this repo\nClean up the docs folder\nWhat docs does this project need?\nWhere should this architecture diagram go?\nHow should I name this ADR?\nFind stale documentation\n```\n\n## References\n\n- [Keep a Changelog](https://keepachangelog.com/)\n- [Architecture Decision Records (ADR)](https://adr.github.io/)\n- [Diátaxis — documentation framework](https://diataxis.fr/)\n- [Choose a License](https://choosealicense.com/)\n- [Markdownlint](https://github.com/DavidAnson/markdownlint)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2097,"content_sha256":"36e3e9af418aa7471c73adb6dc6067143bddb4a70ed06c41cd39122d1baa5390"},{"filename":"rules/_sections.md","content":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Structure (structure)\n\n**Impact:** CRITICAL\n**Description:** Where documentation lives in the repo — which files belong at the root, and how the `docs/` folder is organized. Structure decisions made early shape every future docs choice; getting them wrong scatters knowledge across the tree.\n\n## 2. Naming (naming)\n\n**Impact:** CRITICAL\n**Description:** Consistent file naming so readers can find docs without guessing — `UPPERCASE.md` for conventional root files, `kebab-case.md` for everything in `docs/`, numbered prefixes for ADRs, and an explicit list of anti-patterns (dates, draft markers, first-person names) to avoid.\n\n## 3. Essential Files (essential)\n\n**Impact:** HIGH\n**Description:** The minimum set every project needs (README, CHANGELOG, LICENSE) plus situational additions (CONTRIBUTING for external contributors, SECURITY for internet-facing services). These files set first impressions, legal posture, and incident response paths.\n\n## 4. Quality (quality)\n\n**Impact:** HIGH\n**Description:** Content quality inside docs — conciseness over bloat, detection of AI-generated slop patterns in prose (filler phrases, generic praise, closing sign-offs), valid heading hierarchy, copy-pasteable code blocks, and descriptive non-broken links. Where Cleanup removes whole junk files, Quality cuts junk inside otherwise-legitimate docs.\n\n## 5. Cleanup (cleanup)\n\n**Impact:** HIGH\n**Description:** Identifying junk that accumulates — AI-generated plan/summary files, near-duplicates of the same content, orphaned drafts nobody links to, and empty stubs. Cleanup is triaged, never automatic, so the user keeps final say on every delete.\n\n## 6. Lifecycle (lifecycle)\n\n**Impact:** MEDIUM\n**Description:** How docs are created, kept current, archived, and replaced. Covers freshness dates on architecture docs, the archive workflow, ADR proposed → accepted → superseded states, and the discipline of updating CHANGELOG in the same PR as the change.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2144,"content_sha256":"95068b5a5b8a27a7a5c91c2f43e374fd0e5e96013e57a78bf6eee7c0c114b4d8"},{"filename":"rules/_template.md","content":"---\ntitle: Rule Title Here\nimpact: CRITICAL|HIGH|MEDIUM\nimpactDescription: \"Specific consequence — e.g., 'Scatters knowledge; readers can't find docs'\"\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\n**Impact: LEVEL (impactDescription)**\n\n1-2 sentences explaining why this rule matters.\n\n## Incorrect\n\n```\n❌ Bad pattern — what to avoid\n```\n\n**Problems:**\n- Problem 1\n- Problem 2\n\n## Correct\n\n```\n✅ Good pattern — the convention\n```\n\n**Benefits:**\n- Benefit 1\n- Benefit 2\n\n## Detection / Enforcement (if applicable)\n\n```bash\n# Command or check that surfaces this pattern\n```\n\nReference: [Link](url)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":608,"content_sha256":"4942c20453e263d8009e905b8889b459e119391952e4319422d29661b193739b"},{"filename":"rules/cleanup-ai-junk.md","content":"---\ntitle: AI-Generated Junk Files\nimpact: HIGH\nimpactDescription: \"Agents create transient plan/summary files that masquerade as documentation\"\ntags: cleanup, ai-generated, junk, agents\n---\n\n## AI-Generated Junk Files\n\n**Impact: HIGH (Agents create transient plan/summary files that masquerade as documentation)**\n\nWhen you let coding agents work on a project, they tend to leave behind plan files, status summaries, and \"next steps\" notes that look like documentation but are actually transient working memory. After a few sprints, these accumulate as dozens of `PLAN.md`/`SUMMARY.md` files that nobody owns and nobody reads.\n\n## How to recognize AI junk\n\n### 1. By filename\n\nCommon patterns generated by AI agents:\n\n```\nPLAN.md IMPLEMENTATION-PLAN.md\nTODO.md IMPLEMENTATION-SUMMARY.md\nNEXT-STEPS.md REFACTOR-PLAN.md\nCOMPLETED.md REFACTOR-NOTES.md\nPROGRESS.md CHANGES.md (when CHANGELOG.md already exists)\nTASK-LIST.md SESSION-NOTES.md\nCONTEXT.md DECISIONS.md (when docs/adr/ exists)\nWORK-LOG.md DEBUG-NOTES.md\n```\n\n### 2. By content fingerprint\n\nAI-generated docs often have telltale signatures:\n\n- Trailing line: **\"🤖 Generated with Claude Code\"** / **\"Generated by GitHub Copilot\"** / **\"Created by Codex\"**\n- Phrases like **\"I have completed the following tasks\"** or **\"Here is a summary of the changes\"**\n- Bullet lists in past tense describing what was *just* done\n- \"Next steps:\" sections that were never actioned\n- Repeated boilerplate intros (\"This document describes...\")\n\n### 3. By context\n\n- File has 1 commit (created by the agent, never edited by a human)\n- Last commit message: `\"Add PLAN.md\"` / `\"Update progress\"` / `\"Add session summary\"`\n- Located at root of the repo (instead of properly organized under `docs/`)\n- Date in the filename: `notes-2025-09-14.md`, `plan-q3-2025.md`\n\n## Incorrect — what they look like\n\n```markdown\n❌ PLAN.md (committed by an agent, 8 months ago, never edited since)\n\n# Implementation Plan\n\nI'll be implementing the new user export feature in the following steps:\n\n## Phase 1: Schema (Day 1)\n- [x] Add export_jobs table\n- [x] Add foreign key to users\n- [x] Write migration\n\n## Phase 2: Service layer (Day 2-3)\n- [x] Create UserExportService\n- [ ] Add streaming support\n\n## Next steps\n- Discuss approach with team\n- Get sign-off before deployment\n\n🤖 Generated with Claude Code\n```\n\n**Problems:**\n- This was tracking work-in-progress; the work shipped 8 months ago\n- The unchecked item is either obsolete (already done in a later PR) or forgotten\n- File pollutes the repo's root and gets indexed by GitHub search\n- Future agents read it and treat it as authoritative current state\n\n## Correct — how to triage\n\nFor each candidate junk file, ask:\n\n1. **Is the work in this file complete?** → Delete. Work history is in git/PRs.\n2. **Is the work *abandoned*?** → Delete. (If you want to remember it, open an issue.)\n3. **Is the work ongoing?** → Move it to the issue tracker, not a markdown file in the repo.\n4. **Does it contain unique knowledge** (architecture decision, gotcha, runbook)? → Refactor into a proper doc:\n - Architecture decision → `docs/adr/NNNN-...md`\n - Gotcha / how-to → `docs/guides/...md`\n - Ops procedure → `docs/runbooks/...md`\n\n**Never auto-delete.** Always surface for the user's approval first.\n\n## Detection\n\n```bash\n# Filename patterns\nfind . -maxdepth 3 -type f -name '*.md' \\\n -not -path './node_modules/*' -not -path './vendor/*' \\\n | grep -Ei \\\n '(PLAN|TODO|SUMMARY|PROGRESS|NEXT-STEPS|COMPLETED|TASK-LIST|SESSION-NOTES|WORK-LOG|REFACTOR-NOTES|DEBUG-NOTES|CONTEXT|CHANGES)\\.md

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

\n\n# Content fingerprints — AI footer signatures\ngrep -rln -E '🤖 (Generated|Created) with|Co-Authored-By: Claude|Generated by (Copilot|Codex|Cursor)' \\\n --include='*.md' .\n\n# Markdown files committed by an agent and never edited by a human (single commit on the file).\n# Useful for catching transient plan/summary files that landed and were forgotten.\nfind . -name '*.md' -not -path './node_modules/*' -not -path './vendor/*' | while read f; do\n COMMITS=$(git log --oneline -- \"$f\" | wc -l | tr -d ' ')\n [ \"$COMMITS\" = \"1\" ] && echo \"SINGLE-COMMIT (likely auto-generated): $f\"\ndone\n```\n\n## Prevention\n\n- **Tell agents not to create plan files**: in your `CLAUDE.md` / `AGENTS.md` / `cursor.json`, instruct: \"Do not create PLAN.md, TODO.md, or progress-tracking markdown files in the repo. Use the conversation context or the issue tracker.\"\n- **CI gate**: block PRs that add `PLAN.md` / `TODO.md` / `SUMMARY.md` / etc. at root\n- **Periodic audit**: every quarter, run the detection above\n\n```yaml\n# .github/workflows/no-agent-junk.yml\n- name: Block AI-junk filenames\n run: |\n NEW=$(git diff --name-only --diff-filter=A origin/main...HEAD)\n BAD=$(echo \"$NEW\" | grep -Ei '^(PLAN|TODO|SUMMARY|PROGRESS|NEXT-STEPS)\\.md

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

)\n test -z \"$BAD\" || { echo \"Don't commit transient plan/summary files: $BAD\"; exit 1; }\n```\n\nReference: [Diátaxis — \"what documentation is not\"](https://diataxis.fr/) · [Internal: cleanup-orphans, cleanup-empty-stubs]\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5224,"content_sha256":"a94891a99985e346a22967fc417b7fe5c4ae9f87267afa079dd4f478824b0c65"},{"filename":"rules/cleanup-duplicates.md","content":"---\ntitle: Duplicate Documentation\nimpact: HIGH\nimpactDescription: \"Two copies of the same doc inevitably drift; readers can't tell which is current\"\ntags: cleanup, duplicates, consolidation\n---\n\n## Duplicate Documentation\n\n**Impact: HIGH (Two copies of the same doc inevitably drift; readers can't tell which is current)**\n\nDuplicate docs are the documentation equivalent of code duplication: every update has to happen in N places, and after the first inconsistency, no reader can tell which version is authoritative. Common causes are agents creating a \"v2\" of an existing doc, refactors that didn't delete the predecessor, and forks that drift.\n\n## How duplicates appear\n\n### 1. Near-identical content under different filenames\n\n```\ndocs/architecture.md\ndocs/architecture-overview.md\ndocs/system-design.md\n```\n\nThree files, ~70% overlapping content, last updated by different authors at different times.\n\n### 2. README sections duplicated in docs/\n\n```\nREADME.md # has \"## Installation\" with 30 lines\ndocs/guides/getting-started.md # has its own install section, slightly different\ndocs/guides/setup.md # also has install steps, outdated\n```\n\n### 3. \"Updated\" doc next to the old one\n\n```\ndocs/deployment.md\ndocs/deployment-v2.md\ndocs/deployment-NEW.md\n```\n\nAlways pick one; one of them is lying.\n\n### 4. ADR vs design doc covering the same decision\n\n```\ndocs/adr/0007-choose-mysql.md\ndocs/architecture/database-choice.md\n```\n\nThe ADR is the canonical record; the design doc duplicates it. Either delete the design doc or replace it with a one-line pointer to the ADR.\n\n## Incorrect\n\n```\n❌ Three near-duplicates of \"how to deploy\"\nREADME.md → ## Deployment (15 lines)\ndocs/guides/deployment.md (80 lines, slightly different commands)\ndocs/runbooks/deploy-production.md (60 lines, mostly the same as guides/deployment.md but with prod-specific notes)\n```\n\n**Problems:**\n- A new engineer asks \"how do I deploy?\" — gets three different answers\n- A change to deploy command must update all three (and usually doesn't)\n- Each copy ages at a different rate\n\n## Correct\n\n```\n✅ One canonical source; others link to it\n\nREADME.md\n ## Deployment\n See [docs/runbooks/deploy-production.md](docs/runbooks/deploy-production.md).\n\ndocs/guides/deployment.md ← DELETED, content merged into runbook\n\ndocs/runbooks/deploy-production.md ← the authoritative source\n # Deploy to production\n …full procedure…\n```\n\n**Benefits:**\n- One place to change\n- Readers reach the canonical version regardless of where they started\n- Future \"is this current?\" question has one file to check\n\n## Resolution strategies\n\nWhen you find duplicates:\n\n1. **Pick a winner** — the most current/most detailed/best-located version\n2. **Merge unique content** from the others into the winner\n3. **Delete the losers** — or replace them with one-line pointers if their location was useful (e.g., README → docs/runbooks)\n4. **Add a CI check** to prevent re-introduction (markdownlint can flag similar headings across files)\n\n## Detection\n\n```bash\n# Find near-duplicate markdown files (jscpd can do this)\nnpx jscpd --languages markdown --min-lines 20 docs/ README.md\n\n# Or compare line-by-line for high similarity (bash; needs globstar for **)\nshopt -s globstar nullglob\nfor a in docs/**/*.md; do\n for b in docs/**/*.md; do\n [[ \"$a\" \u003c \"$b\" ]] || continue\n SIM=$(diff \u003c(sort \"$a\") \u003c(sort \"$b\") | wc -l)\n LINES=$(wc -l \u003c \"$a\")\n if [ \"$LINES\" -gt 50 ] && [ \"$SIM\" -lt $((LINES / 4)) ]; then\n echo \"SIMILAR: $a \u003c-> $b (diff: $SIM lines)\"\n fi\n done\ndone\n\n# Files with the same H1 title\ngrep -rh '^# ' docs/ | sort | uniq -d\n```\n\nReference: [Diátaxis — \"one job per document\"](https://diataxis.fr/) · [jscpd](https://github.com/kucherenko/jscpd)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3791,"content_sha256":"84b485260765f00d828f4bc6649a7b7327154c995a7f61ce7ec4deaf3efb4c99"},{"filename":"rules/cleanup-empty-stubs.md","content":"---\ntitle: Empty Stubs and Placeholder Files\nimpact: MEDIUM\nimpactDescription: \"Stubs make the docs tree look complete when it isn't — false confidence\"\ntags: cleanup, stubs, placeholder, tbd\n---\n\n## Empty Stubs and Placeholder Files\n\n**Impact: MEDIUM (Stubs make the docs tree look complete when it isn't — false confidence)**\n\nAn empty stub is a markdown file that exists but contains nothing useful: just a heading, just \"TBD\", just a placeholder paragraph. Stubs are dangerous because they make the docs index look populated while delivering nothing — readers click expecting content and get an empty page, eroding trust in the entire docs.\n\n## How to recognize stubs\n\n### 1. Files with only a heading\n\n```markdown\n# Deployment Guide\n\n```\n\n(One line, then nothing. 100% useless.)\n\n### 2. Files with \"TBD\" / \"Coming soon\" / placeholder content\n\n```markdown\n# API Reference\n\nTBD — will document this later.\n```\n\n```markdown\n# Architecture Overview\n\nThis document describes the architecture of the system.\n\n(coming soon)\n```\n\n### 3. Files with only Lorem ipsum / template content\n\n```markdown\n# Title\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit...\n```\n\n### 4. Files with the rule-template unchanged\n\n(A bigger problem in skill repos like this one) — an `.md` file containing the literal `## Rule Title Here` template heading that was never edited.\n\n### 5. Tiny files (\u003c 30 lines, almost no information)\n\nA 12-line `architecture.md` that just says \"We use Laravel and React\" is a stub even if it doesn't say \"TBD\".\n\n## Incorrect\n\n```markdown\n❌ A stub that pretends to be documentation\ndocs/guides/deployment.md (the entire file):\n\n# Deployment\n\nTBD\n```\n\n**Problems:**\n- A reader clicking this link gets nothing\n- The file's existence implies the topic is documented when it isn't\n- The \"TBD\" was written 18 months ago and forgotten\n\n## Correct — three options\n\n1. **Fill it in** — if you're going to write the doc, just write it (or at least cover the basics in 50+ lines). Don't create an empty file as a \"reminder\".\n\n2. **Delete it** — if there's no plan to fill it in, delete. Reduce false expectations.\n\n3. **Promote to an issue** — if you genuinely want to track \"we need to write this someday\", open a GitHub issue. The issue is for tracking; the markdown file is for content. Don't conflate them.\n\n```markdown\n✅ Replace with content OR delete\ndocs/guides/deployment.md (replacement):\n\n# Deployment\n\nProduction deploys happen automatically when a tag is pushed to `main`:\n\n1. Run tests locally: `php artisan test`\n2. Bump the version in `composer.json`\n3. Update CHANGELOG.md under `[Unreleased]`\n4. Tag: `git tag v1.2.3 && git push --tags`\n5. CI builds, tests, and deploys to production within 10 minutes\n…etc, with real content.\n```\n\n## Detection\n\n```bash\n# Files under 30 lines (likely stubs)\nfind docs/ -name '*.md' -type f -exec wc -l {} \\; | \\\n awk '$1 \u003c 30 { print }' | sort -n\n\n# Files containing \"TBD\" / \"Coming soon\" / \"Lorem\"\ngrep -rlEi '\\b(TBD|Coming soon|Lorem ipsum|placeholder|to be (done|written|filled))\\b' \\\n --include='*.md' docs/\n\n# Files matching the rule-template heading (in this skills repo)\ngrep -rln '^## Rule Title Here

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

--include='*.md' rules/\n\n# Files with only a title and no content\nfor f in $(find docs/ -name '*.md'); do\n CONTENT_LINES=$(grep -cvE '^(#|$)' \"$f\")\n [ \"$CONTENT_LINES\" -lt 3 ] && echo \"NEAR-EMPTY: $f\"\ndone\n```\n\n## Prevention: don't create empty files\n\nA common anti-pattern is creating the docs/ skeleton up-front: `touch docs/architecture.md docs/deployment.md docs/api.md`. This produces nothing but stubs.\n\nInstead: create each doc file *when you have content for it*. The folder structure can be empty until then.\n\nReference: [Diátaxis — \"documentation is not a TODO list\"](https://diataxis.fr/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3799,"content_sha256":"7e989e6f0d78c703cddb2c79cb39cf054cbbd2ddee1d2e0602a651a86eaf6100"},{"filename":"rules/cleanup-orphans.md","content":"---\ntitle: Orphaned Documentation\nimpact: MEDIUM\nimpactDescription: \"Docs nobody links to are docs nobody finds — they age into landmines\"\ntags: cleanup, orphans, link-graph\n---\n\n## Orphaned Documentation\n\n**Impact: MEDIUM (Docs nobody links to are docs nobody finds — they age into landmines)**\n\nAn orphan is a markdown file that no other file links to. It exists on disk, gets indexed by `git grep`, but nobody navigates to it via the normal docs flow. Orphans are usually either: drafts that were abandoned, docs whose linker was deleted, or junk that was never properly integrated.\n\n## How to spot orphans\n\nThe simplest definition: **for every `.md` file in `docs/`, at least one *other* `.md` file should link to it**, directly or transitively from README.md.\n\nFiles that pass:\n- `README.md` links to `docs/architecture/overview.md` ✓\n- `docs/architecture/overview.md` links to `docs/architecture/data-model.md` ✓\n- `docs/adr/0001-...md` is linked from `docs/adr/README.md` (or implicitly from numbering) ✓\n\nFiles that *fail*:\n- `docs/old-onboarding-notes.md` — no inbound link\n- `docs/api-thoughts.md` — was linked from a deleted README section\n- `docs/guides/payments-deep-dive.md` — written 2 years ago, never linked\n\n## Incorrect\n\n```\n❌ Orphan piles up while linked docs stay current\ndocs/\n├── architecture/\n│ └── overview.md ← linked from README\n├── guides/\n│ ├── deployment.md ← linked\n│ ├── getting-started.md ← linked\n│ ├── old-onboarding.md ← ORPHAN (no inbound links)\n│ ├── api-thoughts.md ← ORPHAN\n│ └── payments-deep-dive.md ← ORPHAN\n└── archive/\n └── ...\n```\n\n**Problems:**\n- Orphans show up in `grep` results and confuse readers who land on them\n- Search engines (GitHub search, MkDocs site search) surface them\n- Future \"is this still relevant?\" question has no obvious answer\n\n## Correct — resolution options\n\nFor each orphan:\n\n1. **Link it** — if the content is current and useful, add an inbound link from the closest hub (README, `docs/README.md`, the relevant guide). Then it's no longer an orphan.\n2. **Archive it** — if it was once useful but no longer is, move to `docs/archive/\u003cyear>/`. The archive folder is *intentionally* unlinked from the main docs.\n3. **Delete it** — if it's a draft, near-duplicate, or AI junk, delete. Git history preserves it.\n\n**Never auto-delete.** Always surface for the user's approval.\n\n## Detection\n\n```bash\n# Find all .md files in docs/, then check whether any other .md links to them.\n# Uses two grep passes (one per pattern) since -F (fixed-string) doesn't support\n# alternation; bare paths with `.` characters would mis-trigger with -E.\nfind docs/ -name '*.md' -type f | while read f; do\n base=$(basename \"$f\" .md)\n rel=${f#./} # path without leading ./\n REFS=$( { grep -rlF \"$rel\" --include='*.md' \\\n --exclude-dir=node_modules --exclude-dir=vendor . ;\n grep -rlF \"$base\" --include='*.md' \\\n --exclude-dir=node_modules --exclude-dir=vendor . ; } \\\n | sort -u | grep -v \"^$f$\" )\n [ -z \"$REFS\" ] && echo \"ORPHAN: $f\"\ndone\n```\n\nTools that do this better:\n\n- **lychee** — checks for broken links; combined with a \"list all referenced files\" pass, can surface orphans\n- **markdown-link-check** — per-file link checker\n- **MkDocs / Docusaurus** — build-time warnings for unreferenced pages (with strict mode)\n\n## Archived docs are *intentionally* orphans\n\n`docs/archive/` is the one place where being unlinked is correct. The archive holds superseded content for historical reference; it should NOT be linked from current docs (otherwise readers might follow a stale link). Detection should exclude `docs/archive/`.\n\n```bash\nfind docs/ -name '*.md' -not -path 'docs/archive/*' …\n```\n\nReference: [lychee](https://github.com/lycheeverse/lychee) · [markdown-link-check](https://github.com/tcort/markdown-link-check)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4020,"content_sha256":"b14e57ac83dedf1eff1141897e6bad3c6d0e7d3aab2608bb7698292305ac3070"},{"filename":"rules/essential-changelog.md","content":"---\ntitle: CHANGELOG — Keep-a-Changelog Format\nimpact: HIGH\nimpactDescription: \"Release history readers can actually parse; the alternative is reading every commit\"\ntags: essential, changelog, releases\n---\n\n## CHANGELOG — Keep-a-Changelog Format\n\n**Impact: HIGH (Release history readers can actually parse; the alternative is reading every commit)**\n\nEvery project past its first release needs a CHANGELOG that humans can read in 30 seconds. \"What changed in v3.2?\" should not require `git log v3.1..v3.2`. The [Keep a Changelog](https://keepachangelog.com/) format is the de-facto standard: one section per version, grouped by change type, newest first.\n\n## Required structure\n\n```markdown\n# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/).\n\n## [Unreleased]\n\n### Added\n- Bullet for each new feature in flight\n\n## [1.3.0] - 2026-05-10\n\n### Added\n- Bulk user export endpoint (#412)\n- MySQL 8.4 support; bumped CI to test against it\n\n### Changed\n- Password hashing default switched to argon2id (existing bcrypt hashes still validated and re-hashed on login)\n\n### Fixed\n- Stripe webhook idempotency key handling (#398, #401)\n\n### Security\n- Bumped guzzlehttp/guzzle to 7.9.2 (CVE-2024-XXXX)\n\n## [1.2.1] - 2026-04-22\n\n### Fixed\n- Cursor pagination skipping the last row of each page (#387)\n\n## [1.2.0] - 2026-04-01\n\n### Added\n- Initial release of public REST API\n\n[Unreleased]: https://github.com/org/repo/compare/v1.3.0...HEAD\n[1.3.0]: https://github.com/org/repo/compare/v1.2.1...v1.3.0\n[1.2.1]: https://github.com/org/repo/compare/v1.2.0...v1.2.1\n[1.2.0]: https://github.com/org/repo/releases/tag/v1.2.0\n```\n\n## Change-type buckets (use these)\n\n| Bucket | When to use |\n|---|---|\n| **Added** | New features or capabilities |\n| **Changed** | Changes to existing functionality |\n| **Deprecated** | Soon-to-be-removed features (announce here, then remove next major) |\n| **Removed** | Removed features |\n| **Fixed** | Bug fixes |\n| **Security** | CVE patches, vulnerability fixes |\n\nDrop unused buckets from a given release (don't include empty `### Added` if there were no additions).\n\n## Incorrect\n\n```markdown\n❌ Free-form CHANGELOG that nobody can scan\n\n# Changes\n\nv1.3 — May 2026 — lots of stuff, see commits for details\nv1.2 — April 2026 — bugfixes and improvements\nv1.1 — March 2026 — added cool feature\nv1.0 — Feb 2026 — initial release\n```\n\n**Problems:**\n- \"Lots of stuff\" tells the reader nothing\n- No buckets, so security fixes mix with cosmetic changes\n- No dates in ISO format (international ambiguity)\n- No links to commits, PRs, or version tags\n\n## CHANGELOG-in-the-PR discipline\n\nA CHANGELOG entry should land in **the same PR** as the change it describes — not after the fact, not \"later in a batch\". This is the only way to keep it accurate.\n\nEnforce with CI:\n\n```yaml\n# .github/workflows/changelog.yml\n- name: CHANGELOG entry required for non-trivial changes\n run: |\n if git diff --name-only origin/main...HEAD | grep -qvE '^(\\.github/|docs/|tests?/|README\\.md|\\.gitignore)

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

; then\n git diff origin/main...HEAD -- CHANGELOG.md | grep -q '^+' \\\n || { echo \"Add a CHANGELOG.md entry\"; exit 1; }\n fi\n```\n\nSkip for docs-only, test-only, and trivial PRs (use a label or PR title prefix).\n\n## When to start a CHANGELOG\n\n- **Library / package** — from the very first published version\n- **Application** — from the first deployment to production OR the first release tagged with `v`\n- **Internal tool** — when more than one team consumes it\n\nFor pre-1.0 projects in flux, [Keep a Changelog](https://keepachangelog.com/en/1.1.0/#how) explicitly endorses starting with `0.x.y` and using `[Unreleased]` heavily.\n\nReference: [Keep a Changelog](https://keepachangelog.com/) · [Semantic Versioning](https://semver.org/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3955,"content_sha256":"4570a596d0bea0c52c9d549f1e57cc4749b49f005f5e5f9a197749223ce10c5e"},{"filename":"rules/essential-contributing.md","content":"---\ntitle: CONTRIBUTING — When and What\nimpact: MEDIUM\nimpactDescription: \"Sets the rules of engagement; without it, every PR is a guess\"\ntags: essential, contributing, process\n---\n\n## CONTRIBUTING — When and What\n\n**Impact: MEDIUM (Sets the rules of engagement; without it, every PR is a guess)**\n\nA `CONTRIBUTING.md` tells outside (or new-to-the-team) contributors how to participate: which branch to target, commit message style, test expectations, and how PRs get reviewed. GitHub auto-links to it whenever someone opens an issue or PR.\n\n## When required\n\n- **Open-source projects** — required from day one\n- **Internal projects with external collaborators** (consultants, contractors) — recommended\n- **Solo or single-team internal projects** — optional; an `.md` heading inside the README's \"Development\" section is fine for small teams\n\nIf the project is closed-source and only your team contributes, skip `CONTRIBUTING.md` and put the same information in the README.\n\n## Recommended structure\n\n```markdown\n# Contributing\n\nThanks for considering a contribution!\n\n## Branching\n\n- `main` is protected; PRs only\n- Feature branches: `feat/\u003cshort-desc>`\n- Bug fixes: `fix/\u003cshort-desc>`\n\n## Commit messages\n\nWe follow [Conventional Commits](https://www.conventionalcommits.org/):\n\n```\nfeat(auth): add MFA enrollment endpoint\nfix(checkout): handle expired payment intent\ndocs(readme): clarify install steps\n```\n\n## Development setup\n\n```bash\ngit clone …\ncomposer install && npm install\ncp .env.example .env\nphp artisan migrate\n```\n\n## Running tests\n\n```bash\nphp artisan test # PHP / Laravel\nnpm test # JS / TS\n```\n\nAll tests must pass; new features need new tests.\n\n## Pull request checklist\n\n- [ ] CHANGELOG.md updated (under `[Unreleased]`)\n- [ ] Tests added / updated\n- [ ] `composer.lock` / `package-lock.json` committed if deps changed\n- [ ] Linked to a GitHub issue (if applicable)\n\n## Review process\n\n- One maintainer approval required\n- CI must pass (`tests`, `lint`, `markdown`)\n- Squash-merge — keep `main` history linear\n\n## Code of Conduct\n\nThis project follows the [Contributor Covenant](CODE_OF_CONDUCT.md).\n```\n\n## Incorrect\n\n```markdown\n❌ Vague CONTRIBUTING that says \"just open a PR\"\n\n# Contributing\n\nOpen a PR. We'll review.\n```\n\n**Problems:**\n- No branch policy → reviewer has to comment on every PR\n- No test expectation → contributors merge without running them\n- No commit style → CHANGELOG generation is harder, history is noisy\n\n## Detection\n\n```bash\n# Does CONTRIBUTING exist where needed?\nif [ -f \"package.json\" ] && grep -q '\"private\"' package.json; then\n : # internal, optional\nelse\n test -f CONTRIBUTING.md || echo \"MISSING CONTRIBUTING.md (recommended for non-private projects)\"\nfi\n```\n\nReference: [GitHub — Setting guidelines for repository contributors](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors) · [Conventional Commits](https://www.conventionalcommits.org/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3037,"content_sha256":"8b8a655fc3937e718a002fa799ee8edd4bd7dfb8568f37a6d443ae024db2cfde"},{"filename":"rules/essential-license.md","content":"---\ntitle: LICENSE — Required Legal Clarity\nimpact: CRITICAL\nimpactDescription: \"Without a license, code is technically un-reusable; many companies' legal review blocks adoption outright\"\ntags: essential, license, legal\n---\n\n## LICENSE — Required Legal Clarity\n\n**Impact: CRITICAL (Without a license, code is technically un-reusable; many companies' legal review blocks adoption outright)**\n\nA repository without a LICENSE file means **all rights reserved** by default in most jurisdictions — nobody can legally use, copy, or modify the code, even if it's public. Every serious open-source project has one, and most companies' legal teams reject internal adoption of unlicensed dependencies.\n\n## Required\n\n```\nLICENSE # at repo root, no extension by convention\n```\n\nContents: the full text of the license you've chosen. For MIT, that's the standard ~20-line text with `\u003cyear>` and `\u003ccopyright holder>` filled in.\n\n## Choosing a license\n\nUse [choosealicense.com](https://choosealicense.com/) — it asks 2–3 questions and recommends one. Common picks:\n\n| License | When | Notes |\n|---|---|---|\n| **MIT** | Permissive; you want maximum adoption | Short, well-known, allows commercial use |\n| **Apache-2.0** | Permissive + explicit patent grant | Slightly longer; common for company-backed OSS |\n| **AGPL-3.0** | Copyleft + network-use clause | SaaS deployments must share modifications |\n| **BSL** / **Elastic 2.0** | Source-available, time-delayed open | \"Open-ish\" — verify it suits your goal |\n| **Proprietary** | Closed-source | Add `LICENSE` saying \"All rights reserved\" + contact for commercial terms |\n\nFor internal-only repos, still include a `LICENSE` file stating the company holds copyright and the code is for internal use only. This removes ambiguity for departing employees and contractors.\n\n## Incorrect\n\n```\n❌ No LICENSE file at all\n.\n├── README.md\n├── CHANGELOG.md\n└── src/...\n```\n\n```\n❌ Wrong placement / naming\n.\n├── docs/license.txt (should be at root, named LICENSE)\n└── LICENSE.MD (works but unconventional case)\n```\n\n```\n❌ \"MIT\" mentioned only in README, no file\nREADME:\n> ## License\n> MIT\n```\n\n(GitHub will not detect this as MIT licensed; license badges and tooling will fail.)\n\n## Correct\n\n```\n✅ LICENSE at root\n.\n├── LICENSE ← the full license text\n├── README.md\n└── src/...\n```\n\nREADME links to it:\n\n```markdown\n## License\n\n[MIT](LICENSE)\n```\n\n## License consistency in package manifests\n\nKeep the manifest field in sync with the LICENSE file:\n\n```json\n// package.json\n{\n \"license\": \"MIT\"\n}\n```\n\n```json\n// composer.json\n{\n \"license\": \"MIT\"\n}\n```\n\nMismatch (LICENSE says MIT, `package.json` says Apache-2.0) trips up `npm audit`, license-scanning tools, and human reviewers.\n\n## Detection\n\n```bash\ntest -f LICENSE -o -f LICENSE.md -o -f LICENSE.txt || echo \"MISSING LICENSE\"\n\n# Compare LICENSE to package.json (rough sanity check)\nLICENSE_NAME=$(grep -oE '(MIT|Apache|BSD|GPL|ISC|MPL)' LICENSE | head -1)\nPKG_LICENSE=$(node -e \"console.log(require('./package.json').license)\" 2>/dev/null)\necho \"LICENSE file: $LICENSE_NAME, package.json: $PKG_LICENSE\"\n```\n\nReference: [Choose a License](https://choosealicense.com/) · [GitHub — Licensing a repository](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository) · [SPDX license list](https://spdx.org/licenses/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3506,"content_sha256":"e98729ab581a933deded33ed928c92b8225bc0d93f8fea071cccb4a1fcb782b8"},{"filename":"rules/essential-readme.md","content":"---\ntitle: README — Required Content\nimpact: CRITICAL\nimpactDescription: \"First impression for every reader; missing or stale README is the #1 onboarding blocker\"\ntags: essential, readme, onboarding\n---\n\n## README — Required Content\n\n**Impact: CRITICAL (First impression for every reader; missing or stale README is the #1 onboarding blocker)**\n\nThe README is the front door. Every new contributor, every dependency review, every \"is this project maintained?\" check starts here. A weak README means each onboarding repeats the same questions in Slack.\n\n## Required sections (in order)\n\n````markdown\n# Project Name\n\nOne-sentence description: what does this do, for whom.\n\n[Badges optional — build, version, license]\n\n## Overview\n\n2–3 sentences expanding on the one-liner. Who uses this, what problem it solves.\n\n## Requirements\n\n- PHP 8.3+ / Node 22+\n- MySQL 8.0+ / Redis (if used)\n- Any other prerequisites\n\n## Installation\n\n```bash\ngit clone …\ncomposer install\nnpm install\ncp .env.example .env\nphp artisan key:generate\nphp artisan migrate\n```\n\n## Usage\n\nHow to run it locally:\n\n```bash\nphp artisan serve # backend\nnpm run dev # frontend\n```\n\nHow to run tests:\n\n```bash\nphp artisan test\nnpm test\n```\n\n## Documentation\n\n- [Architecture overview](docs/architecture/overview.md)\n- [Deployment guide](docs/guides/deployment.md)\n- [API reference](docs/api/openapi.yaml)\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\n[MIT](LICENSE) (or whichever)\n````\n\n## Incorrect\n\n```markdown\n❌ Sparse README missing essentials\n# my-app\n\nA web app.\n\n## Setup\n\nRun npm install. Should work.\n```\n\n**Problems:**\n- No description of *what* the app does or *for whom*\n- No requirements (which Node version? what database?)\n- \"Should work\" — when it doesn't, the reader has no context\n- No path to architecture docs, deployment, contributing\n- No license — many companies' legal review will block adoption\n\n## What to include vs leave out\n\n**Include:**\n- One-sentence description\n- Requirements (with versions)\n- Install / run / test commands\n- Links to deeper docs (`docs/`)\n- License\n\n**Don't include:**\n- A full API reference (link to `docs/api/`)\n- Detailed architecture (link to `docs/architecture/overview.md`)\n- Release history (that's `CHANGELOG.md`)\n- Long FAQs (`docs/guides/troubleshooting.md`)\n\nA README that's longer than one screen is doing too much. Split it.\n\n## Verification\n\nAdd a `Last verified: YYYY-MM-DD` line at the top of installation. Once a quarter, walk through the steps from scratch on a clean checkout and update the date.\n\n```markdown\n## Installation\n\n_Last verified on a clean checkout: 2026-04-12_\n\ngit clone …\n…\n```\n\n## Detection\n\n```bash\n# Does README exist and have minimum required sections?\ntest -f README.md || echo \"MISSING README.md\"\nfor h in 'Overview' 'Requirements' 'Installation' 'Usage' 'License'; do\n grep -q \"^## $h\" README.md || echo \"MISSING SECTION: ## $h\"\ndone\n```\n\nReference: [Make a README](https://www.makeareadme.com/) · [GitHub — About READMEs](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3184,"content_sha256":"69fa2e251177b96c88268c3a8aa76f97e381f550a1087acb190f35bed2873ad5"},{"filename":"rules/essential-security.md","content":"---\ntitle: SECURITY.md — Vulnerability Reporting Policy\nimpact: HIGH\nimpactDescription: \"Without a clear policy, researchers go public; with one, they email you privately\"\ntags: essential, security, vulnerability-disclosure\n---\n\n## SECURITY.md — Vulnerability Reporting Policy\n\n**Impact: HIGH (Without a clear policy, researchers go public; with one, they email you privately)**\n\nA `SECURITY.md` tells security researchers how to report a vulnerability to you privately. Without it, well-intentioned researchers either open a public GitHub issue (instant CVE) or give up and never report. GitHub's \"Security\" tab links directly to this file.\n\n## When required\n\n- **Any internet-facing service** — required\n- **Open-source libraries** — required from first public release\n- **Internal-only projects** — optional, but useful for clarity even internally\n- **Closed-source applications used by customers** — recommended\n\n## Recommended structure\n\n```markdown\n# Security Policy\n\n## Supported versions\n\nWe provide security updates for the following versions:\n\n| Version | Supported |\n|---------|-----------|\n| 3.x | ✅ |\n| 2.x | ✅ (until 2026-12) |\n| 1.x | ❌ (EOL) |\n\n## Reporting a vulnerability\n\n**Please do not file a public GitHub issue for security vulnerabilities.**\n\nEmail us at **[email protected]** with:\n- A description of the issue\n- Steps to reproduce\n- Affected versions (if known)\n- Any proof-of-concept code (optional)\n\nWe acknowledge reports within **2 business days** and aim to issue a fix within **30 days** for high/critical severity.\n\nIf you prefer encrypted communication, our PGP key is at \u003chttps://example.com/security.asc>.\n\n## Disclosure timeline\n\n- Day 0: We receive your report and acknowledge\n- Day 1–14: Triage, reproduce, develop fix\n- Day 15–30: Release patched version, notify users\n- Day 30+: Public disclosure (we'll credit you unless you prefer anonymity)\n\n## Safe harbor\n\nWe will not pursue legal action against researchers who:\n- Make a good-faith effort to follow this policy\n- Avoid privacy violations, service disruption, or destruction of data\n- Give us reasonable time to remediate before public disclosure\n```\n\n## Incorrect\n\n```markdown\n❌ No SECURITY.md at all on a public web app\n\n(No mechanism for reporting; researchers either guess an email or go public.)\n```\n\n```markdown\n❌ \"Just open an issue\"\n\n# Security\n\nIf you find a security issue, please open a GitHub issue.\n```\n\n(This guarantees public disclosure of vulnerabilities — the worst possible default.)\n\n## Use GitHub's built-in private reporting\n\nGitHub supports **Private Security Advisories** — researchers can report via the Security tab without exposing the issue publicly. Enable in Settings → Security → \"Private vulnerability reporting\". Reference it in `SECURITY.md`:\n\n```markdown\nYou may also use **GitHub's private vulnerability reporting**:\n\u003chttps://github.com/org/repo/security/advisories/new>\n```\n\n## Detection\n\n```bash\n# Internet-facing? Public repo? — should have SECURITY.md\ntest -f SECURITY.md || test -f .github/SECURITY.md \\\n || echo \"MISSING SECURITY.md (required for public/internet-facing projects)\"\n```\n\nReference: [GitHub — Adding a security policy](https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository) · [GitHub Private Vulnerability Reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) · [Disclose.io safe-harbor template](https://github.com/disclose/disclose)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3629,"content_sha256":"6498f2823df5ebe8fc068f8c35b37449887bf59d69e8c2eeca66766793ae78a3"},{"filename":"rules/lifecycle-adr-process.md","content":"---\ntitle: ADR Process — Proposed → Accepted → Superseded\nimpact: HIGH\nimpactDescription: \"Architecture decisions need a paper trail; without it, the same debates repeat every year\"\ntags: lifecycle, adr, architecture-decision-record\n---\n\n## ADR Process — Proposed → Accepted → Superseded\n\n**Impact: HIGH (Architecture decisions need a paper trail; without it, the same debates repeat every year)**\n\nADRs (Architecture Decision Records) document **why** a non-trivial technical decision was made. The format is append-only with explicit status transitions, so future readers can trace the reasoning *and* see whether each decision is still in force.\n\n## What deserves an ADR\n\nWrite an ADR when:\n\n- **The decision is hard to reverse** — choice of database, framework, monorepo vs polyrepo\n- **It will be questioned later** — \"why did we pick MySQL over Postgres?\"\n- **There are real trade-offs** — multiple defensible options\n- **It affects code structure** for years to come\n\nDon't write an ADR for:\n\n- Small implementation choices (which loop pattern, which formatting)\n- Decisions documented better elsewhere (security policies → `SECURITY.md`)\n- Tactical config (which lint rule severity)\n\nA good rule of thumb: if you can explain the choice in a commit message, you don't need an ADR.\n\n## Status lifecycle\n\n```\nproposed → accepted → superseded\n ↘ rejected\n ↘ deprecated\n```\n\n| Status | Meaning |\n|---|---|\n| **Proposed** | Drafted, under team review |\n| **Accepted** | Active — this is what we do |\n| **Rejected** | Considered and dismissed (the reasoning is still useful) |\n| **Deprecated** | No longer the recommended approach, but legacy code may still follow it |\n| **Superseded by NNNN** | Replaced by a later ADR — link to it |\n\nStatus is part of the **content**, not the filename. Don't rename `0007-...` to `0007-DEPRECATED-...`. Update the `Status:` line.\n\n## ADR template (Michael Nygard's, lightly extended)\n\n```markdown\n# 0007. Rate-limit the public API\n\n| Field | Value |\n|---------|-------|\n| Date | 2026-05-16 |\n| Status | Accepted |\n| Owner | @org/platform |\n\n## Context\n\nWhat forces are at play? What problem are we solving? What constraints exist?\n\n(2–4 paragraphs)\n\n## Decision\n\nWhat did we decide to do? Be specific and concrete.\n\n(1–2 paragraphs)\n\n## Consequences\n\nWhat becomes easier as a result? What becomes harder? Any follow-on work?\n\n- Easier: …\n- Harder: …\n- Follow-up: …\n\n## Alternatives considered\n\n- **Option A** — rejected because …\n- **Option B** — rejected because …\n- **Status quo (do nothing)** — rejected because …\n\n## References\n\n- Linked issue/PR: #1234\n- Related ADRs: 0003, 0009\n```\n\n## When superseding an ADR\n\n1. **Write a new ADR** (e.g., `0019`) that describes the new decision\n2. **Update the old ADR's status** to `Superseded by ADR-0019` and link to it\n3. **Do NOT delete or rewrite the old ADR** — it remains as historical record\n\n```markdown\n# 0007. Rate-limit the public API\n\n| Field | Value |\n|---------|-------|\n| Date | 2026-05-16 |\n| Status | Superseded by [ADR-0019](0019-tiered-rate-limit-with-redis.md) |\n| Owner | @org/platform |\n\n(Original content unchanged below.)\n…\n```\n\n## Incorrect\n\n```\n❌ Decisions live only in Slack and PR descriptions\n\n(Team picks Redis over Memcached in a Slack thread; 6 months later, three new\nengineers ask why, and one of them re-litigates the decision.)\n```\n\n```\n❌ Status managed via filename renames\n\ndocs/adr/0007-rate-limit-DEPRECATED.md # makes the file harder to reference\ndocs/adr/0007-rate-limit-SUPERSEDED.md # breaks links from older code/PRs\n```\n\n```\n❌ Deleting superseded ADRs\n\ngit rm docs/adr/0007-... # erases the \"why we used to do it differently\"\n```\n\n## Detection\n\n```bash\n# ADRs without an explicit status line (covers both prose `Status: ...` and table `| Status | ... |`)\nfor f in docs/adr/[0-9]*.md; do\n grep -qiE '^(Status[:* ]|\\| *Status)' \"$f\" || echo \"NO STATUS: $f\"\ndone\n\n# ADRs claiming Superseded without a link (handles both prose and table-format)\ngrep -lEi '(^|\\| *)Status[:|* ]+.*Superseded' docs/adr/*.md | while read f; do\n grep -Ei 'Superseded by[: ]+\\[?[Aa][Dd][Rr]-?[0-9]+' \"$f\" >/dev/null \\\n || echo \"MISSING SUPERSEDED LINK: $f\"\ndone\n```\n\nReference: [adr.github.io](https://adr.github.io/) · [Michael Nygard's original post](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) · [adr-tools CLI](https://github.com/npryce/adr-tools)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4541,"content_sha256":"f25a7471b16f090efc1c3d57e02b82f599fe4d4c4aa8f2a5a2d8b82d1a9eae5b"},{"filename":"rules/lifecycle-archive.md","content":"---\ntitle: Archive Workflow for Superseded Docs\nimpact: MEDIUM\nimpactDescription: \"Keep history without polluting current docs; deletion is reversible via git, but archive is more discoverable\"\ntags: lifecycle, archive, history\n---\n\n## Archive Workflow for Superseded Docs\n\n**Impact: MEDIUM (Keep history without polluting current docs; deletion is reversible via git, but archive is more discoverable)**\n\nSome docs become wrong over time but contain context that's still useful: old architecture diagrams, prior deployment procedures, RFCs that were rejected. Don't delete them (you lose discoverability) and don't leave them in the current docs (you mislead readers). Move them to `docs/archive/\u003cyear>/` with a clear archive note.\n\n## The archive folder\n\n```\ndocs/archive/\n├── 2024/\n│ ├── q3-launch-plan.md\n│ ├── old-architecture-overview.md ← superseded by docs/architecture/overview.md\n│ └── deprecated-deployment.md\n└── 2025/\n ├── mysql-8-upgrade-plan.md ← upgrade completed; plan kept for history\n └── rejected-graphql-rfc.md\n```\n\nRules for the archive:\n\n1. **Sub-foldered by year** — keeps it scannable as the archive grows\n2. **Not linked from current docs** — being unlinked is the point (see `cleanup-orphans` for the exception)\n3. **First line of archived doc states why** — see the template below\n\n## Archive note (required at top of every archived doc)\n\n```markdown\n> **Archived 2025-11-03.** Superseded by [docs/architecture/overview.md](../../architecture/overview.md).\n> Kept for historical context only. Do not follow procedures or rely on facts in this doc.\n\n# Old Architecture Overview\n\n(Original content follows unchanged.)\n…\n```\n\nThe archive note has three jobs:\n- **Date** — when this was moved (not when it was written)\n- **Superseded by** — link to the current canonical version (if one exists)\n- **Warning** — explicit \"don't use this\" so misreaders self-correct\n\n## Incorrect\n\n```\n❌ Renaming instead of archiving\ndocs/architecture-OLD.md (clutters current docs/)\ndocs/architecture-archive.md (still in current docs/)\ndocs/old-deployment-DEPRECATED.md\n```\n\n(These are just renamed-in-place; readers and tools still find them mixed with current docs.)\n\n```\n❌ Outright deletion of unique context\n$ git rm docs/mysql-8-upgrade-plan.md # plan that documented WHY the upgrade was painful\n```\n\n(Git history preserves the file, but nobody will think to look for it via `git log`.)\n\n## Correct\n\n```bash\n# Move to archive with a one-line PR message\nmkdir -p docs/archive/2025\ngit mv docs/architecture-overview.md docs/archive/2025/old-architecture-overview.md\n$EDITOR docs/archive/2025/old-architecture-overview.md # add the archive note at top\ngit commit -m \"docs: archive old architecture overview (superseded by new overview)\"\n```\n\nThe new canonical doc (`docs/architecture/overview.md`) was either created in a separate PR or in the same PR as the archive move.\n\n## When to archive vs delete\n\n| Situation | Action |\n|---|---|\n| Old doc replaced by a new one, but the old contains historical decisions / context | Archive |\n| Migration plan that succeeded — the plan itself documents trade-offs | Archive |\n| Rejected RFC — the rejection rationale is useful history | Archive |\n| AI-generated plan file that was never relevant to begin with | Delete (see `cleanup-ai-junk`) |\n| Empty stub | Delete (see `cleanup-empty-stubs`) |\n| Near-duplicate of another doc with no unique content | Delete (see `cleanup-duplicates`) |\n| Personal notes / scratchpad | Delete |\n\n**Tip:** when in doubt, archive rather than delete. Deletion is final-feeling for readers (git history exists but is invisible); archive keeps the file visible to anyone browsing.\n\n## Detection — what's in the archive that shouldn't be\n\n```bash\n# Archived docs missing the archive note (bash; needs globstar for **)\nshopt -s globstar nullglob\nfor f in docs/archive/**/*.md; do\n head -3 \"$f\" | grep -q 'Archived' || echo \"MISSING ARCHIVE NOTE: $f\"\ndone\n\n# Archived docs linked from CURRENT docs (mistake — current docs should link to current docs).\n# Filter on the source-file path, not on the matched line.\nfind docs/ -name '*.md' -not -path 'docs/archive/*' -print0 | \\\n xargs -0 grep -lE 'docs/archive/'\n```\n\nReference: Diátaxis · [Internal: cleanup-orphans (archive is the intentional-orphan exception)]\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4419,"content_sha256":"e05c78d554791eba2c81361dfc08e2249583f41d7b7f23cfa7c6e1d3ed4aead7"},{"filename":"rules/lifecycle-changelog-discipline.md","content":"---\ntitle: CHANGELOG-in-the-PR Discipline\nimpact: HIGH\nimpactDescription: \"CHANGELOG entries written after the fact are always wrong; do it with the change\"\ntags: lifecycle, changelog, process, ci\n---\n\n## CHANGELOG-in-the-PR Discipline\n\n**Impact: HIGH (CHANGELOG entries written after the fact are always wrong; do it with the change)**\n\nA CHANGELOG only stays accurate if entries are written **in the same PR as the change**. The instant you defer to \"we'll batch this before release\", entries get missed, misattributed, or rewritten from memory inaccurately. The discipline is: every non-trivial PR adds a line under `[Unreleased]`.\n\n## The rule\n\nFor every PR that is **not** purely:\n- documentation-only\n- test-only\n- internal refactor (no behavior change visible to users)\n- trivial dependency bump (no breaking changes)\n\n…the PR must include a CHANGELOG entry under `[Unreleased]`.\n\n## Incorrect\n\n```\n❌ PR description: \"We'll add the CHANGELOG entry before the release\"\n\n(Three months later: 47 PRs have shipped, half of them touched user-visible\nbehavior, the release manager is reconstructing CHANGELOG entries from\nPR titles, and three changes are missing entirely.)\n```\n\n```\n❌ Generic CHANGELOG entries\n\n## [Unreleased]\n### Changed\n- Various improvements and bug fixes\n- Updated dependencies\n- Refactored internals\n```\n\n(Tells the reader nothing actionable. May as well be empty.)\n\n## Correct — entry written in the PR that ships the change\n\n```markdown\n## [Unreleased]\n\n### Added\n- Bulk user export endpoint (#412): `GET /api/users/export?format=csv` streams\n up to 1M rows without buffering.\n\n### Changed\n- Password hashing default switched from bcrypt to argon2id for new users.\n Existing bcrypt hashes are still accepted and re-hashed transparently on\n next successful login. (#421)\n\n### Fixed\n- Cursor pagination dropping the last row of each page (#387).\n```\n\nEach entry tells the user **what changed** and **why they might care**.\n\n## CI enforcement\n\nBlock PRs that don't add a CHANGELOG entry, with a skip-label for trivial PRs:\n\n```yaml\n# .github/workflows/changelog.yml\nname: CHANGELOG check\non: pull_request\n\njobs:\n changelog:\n runs-on: ubuntu-latest\n if: \"!contains(github.event.pull_request.labels.*.name, 'skip-changelog')\"\n steps:\n - uses: actions/checkout@v4\n with: { fetch-depth: 0 }\n - name: Require CHANGELOG entry\n run: |\n # Skip if PR only touches docs/tests/CI\n CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)\n IGNORE='^(\\.github/|docs/|tests?/|README\\.md|\\.gitignore|CHANGELOG\\.md)'\n NON_TRIVIAL=$(echo \"$CHANGED\" | grep -vE \"$IGNORE\" || true)\n if [ -n \"$NON_TRIVIAL\" ]; then\n CHANGELOG_DIFF=$(git diff origin/${{ github.base_ref }}...HEAD -- CHANGELOG.md)\n echo \"$CHANGELOG_DIFF\" | grep -qE '^\\+[^+]' \\\n || { echo \"::error::Add a CHANGELOG.md entry under [Unreleased] (or label 'skip-changelog')\"; exit 1; }\n fi\n```\n\nThe `skip-changelog` label is for genuine exceptions (docs-only, test-only). Don't abuse it.\n\n## On release: cut [Unreleased]\n\nWhen releasing a new version:\n\n1. Rename `[Unreleased]` to `[1.4.0] - 2026-05-16`\n2. Add a fresh empty `## [Unreleased]` heading on top\n3. Update the compare-link footnotes:\n\n```markdown\n[Unreleased]: https://github.com/org/repo/compare/v1.4.0...HEAD\n[1.4.0]: https://github.com/org/repo/compare/v1.3.0...v1.4.0\n```\n\n4. Commit with `chore(release): v1.4.0`\n5. Tag: `git tag v1.4.0`\n\n## Entry-writing tips\n\n- **Subject of the sentence is what changed**, not \"we\": \"Added X\" not \"We added X\"\n- **One PR = one bullet** in most cases; only split if the PR ships two distinct changes\n- **Link the PR or issue** in parentheses at the end\n- **Don't include refactors that aren't user-visible** — they belong in commit messages, not CHANGELOG\n\nReference: [Keep a Changelog](https://keepachangelog.com/) · [Conventional Commits → CHANGELOG generation](https://github.com/conventional-changelog/conventional-changelog) (if you prefer auto-generated)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4082,"content_sha256":"981dc73f048b7f8e74d4f4c05c1ffb75f743a7ea2109d4ff71a99e9d98cc9773"},{"filename":"rules/lifecycle-freshness.md","content":"---\ntitle: Freshness Dates on Architecture Docs\nimpact: MEDIUM\nimpactDescription: \"Without a freshness signal, every reader has to guess if the doc is still accurate\"\ntags: lifecycle, freshness, verification\n---\n\n## Freshness Dates on Architecture Docs\n\n**Impact: MEDIUM (Without a freshness signal, every reader has to guess if the doc is still accurate)**\n\nAn architecture doc from 2022 might still be accurate or might be wildly out of date — without an explicit \"Last verified\" line, the reader can't tell. Adding a freshness date converts a doc from \"trust at your own risk\" to \"verified accurate as of YYYY-MM-DD\".\n\n## What needs a freshness date\n\n| Doc type | Freshness needed? |\n|---|---|\n| `docs/architecture/*` | **Yes** — these describe how the system *currently* works |\n| `docs/guides/*` | **Yes** — setup steps change |\n| `docs/runbooks/*` | **Yes** — procedures must be verified |\n| `docs/api/*` | If hand-written; not if auto-generated from code |\n| `docs/adr/*` | **No** — ADRs are dated by design (Date: field); they describe a past decision |\n| `docs/archive/*` | **No** — archived = frozen in time |\n| `README.md` | The \"Installation\" section — yes |\n\n## Correct\n\n```markdown\n# Architecture Overview\n\n_Last verified on a fresh checkout: 2026-04-12 by @asyraf_\n\n## System layout\n\nThe application is a Laravel monolith with an Inertia.js + React frontend …\n```\n\nOr as a table at the top:\n\n```markdown\n| Field | Value |\n|---|---|\n| Last verified | 2026-04-12 |\n| Verified by | @asyraf |\n| Owner | @org/platform |\n```\n\n## Incorrect\n\n```markdown\n❌ No freshness signal anywhere\n# Architecture Overview\n\nThe application uses MySQL for orders and Redis for sessions.\n…\n```\n\n(Was that true in 2022? Still true today? You'd have to ask.)\n\n## When to refresh\n\nSet a cadence per doc-type:\n\n- **Architecture docs** — quarterly (or when a major change ships)\n- **Runbooks** — verify by *executing* them quarterly (a \"walk-through\" exercise)\n- **Setup / install guides** — verify by running them on a clean checkout quarterly\n- **API references** — when they're hand-written, every time a public endpoint changes\n\nThe freshness date is part of the doc's content; updating it is part of the verification work, not separate paperwork.\n\n## Detection\n\n```bash\n# Find architecture / guide / runbook docs without a \"Last verified\" line\nfor f in docs/architecture/*.md docs/guides/*.md docs/runbooks/*.md; do\n grep -qE '_?Last verified' \"$f\" || echo \"NO FRESHNESS DATE: $f\"\ndone\n\n# Find docs whose \"Last verified\" date is > 12 months ago.\n# Compute threshold in bash (BSD awk on macOS doesn't have strftime/systime).\nTHRESHOLD=$(date -v-12m +%F 2>/dev/null || date -d '12 months ago' +%F)\ngrep -rEoH 'Last verified[: ]+[0-9]{4}-[0-9]{2}-[0-9]{2}' docs/ \\\n | awk -F: -v t=\"$THRESHOLD\" '$NF \u003c t { print \"STALE: \" $0 }'\n```\n\n## CI: prompt-rather-than-block\n\nDon't block PRs on freshness dates — that creates noise. Instead, prompt with a weekly digest:\n\n```yaml\n# .github/workflows/docs-freshness.yml — runs weekly\n- name: List stale docs\n run: |\n THRESHOLD=$(date -d '12 months ago' +%F)\n grep -rEoH 'Last verified[: ]+[0-9]{4}-[0-9]{2}-[0-9]{2}' docs/ \\\n | awk -F'verified[: ]+' -v t=\"$THRESHOLD\" '$2 \u003c t { print }' \\\n | tee stale-docs.txt\n # Open a GitHub issue listing stale docs (or post to Slack)\n```\n\nReference: [Diátaxis — keeping documentation maintained](https://diataxis.fr/) · Internal: `docs-outdated-architecture` rule in the [technical-debt](../../technical-debt) skill covers detection of stale content\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3582,"content_sha256":"ba4b82f165d533245786aa2cbbe575dfe93fadc2c0aecd7cf268f91cbf57fde9"},{"filename":"rules/naming-adr-files.md","content":"---\ntitle: ADR File Naming — Numbered Prefix\nimpact: HIGH\nimpactDescription: \"ADRs are append-only history; numbering makes order, references, and freshness obvious\"\ntags: naming, adr, architecture-decision-record\n---\n\n## ADR File Naming — Numbered Prefix\n\n**Impact: HIGH (ADRs are append-only history; numbering makes order, references, and freshness obvious)**\n\nArchitecture Decision Records (ADRs) are an append-only log of \"why we chose X\". A numbered prefix (`0001-...`, `0002-...`) makes chronology explicit, lets you say \"see ADR-0007\" in a PR, and groups all ADRs together when sorted alphabetically. Four-digit padding handles up to 9,999 ADRs without re-sorting.\n\n## Incorrect\n\n```\n❌ Inconsistent or missing numbering\ndocs/adr/\n├── record-architecture-decisions.md # no number\n├── 2-choose-mysql.md # single digit, sorts after 19\n├── ADR-3-inertia-for-spa.md # extra prefix\n├── 0004-use-redis-cache.md\n├── adopt-tailwind.md # no number\n└── 10-restructure-services.md # missing zero-padding\n```\n\n**Problems:**\n- Listing `docs/adr/` sorts in unpredictable order (`10-...` comes before `2-...`)\n- \"See ADR 3\" — but `ADR-3-inertia-for-spa.md` has the wrong prefix shape\n- Some have numbers, some don't; you can't tell which were written first\n\n## Correct\n\n```\n✅ Four-digit zero-padded prefix, kebab-case body\ndocs/adr/\n├── 0001-record-architecture-decisions.md # the meta-ADR — \"we will record decisions\"\n├── 0002-choose-mysql-over-postgres.md\n├── 0003-adopt-inertia-for-spa.md\n├── 0004-use-redis-for-session-store.md\n├── 0005-monorepo-package-layout.md\n├── ...\n└── 0042-rate-limit-public-api.md\n```\n\n**Benefits:**\n- Lexicographic sort = chronological sort, always\n- Compact references in PRs and code comments: \"ADR-0007\", \"see #0007\"\n- Four digits handle a decade of decisions without renumbering\n- New ADRs always append at the end — clear that history is immutable\n\n## Naming components\n\n```\n0007-rate-limit-public-api.md\n└┬─┘ └───────┬──────────────┘\n │ │\n │ └── Short description of the decision (kebab-case, 3–7 words)\n └── 4-digit zero-padded sequential number\n```\n\n- **Number** — sequentially assigned, never reused, never re-ordered\n- **Description** — present-tense verb where applicable; matches the ADR title\n\n## Template\n\nUse [Michael Nygard's template](https://github.com/joelparkerhenderson/architecture-decision-record) (or `adr-tools` CLI). Each ADR has:\n\n```markdown\n# 0007. Rate-limit the public API\n\nDate: 2026-05-16\nStatus: Accepted\n\n## Context\nWhat forces are at play?\n\n## Decision\nWhat did we decide?\n\n## Consequences\nWhat becomes easier? Harder?\n```\n\n## Tooling\n\n```bash\n# adr-tools CLI (https://github.com/npryce/adr-tools)\nbrew install adr-tools\nadr init docs/adr\nadr new \"Rate-limit the public API\" # auto-creates 0007-rate-limit-the-public-api.md\n```\n\n## Status field — proposed → accepted → superseded\n\nADR status is part of the *content*, not the filename. Don't rename `0007-...` to `0007-SUPERSEDED-...`. Update the `Status:` line and link to the superseding ADR:\n\n```markdown\nStatus: Superseded by ADR-0019\n```\n\nReference: [adr.github.io](https://adr.github.io/) · [Michael Nygard's original post](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) · [adr-tools](https://github.com/npryce/adr-tools)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3542,"content_sha256":"9be7a24ffb2852dcad995544189f182766c69d9aade94f0f2528101d40bd24c3"},{"filename":"rules/naming-anti-patterns.md","content":"---\ntitle: Naming Anti-Patterns to Reject\nimpact: HIGH\nimpactDescription: \"Junk names accumulate fast — once you accept one, the floodgates open\"\ntags: naming, anti-patterns, cleanup\n---\n\n## Naming Anti-Patterns to Reject\n\n**Impact: HIGH (Junk names accumulate fast — once you accept one, the floodgates open)**\n\nA docs folder degrades one bad name at a time. `MyNotes.md` makes `JohnsThoughts.md` feel acceptable, and within a year you can't tell what's real documentation and what's someone's scratchpad. Reject these patterns at PR review time.\n\n## The anti-patterns\n\n### 1. Dates in filenames\n\n```\n❌ deployment-2025-09-14.md\n❌ notes-2024-q3.md\n❌ 2026-03-meeting.md\n```\n\n**Why bad:** dates make readers wonder which version is current. Use the `Last modified` git timestamp + a `Last verified:` line inside the doc instead.\n\n**Allowed exception:** archive folders may include the year: `docs/archive/2024/launch-plan.md`.\n\n### 2. First-person / owner names\n\n```\n❌ MyNotes.md\n❌ JohnsArchitectureThoughts.md\n❌ Asyraf-deployment-draft.md\n```\n\n**Why bad:** docs belong to the project, not a person. If only one person can maintain it, it's not documentation — it's a private note. Use the issue tracker or a personal scratchpad.\n\n### 3. Draft / temp / version markers\n\n```\n❌ deployment-DRAFT.md\n❌ architecture-FINAL.md\n❌ architecture-FINAL-v2.md (the FINAL-v2 paradox)\n❌ deployment-OLD.md\n❌ tmp-notes.md\n❌ test-doc.md\n```\n\n**Why bad:** git history is the source of truth for \"draft vs final\" — that's what branches and PRs are for. \"FINAL-v2\" almost always means \"we never deleted the old one\".\n\n### 4. Mixed-purpose / vague names\n\n```\n❌ misc.md\n❌ stuff.md\n❌ notes.md (at root)\n❌ documentation.md (the whole project's docs in one file)\n❌ general.md\n```\n\n**Why bad:** if you can't name the doc precisely, it doesn't have a clear purpose. Either split it into focused docs or delete it.\n\n### 5. AI-plan / status / summary files\n\n```\n❌ PLAN.md\n❌ IMPLEMENTATION-PLAN.md\n❌ REFACTOR-PLAN.md\n❌ IMPLEMENTATION-SUMMARY.md\n❌ COMPLETED.md\n❌ NEXT-STEPS.md\n❌ TODO.md\n```\n\n**Why bad:** these are agent-generated transient state, not documentation. The work either landed (history is in git/PRs) or it didn't (tracking belongs in the issue tracker). See `cleanup-ai-junk`.\n\n### 6. Numbered without ADR semantics\n\n```\n❌ doc-1.md, doc-2.md, doc-3.md (numbers without meaning)\n❌ chapter-1.md, chapter-2.md (this is a book, not a docs folder)\n```\n\n**Why bad:** numbering implies order, but these have no append-only / decision-record semantics. Either use ADRs (`docs/adr/0001-...`) or use descriptive names.\n\n## Correct — what to use instead\n\n| Bad | Good |\n|---|---|\n| `MyArchitectureNotes.md` | `docs/architecture/overview.md` |\n| `deployment-2025-09.md` | `docs/guides/deployment.md` (with internal `Last verified:` line) |\n| `deployment-DRAFT.md` | Open a PR; keep the draft on a branch |\n| `notes.md` | Either a focused doc OR delete and use the issue tracker |\n| `PLAN.md` | A linked GitHub issue or project board |\n| `architecture-FINAL-v2.md` | `docs/architecture/overview.md` + git history |\n\n## Detection\n\n```bash\n# Names containing dates, draft markers, first-person, or AI-junk patterns\nfind docs/ -name '*.md' | grep -Ein \\\n '/(DRAFT|FINAL|OLD|TMP|TEMP|TODO|PLAN|SUMMARY|NEXT-STEPS|My[A-Z][a-z]|[12][0-9]{3}-[0-9]{2}-[0-9]{2}|v[0-9]+\\.md)'\n```\n\nReference: [Diátaxis](https://diataxis.fr/) · [Documentation System — naming conventions](https://docs.divio.com/documentation-system/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3570,"content_sha256":"7faf9096c96e33647e3985cb7c18a5b1ec8a39909caf1f3452186f8b09890b6d"},{"filename":"rules/naming-docs-files.md","content":"---\ntitle: kebab-case for Files Under docs/\nimpact: CRITICAL\nimpactDescription: \"URLs, search, and grep all behave better with lowercase-hyphenated names\"\ntags: naming, docs-folder, kebab-case\n---\n\n## kebab-case for Files Under docs/\n\n**Impact: CRITICAL (URLs, search, and grep all behave better with lowercase-hyphenated names)**\n\nFiles under `docs/` get linked, served as URLs by static-site generators, and grepped daily. `kebab-case` (lowercase, hyphen-separated) avoids case-sensitivity bugs, generates clean URLs (`docs/deployment-guide.md` → `/deployment-guide`), and is the dominant convention across documentation sites.\n\n## Incorrect\n\n```\n❌ Mixed cases, spaces, underscores, PascalCase\ndocs/\n├── DeploymentGuide.md # PascalCase\n├── deployment_guide.md # snake_case\n├── Deployment Guide.md # spaces — break URLs\n├── Architecture Overview.md # spaces + Title Case\n├── api-Reference.md # mixed case\n├── data_model.MD # uppercase extension\n└── Onboarding.md # initial capital\n```\n\n**Problems:**\n- Mixed conventions force readers to guess each filename\n- Spaces in filenames produce `%20`-encoded URLs that are hard to type and look broken\n- Case mismatches between branches cause \"file not found\" on Linux CI while working on macOS/Windows\n- Static-site generators usually lowercase URLs anyway, so `DeploymentGuide.md` and `deployment-guide.md` collide\n\n## Correct\n\n```\n✅ Consistent kebab-case throughout docs/\ndocs/\n├── architecture/\n│ ├── overview.md\n│ ├── data-model.md\n│ └── service-boundaries.md\n├── guides/\n│ ├── deployment-guide.md\n│ ├── local-development.md\n│ ├── getting-started.md\n│ └── api-reference.md\n└── runbooks/\n ├── incident-response.md\n └── deploy-production.md\n```\n\n**Benefits:**\n- Predictable: anyone can guess the filename from the topic\n- URL-friendly: `/docs/guides/deployment-guide` reads naturally\n- Case-safe: lowercase eliminates case-sensitivity differences across OSes\n- Greppable: `grep -rn 'deployment-guide' docs/` always finds the file\n\n## Conventions\n\n- **All lowercase** — `deployment-guide.md`, not `Deployment-Guide.md`\n- **Hyphens, not underscores** — `data-model.md`, not `data_model.md`\n- **No spaces** — ever\n- **`.md` extension lowercase** — `.md`, not `.MD`\n- **Keep names short** — 2–4 words ideal; if you need 6, the doc may be doing too much\n- **Use nouns or noun-phrases** — `deployment-guide.md`, not `how-to-deploy.md` (the folder structure already conveys the verb)\n\n## Detection\n\n```bash\n# Find files in docs/ with bad casing or characters\nfind docs/ -name '*.md' | grep -E '[A-Z]|[[:space:]]|_'\n```\n\nAny hit is a candidate for renaming via `git mv`. Add a markdownlint rule or a CI grep to prevent regression.\n\nReference: [Diátaxis — naming](https://diataxis.fr/) · [Markdownlint](https://github.com/DavidAnson/markdownlint)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3025,"content_sha256":"607167734a3a65ea6a3822df8f3dfa634622ff59291252207cb6f1a84c40dad9"},{"filename":"rules/naming-root-files.md","content":"---\ntitle: UPPERCASE Naming for Root-Level Conventional Files\nimpact: CRITICAL\nimpactDescription: \"Hosting platforms, tools, and readers expect specific filenames at root — case matters\"\ntags: naming, root, conventions\n---\n\n## UPPERCASE Naming for Root-Level Conventional Files\n\n**Impact: CRITICAL (Hosting platforms, tools, and readers expect specific filenames at root — case matters)**\n\nGitHub, GitLab, Bitbucket, npm, Packagist, and most static-site generators look for **specific filenames in specific cases** at the repo root. `Readme.md` is not the same as `README.md` to a case-sensitive filesystem, and on case-sensitive CI runners (Linux containers) a mismatch breaks tools that auto-render the file.\n\n## Incorrect\n\n```\n❌ Inconsistent / wrong casing\n.\n├── Readme.md # GitHub renders, but tools that grep \"README\" miss it\n├── Changelog.md # Keep-a-Changelog tooling expects CHANGELOG.md\n├── license.md # Should be LICENSE (no extension by convention)\n├── Contributing.md # GitHub's \"How to contribute\" UI looks for CONTRIBUTING.md\n└── security.md # GitHub security advisories look for SECURITY.md\n```\n\n**Problems:**\n- GitHub case-sensitively matches `SECURITY.md` for the Security Advisories tab; `security.md` won't link\n- Many CI tools (e.g., `markdownlint` rule MD041) and license detectors look up the exact uppercase name\n- Linux filesystems treat `README.md` and `Readme.md` as distinct files — switching between editor casings creates phantom duplicates in git\n\n## Correct\n\n```\n✅ Conventional UPPERCASE names at root\n.\n├── README.md # uppercase, .md extension\n├── CHANGELOG.md\n├── LICENSE # no .md extension (long-standing convention)\n├── CONTRIBUTING.md\n├── SECURITY.md\n├── CODE_OF_CONDUCT.md # underscores, not hyphens\n└── AUTHORS.md # or MAINTAINERS.md\n```\n\n**Benefits:**\n- GitHub auto-detects each file for its respective tab/feature (Security, Contributing, Code of Conduct)\n- License-detection tools (GitHub Linguist, choosealicense.com) recognize `LICENSE` without extension\n- Case-sensitive CI environments behave the same as your dev machine\n\n## The `LICENSE` extension convention\n\n`LICENSE` (no extension) is the long-standing convention from open-source culture. GitHub auto-detects the license type from the file content regardless of extension, but `LICENSE` without extension is the dominant pattern. `LICENSE.md` and `LICENSE.txt` also work; pick one and be consistent across your repos.\n\n## Detection\n\n```bash\n# Find mis-cased conventional root files\nls *.md LICENSE* 2>/dev/null | \\\n awk 'BEGIN{IGNORECASE=1}\n /^(readme|changelog|contributing|security|code[-_]of[-_]conduct|authors|maintainers)\\.md$/ \\\n && $0 !~ /^(README|CHANGELOG|CONTRIBUTING|SECURITY|CODE_OF_CONDUCT|AUTHORS|MAINTAINERS)\\.md$/ \\\n { print \"MIS-CASED: \" $0 }'\n\n# Or simply:\nls Readme.md Changelog.md Contributing.md Security.md 2>/dev/null\n```\n\nReference: [GitHub — Adding a security policy](https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository) · [Keep a Changelog — naming](https://keepachangelog.com/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3303,"content_sha256":"ea8ccf6d6209cf9f7642349a8f1b372549bb144570b1b915cf7a75f61c5ddb9e"},{"filename":"rules/quality-ai-slop.md","content":"---\ntitle: AI Slop in Documentation Content\nimpact: HIGH\nimpactDescription: \"AI-generated content has telltale fingerprints that erode docs quality file-by-file\"\ntags: quality, ai-generated, slop, cleanup\n---\n\n## AI Slop in Documentation Content\n\n**Impact: HIGH (AI-generated content has telltale fingerprints that erode docs quality file-by-file)**\n\n[`cleanup-ai-junk`](cleanup-ai-junk.md) covers whole files agents leave behind. This rule covers **content inside otherwise-legitimate docs** — a README, an architecture overview, a runbook — that has been bulked up with AI-style filler. The patterns are recognizable; once you see them, you can't unsee them.\n\n## Slop fingerprints in content\n\n### 1. Filler phrases — delete on sight\n\n```\n\"It's worth noting that...\"\n\"It's important to understand...\"\n\"In summary...\" / \"To summarize...\"\n\"Let's dive into...\" / \"Let's take a look at...\"\n\"As we'll see...\" / \"As mentioned earlier...\"\n\"That being said...\" / \"Having said that...\"\n\"At its core...\" / \"Essentially...\"\n\"In essence...\" / \"Fundamentally...\"\n\"It goes without saying...\"\n\"Needless to say...\" (if it's needless to say, don't say it)\n```\n\nThese phrases add words without adding meaning. A human technical writer doesn't sprinkle \"in essence\" through a deployment guide.\n\n### 2. Bullet-list explosions\n\n```markdown\n❌ ## Why we chose Redis\n\n Redis was chosen for the following reasons:\n\n - **Performance** — Redis is very fast\n - **Reliability** — Redis is highly reliable\n - **Scalability** — Redis can scale to large workloads\n - **Community** — Redis has a large and active community\n - **Documentation** — Redis is well-documented\n - **Ecosystem** — Redis has a rich ecosystem of tools\n - **Maturity** — Redis is a mature and proven technology\n - **Versatility** — Redis supports many use cases\n```\n\nEight bullets, zero specifics. Replace with one paragraph that says *what we actually needed* (sub-millisecond GET latency + pub-sub for session invalidation) and *why Redis fit* (battle-tested at our scale; team has prod experience). The version with bullets *looks* informative; the prose version *is* informative.\n\n### 3. Closing sign-offs\n\n```markdown\n❌ ## Conclusion\n\n I hope this guide has been helpful in setting up your development\n environment. If you have any questions, feel free to reach out to\n the team. Happy coding! 🎉\n```\n\nDocumentation isn't a blog post. Cut the goodbye.\n\n### 4. Past-tense narration where present tense fits\n\n```markdown\n❌ When we started this project, we considered using Laravel because\n it offered a good balance of features. We decided that Laravel was\n the right choice. We chose MySQL as our database because of its\n maturity.\n```\n\n```markdown\n✅ The application uses Laravel for its mature ecosystem and MySQL\n for its operational track record. See [ADR-0002](adr/0002-...) for\n the full decision context.\n```\n\nThe \"we did X\" narrative belongs in commit messages or ADRs, not in the current architecture doc.\n\n### 5. Generic praise / hedging\n\n```\n\"leveraging industry best practices\"\n\"following modern development standards\"\n\"a robust and scalable solution\"\n\"a powerful and flexible framework\"\n\"state-of-the-art technology\"\n```\n\nThese phrases are zero-content. If \"industry best practices\" matters, name the specific practice (e.g., \"OWASP ASVS Level 2\", \"12-factor app\", \"Conventional Commits\"). If \"scalable\" matters, name the actual numbers (e.g., \"10k req/s with p95 \u003c 200ms\").\n\n### 6. Markdown formatting overuse\n\n- **Random words bolded for no reason**\n- Excessive heading nesting (`####` after only one `##`)\n- Bullet lists where prose flows better\n- Tables for two items\n\n### 7. The 100-line answer to a 3-line question\n\nA \"How do I run tests?\" section that takes 100 lines to say `php artisan test`.\n\n## Detection\n\n```bash\n# Filler-phrase fingerprints\ngrep -rEi --include='*.md' \\\n \"it's worth noting|let's (dive|take a look|start)|it goes without saying|needless to say|that being said|having said that|at its core|in essence|fundamentally\" \\\n docs/ README.md\n\n# Closing sign-offs in docs\ngrep -rEi --include='*.md' \\\n \"happy coding|hope this (was )?help|if you have any questions|feel free to reach out\" \\\n docs/ README.md\n\n# Generic praise (likely AI-slop)\ngrep -rEi --include='*.md' \\\n \"industry best practices|state-of-the-art|robust and scalable|powerful and flexible\" \\\n docs/ README.md\n\n# Many emojis = often AI-generated (humans rarely emoji-pepper technical docs).\n# `grep -roE` prints one match per emoji occurrence (not per line), so the\n# per-file count below is the true emoji count.\nfind docs/ README.md -name '*.md' -type f 2>/dev/null | while read f; do\n N=$(grep -oE '🤖|✨|🚀|🎉|💡|⚡|📝|🔥|🎯|💪' \"$f\" | wc -l | tr -d ' ')\n if [ \"$N\" -gt 5 ]; then\n echo \"MANY EMOJIS ($N): $f\"\n fi\ndone\n```\n\n## How to triage a slop-flagged doc\n\n1. **Read the whole doc end to end** — is the content valid under the slop?\n2. **If valid** — rewrite the prose: cut filler, name specifics, prefer present tense\n3. **If hollow** — the slop was hiding that the doc has no real content. Either rewrite from scratch with real content, or delete.\n\n**Never** rewrite docs *with another LLM* without close review — you'll just produce different slop. Use the model to draft, then read every line as a human and cut ruthlessly.\n\nReference: Internal: [`cleanup-ai-junk`](cleanup-ai-junk.md) (junk files) · [`quality-conciseness`](quality-conciseness.md) (general bloat) · [On Bullshit (Frankfurt)](https://en.wikipedia.org/wiki/On_Bullshit) — yes, really\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5624,"content_sha256":"405ea1af32106b6aa3eb3c27acdea8e43b1f9de6eb759c3f9f149fb83e8530c4"},{"filename":"rules/quality-code-blocks.md","content":"---\ntitle: Code Blocks — Language Tags and Copy-Pasteability\nimpact: MEDIUM\nimpactDescription: \"Untagged code blocks lose syntax highlighting; broken commands waste reader time\"\ntags: quality, code-blocks, language-tags\n---\n\n## Code Blocks — Language Tags and Copy-Pasteability\n\n**Impact: MEDIUM (Untagged code blocks lose syntax highlighting; broken commands waste reader time)**\n\nA code block without a language tag renders as monospace text with no syntax highlighting. A \"command\" that contains an unexplained placeholder (`\u003cyour-token-here>`) or a typo wastes every future reader's time. Code blocks in docs are contracts: if you paste them, they should work.\n\n## Rules\n\n### 1. Every code block has a language tag\n\n````markdown\n❌ ```\n php artisan migrate\n ```\n\n✅ ```bash\n php artisan migrate\n ```\n````\n\nCommon tags:\n\n| Tag | Use for |\n|---|---|\n| `bash` / `sh` | Shell commands, terminals |\n| `php` | PHP code |\n| `js` / `ts` / `tsx` | JavaScript / TypeScript / TSX |\n| `json` | JSON config |\n| `yaml` / `yml` | YAML (CI configs, etc.) |\n| `sql` | SQL queries |\n| `markdown` / `md` | Nested markdown examples (use 4-backtick outer fence) |\n| `diff` | Patches / before-after |\n| `text` or no tag | Genuinely plain text only |\n\n### 2. Commands you intend to be copy-pasted must actually run\n\n```bash\n❌ git clone \u003cyour-repo-url> # placeholder; reader has to figure out what\n❌ npm install your-package # 'your-package' isn't a real package\n\n✅ git clone [email protected]:your-org/your-repo.git\n✅ npm install # no args — installs from package.json\n```\n\nIf a placeholder is unavoidable (real credentials, secret URL), surround it with a comment that makes the substitution obvious:\n\n```bash\n✅ # Replace YOUR_API_TOKEN with the token from Settings → API\n curl -H \"Authorization: Bearer YOUR_API_TOKEN\" https://api.example.com/...\n```\n\n### 3. Multi-line commands use proper line continuation\n\n```bash\n❌ git commit -m \"feat: add user export\"\n --no-verify\n\n✅ git commit -m \"feat: add user export\" \\\n --no-verify\n```\n\nWithout the backslash, the second line is a separate command (and will fail).\n\n### 4. Don't include ` project-docs — Skillopedia or `>` prompts in copy-pasteable blocks\n\n```bash\n❌ $ npm install\n $ npm run dev\n\n✅ npm install\n npm run dev\n```\n\nThe ` project-docs — Skillopedia is fine if you're showing input/output together (where output lines have no ` project-docs — Skillopedia ), but for copy-paste-friendly blocks, omit the prompt.\n\n### 5. Show output separately from input\n\n````markdown\n❌ ```bash\n $ php artisan about\n Laravel ............. 11.0\n PHP ................. 8.3\n ```\n\n✅ ```bash\n php artisan about\n ```\n\n Output:\n\n ```\n Laravel ............. 11.0\n PHP ................. 8.3\n ```\n````\n\nThis way the reader can copy the command without dragging output along.\n\n### 6. Test the commands\n\nBefore publishing a guide, run every command in it from scratch (clean shell, clean checkout). The number of bugs you find on the first run is sobering.\n\n## Detection\n\n```bash\n# Code blocks with no language tag\nfor f in $(find docs/ README.md -name '*.md' 2>/dev/null); do\n awk -v file=\"$f\" '\n /^```$/ && !in_code { print file \":\" NR \": untagged code block\"; in_code=1; next }\n /^```/ && !in_code { in_code=1; next }\n /^```$/ && in_code { in_code=0 }\n ' \"$f\"\ndone\n\n# Markdownlint rule MD040 (fenced-code-language) catches this automatically\nnpx markdownlint-cli2 --config '.markdownlint.json' '**/*.md'\n```\n\nAdd to `.markdownlint.json`:\n\n```json\n{\n \"MD040\": true // fenced-code-language — language tags required\n}\n```\n\nReference: [Markdownlint MD040](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md040) · [GitHub — Syntax highlighting](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3878,"content_sha256":"59498b4785957d1d520e6356d64de41ddbdf8eae2e7d1a5242c30b10b0f17703"},{"filename":"rules/quality-conciseness.md","content":"---\ntitle: Conciseness — Cut Bloat\nimpact: HIGH\nimpactDescription: \"Long docs aren't more informative — they're more skipped. The real risk is signal-to-noise.\"\ntags: quality, conciseness, brevity, readability\n---\n\n## Conciseness — Cut Bloat\n\n**Impact: HIGH (Long docs aren't more informative — they're more skipped. The real risk is signal-to-noise.)**\n\nA README that's 400 lines doesn't tell the reader more — it tells them less, because they stop reading after the first screen. Good docs say what's needed and stop. Length is a cost, not a virtue.\n\n## Bloat patterns to cut\n\n### 1. Opening boilerplate\n\n```markdown\n❌ # Deployment Guide\n\n This document describes the deployment process for our application.\n It is intended to be read by engineers who are responsible for\n deploying the application to production. Before reading this document,\n you should be familiar with the basics of the application architecture.\n\n Without further ado, let's dive into the deployment process.\n\n ## Overview\n ...\n```\n\n```markdown\n✅ # Deployment Guide\n\n ## Overview\n ...\n```\n\nThe reader knows it's a deployment guide — the H1 says so. They know it's for engineers — they're reading the engineering docs. Five paragraphs of \"this document describes\" is five paragraphs of throat-clearing.\n\n### 2. Restating what code or commands already say\n\n````markdown\n❌ ## Installation\n\n To install the application, you first need to clone the repository\n from GitHub. After cloning the repository, navigate into the\n newly-created directory. Then, install the PHP dependencies by\n running composer install. Once that completes, install the\n JavaScript dependencies by running npm install. Finally, copy the\n example environment file to a new .env file.\n\n ```bash\n git clone …\n cd …\n composer install\n npm install\n cp .env.example .env\n ```\n````\n\n````markdown\n✅ ## Installation\n\n ```bash\n git clone …\n cd …\n composer install\n npm install\n cp .env.example .env\n ```\n````\n\nThe commands are self-explanatory. Don't narrate them.\n\n### 3. Repetition across sections\n\nA README that says the same thing in the \"Overview\", then again in \"Description\", then again in \"About this project\". Pick one.\n\n### 4. \"As mentioned above\" / \"as we'll see later\"\n\nIf something needs cross-referencing, link to it. Inline meta-commentary about the document's structure is noise.\n\n### 5. Over-explaining basics\n\n```markdown\n❌ Run `composer install`. Composer is a dependency manager for PHP\n that allows you to declare the libraries your project depends on\n and manages (install/update) them for you. To learn more about\n Composer, visit https://getcomposer.org.\n```\n\nIf the reader is on the install page of your project, they know what Composer is (or they have one click to learn). Don't teach the basics inline.\n\n## Rough length targets\n\nThese are heuristics — focused docs can be longer if they have to be, and short docs can be too long if they're padded.\n\n| Doc | Target |\n|---|---|\n| README | \u003c 200 lines (link to deeper docs for detail) |\n| Single guide / runbook | \u003c 300 lines |\n| Single ADR | \u003c 150 lines (decision + context + consequences) |\n| Architecture overview | \u003c 500 lines (split into multiple docs if longer) |\n\nIf you hit these and the content is genuinely necessary, split into multiple focused docs rather than letting one file balloon.\n\n## The \"delete a paragraph\" exercise\n\nBefore publishing a doc, try this: pick any paragraph and ask, \"if I deleted this, would a reader miss anything important?\" If the answer is no — or \"they'd just need to read the next paragraph more carefully\" — delete it.\n\nMost docs improve after a 20–30% cut.\n\n## Detection\n\n```bash\n# Long markdown files — review for bloat\nfind docs/ -name '*.md' -exec wc -l {} \\; | sort -rn | head -20\n\n# Docs with low signal: many lines containing \"this document\", \"you should\", \"let's\"\n# Note: grep -c counts matching LINES (one filler line = 1, even with multiple phrases).\ngrep -rEcH \"(this document|you should|let'?s (dive|look|start))\" --include='*.md' docs/ \\\n | awk -F: '{ if ($2 > 5) print \"BLOATY: \" $1 \" (\" $2 \" matching lines)\" }'\n```\n\nReference: [Strunk & White — Omit Needless Words](https://en.wikipedia.org/wiki/The_Elements_of_Style) · [Diátaxis — \"less is more\"](https://diataxis.fr/) · Internal: [`quality-ai-slop`](quality-ai-slop.md) (AI-style filler patterns)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4432,"content_sha256":"a0ec3536f7788535c8beaeda3a81bf92424f33f76c70700f9d302b1822b9f346"},{"filename":"rules/quality-headings.md","content":"---\ntitle: Heading Hierarchy — H1 Once, No Skipped Levels\nimpact: HIGH\nimpactDescription: \"Broken heading hierarchy breaks readability, accessibility, and auto-generated TOCs\"\ntags: quality, headings, structure, accessibility\n---\n\n## Heading Hierarchy — H1 Once, No Skipped Levels\n\n**Impact: HIGH (Broken heading hierarchy breaks readability, accessibility, and auto-generated TOCs)**\n\nEach markdown file has exactly one H1 (the document title), and lower levels go in sequence (H2 → H3 → H4) without skipping. Auto-generated TOCs, screen readers, and GitHub's outline view all rely on this. A file with three H1s or with H1 → H3 jumps reads as broken to humans and as malformed to tools.\n\n## Rules\n\n1. **Exactly one H1 per file** — it's the title\n2. **No skipping levels** — H2 can be followed by H2 or H3, but not H4\n3. **Don't use bold instead of a heading** — `**Important:**` doesn't show up in TOCs\n4. **Don't use H1 inside a doc** — once you've used `#`, use only `##` and below\n5. **Headings should be descriptive, not generic** — `## Configuration` not `## Section 2`\n\n## Incorrect\n\n### Multiple H1s\n\n```markdown\n# Deployment Guide\n\n## Overview\n\nSome overview content.\n\n# Configuration\n\nSome configuration content. ← second H1; should be ##\n\n# Troubleshooting\n\n… ← third H1\n```\n\nThe doc has three \"top-level\" sections in markdown's eyes; GitHub will treat each H1 as a candidate document title. The TOC will be flat and confused.\n\n### Skipped levels\n\n```markdown\n# Architecture Overview\n\n## Components ← H2\n\n#### Authentication ← H4 (skipped H3)\n\nSome content about auth.\n\n#### Authorization ← H4 still\n\n#### Sessions ← H4 still\n```\n\nThe reader expects \"Components\" to have direct sub-sections; instead it has sub-sub-sections. Auto-generated outlines look broken.\n\n### Bold-as-heading\n\n````markdown\n## Setup\n\n**Prerequisites:** ← bold, not heading\n\n- Node 22\n- MySQL 8\n\n**Install:** ← bold, not heading\n\n```bash\nnpm install\n```\n````\n\n\"Prerequisites\" and \"Install\" don't appear in the TOC; readers scanning headings miss them.\n\n## Correct\n\n```markdown\n# Deployment Guide ← exactly one H1, matching the file's purpose\n\n## Overview\n\nBrief overview content.\n\n## Configuration\n\n### Environment variables ← H3 under H2\n\n### Secrets storage\n\n## Troubleshooting\n\n### Build failures ← H3 under H2\n\n### Deploy timeouts\n```\n\nSequential, predictable, scannable.\n\n## Heading content\n\n### Use sentence case\n\n```\n✓ ## Setting up a development environment\n✗ ## Setting Up A Development Environment (title case is awkward to read)\n✗ ## SETTING UP A DEVELOPMENT ENVIRONMENT (shouting)\n```\n\n### Be specific\n\n```\n✓ ## Resetting a user's password from the admin panel\n✗ ## Password reset (which kind? from where?)\n```\n\n### Match how readers search\n\nThe TOC of a 1000-line doc is its index. Headings should answer \"what would I search for?\", not be cute.\n\n## Detection\n\n```bash\n# Files with more than one H1 (set -e safe — uses if/then instead of && chain)\nfind docs/ -name '*.md' -print0 | while IFS= read -r -d '' f; do\n H1_COUNT=$(grep -cE '^# [^#]' \"$f\" || true)\n if [ \"$H1_COUNT\" -gt 1 ]; then\n echo \"MULTIPLE H1s ($H1_COUNT): $f\"\n fi\ndone\n\n# Skipped heading levels (markdownlint rule MD001)\nnpx markdownlint-cli2 'docs/**/*.md' 'README.md'\n# (Configure .markdownlint.json: { \"MD001\": true })\n```\n\n`markdownlint`'s rule MD001 (`heading-increment`) catches skipped levels automatically; MD025 (`single-h1`) catches multiple H1s. Enable both.\n\nReference: [Markdownlint rules MD001 / MD025](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md) · [WAI-ARIA Heading Levels](https://www.w3.org/WAI/tutorials/page-structure/headings/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3927,"content_sha256":"9fecfd147d0f8522674b970fd96e9ecd272e9bb00d0d6e7d46e3e148520d0294"},{"filename":"rules/quality-links.md","content":"---\ntitle: Links — Descriptive Text and No Broken URLs\nimpact: MEDIUM\nimpactDescription: \"'Click here' is unscannable and inaccessible; broken links erode trust in the whole doc set\"\ntags: quality, links, accessibility, broken-links\n---\n\n## Links — Descriptive Text and No Broken URLs\n\n**Impact: MEDIUM (\"Click here\" is unscannable and inaccessible; broken links erode trust in the whole doc set)**\n\nTwo failure modes degrade docs quickly: link text that doesn't describe where the link goes (\"click here\", \"this link\") and links that 404 because the target moved. Both are easy to detect and fix; left unchecked, both compound until readers stop trusting the docs.\n\n## Rule 1 — Descriptive link text\n\n```markdown\n❌ For deployment instructions, click [here](docs/guides/deployment.md).\n❌ See [this page](docs/guides/deployment.md) for deployment.\n❌ Read more about deployment [here](docs/guides/deployment.md).\n\n✅ See the [deployment guide](docs/guides/deployment.md).\n✅ Deployment instructions are in [docs/guides/deployment.md](docs/guides/deployment.md).\n✅ For details, see the [deployment guide](docs/guides/deployment.md).\n```\n\nWhy descriptive text matters:\n\n1. **Scannability** — a reader skimming the doc sees \"deployment guide\", not \"click here\"\n2. **Accessibility** — screen readers can announce just the link text in isolation; \"click here\" out of context is meaningless\n3. **Search** — \"deployment guide\" is a useful keyword; \"click here\" is not\n\n### Quick test\n\nRead just the link text out loud (ignore the surrounding sentence). Does it tell you where you'd land? If yes, it's good. If \"click here\" / \"this link\" / \"more\", rewrite.\n\n## Rule 2 — Use relative paths for in-repo links\n\n```markdown\n❌ See https://github.com/your-org/your-repo/blob/main/docs/architecture/overview.md\n✅ See [docs/architecture/overview.md](docs/architecture/overview.md)\n```\n\nRelative paths:\n- Work on any fork or mirror\n- Don't break when the repo is renamed or moved\n- Resolve correctly when docs are served via MkDocs / GitHub Pages\n\nUse absolute URLs only for **off-repo** targets (external sites, other repos).\n\n## Rule 3 — Check links don't 404\n\nBroken links happen when files are renamed, moved, or deleted without updating their referrers. The fix is automation: a link checker in CI.\n\n```yaml\n# .github/workflows/check-links.yml\nname: Check links\non:\n pull_request:\n paths: ['**/*.md']\n schedule: [{ cron: '0 9 * * MON' }] # weekly catches link-rot\n\njobs:\n lychee:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: lycheeverse/lychee-action@v2\n with:\n args: --no-progress --exclude-mail './**/*.md' README.md\n fail: true\n```\n\n[Lychee](https://github.com/lycheeverse/lychee) is the recommended tool: fast (Rust), supports config files, exits non-zero on broken links so CI fails the PR.\n\nFor per-file checks during local development:\n\n```bash\nnpx markdown-link-check docs/guides/deployment.md\n```\n\n## Rule 4 — Don't link to the current file\n\n```markdown\n❌ See [this section](#installation) for installation. (when \"this section\" IS the installation section)\n```\n\nSelf-references are usually filler. The reader is already there.\n\n## Rule 5 — Use reference-style links for repeated targets\n\nWhen the same URL appears 3+ times in a doc, use reference-style:\n\n```markdown\nSome prose linking to the [Laravel docs][laravel].\n\nSome more prose also linking to the [Laravel docs][laravel] later on.\n\nYet more prose, third time linking to the [Laravel docs][laravel].\n\n[laravel]: https://laravel.com/docs/11.x\n```\n\nThe doc stays readable in source form, and updating the URL in one place updates all occurrences.\n\n## Detection\n\n```bash\n# Non-descriptive link text\ngrep -rEnH '\\[(click here|here|this|this link|read more|more|link)\\]\\(' \\\n --include='*.md' docs/ README.md\n\n# Absolute URLs to your own repo (should be relative)\n# EDIT: replace with your actual GitHub org/repo before running.\nORG_REPO=\"your-org/your-repo\"\ngrep -rEnH \"github\\.com/$ORG_REPO/(blob|tree)/\" --include='*.md' .\n\n# Run link checker locally\nnpx lychee --no-progress './**/*.md' README.md\n```\n\nReference: [Lychee](https://github.com/lycheeverse/lychee) · [markdown-link-check](https://github.com/tcort/markdown-link-check) · [WCAG 2.4.4 — Link Purpose](https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context.html)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4404,"content_sha256":"8ab7b97705e52d8b71470699817929ff263209cd7c6b0dd8fb632fe278c68f7d"},{"filename":"rules/structure-docs-folder.md","content":"---\ntitle: docs/ as the Single Documentation Home\nimpact: CRITICAL\nimpactDescription: \"One predictable home for everything beyond root files — no more 'where did we put that diagram?'\"\ntags: structure, docs-folder, organization\n---\n\n## docs/ as the Single Documentation Home\n\n**Impact: CRITICAL (One predictable home for everything beyond root files — no more \"where did we put that diagram?\")**\n\nEvery project needs one canonical place for documentation that isn't a conventional root file. `docs/` is the standard, recognized by GitHub Pages, MkDocs, Docusaurus, and most static-site generators. Without it, docs scatter across `wiki/`, `docs-internal/`, `notes/`, and inline `.md` files next to source.\n\n## Incorrect\n\n```\n❌ Docs scattered across the tree\n.\n├── README.md\n├── architecture-notes.md # at root\n├── deployment.md # at root\n├── app/Services/PaymentService.md # next to source\n├── notes/\n│ ├── design-meeting-2024.md\n│ └── refactor-ideas.md\n├── wiki/ # parallel docs folder\n│ ├── onboarding.md\n│ └── api.md\n└── internal-docs/\n └── runbook.md\n```\n\n**Problems:**\n- Same kind of content (deployment, runbooks) lives in 3 different folders\n- Static-site generators (MkDocs, Docusaurus, GitHub Pages) expect `docs/` and won't find the others\n- New contributors don't know where to put a new doc — and don't know where to find existing ones\n\n## Correct\n\n```\n✅ Everything non-root lives under docs/\n.\n├── README.md\n├── CHANGELOG.md\n├── LICENSE\n└── docs/\n ├── architecture/\n │ └── overview.md\n ├── adr/\n │ └── 0001-record-architecture-decisions.md\n ├── guides/\n │ ├── getting-started.md\n │ └── deployment.md\n ├── runbooks/\n │ └── incident-response.md\n └── archive/\n └── 2024/\n └── superseded-design.md\n```\n\n**Benefits:**\n- One predictable location — readers know where to look, contributors know where to add\n- Works out of the box with GitHub Pages (`/docs` source), MkDocs, Docusaurus\n- Source code stays uncluttered; no `.md` files next to `.php`/`.tsx` files\n\n## Exceptions\n\nA few `.md` files genuinely belong outside `docs/`:\n\n- **Root-level conventional files** — README, CHANGELOG, LICENSE, etc. (see `structure-root-files`)\n- **GitHub-specific files** — `.github/CODEOWNERS`, `.github/PULL_REQUEST_TEMPLATE.md`, `.github/ISSUE_TEMPLATE/*.md`\n- **Top-level subproject READMEs in monorepos** — `packages/auth/README.md` is fine; the package itself is a unit\n\nEverything else: in `docs/`.\n\n## Detection\n\n```bash\n# Find .md files that aren't in docs/ or one of the allowed locations\nfind . -name '*.md' -type f \\\n -not -path './docs/*' \\\n -not -path './.github/*' \\\n -not -path './node_modules/*' \\\n -not -path './vendor/*' \\\n -not -name 'README.md' \\\n -not -name 'CHANGELOG.md' \\\n -not -name 'CONTRIBUTING.md' \\\n -not -name 'SECURITY.md' \\\n -not -name 'CODE_OF_CONDUCT.md' \\\n -not -name 'AUTHORS.md' \\\n -not -name 'MAINTAINERS.md'\n```\n\nAny hit is a candidate for relocation into `docs/` (or deletion if it's junk).\n\nReference: [Diátaxis — documentation framework](https://diataxis.fr/) · [MkDocs](https://www.mkdocs.org/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3372,"content_sha256":"1745736af61960a954b972f2c0350f5a868c98c2ef481f1dc55fbe5d86cb81f5"},{"filename":"rules/structure-root-files.md","content":"---\ntitle: Root-Level Documentation Files\nimpact: CRITICAL\nimpactDescription: \"Discoverability — first impression for every new reader, every tool, every hosting platform\"\ntags: structure, root, readme, changelog\n---\n\n## Root-Level Documentation Files\n\n**Impact: CRITICAL (Discoverability — first impression for every new reader, every tool, every hosting platform)**\n\nA small, curated set of files lives at the repo root because GitHub, npm, Packagist, and every developer tool look for them there. Anything else belongs in `docs/`. Keeping the root clean turns the first directory listing into a clear signpost rather than a clutter dump.\n\n## Incorrect\n\n```\n❌ Cluttered root — every doc lives at the top level\n.\n├── README.md\n├── CHANGELOG.md\n├── LICENSE\n├── architecture.md\n├── deployment-notes.md\n├── PLAN.md\n├── refactor-thoughts.md\n├── api-reference.md\n├── adr-001.md\n├── adr-002.md\n├── deployment-runbook.md\n├── onboarding.md\n└── ...\n```\n\n**Problems:**\n- A new contributor scrolling the root sees 20+ files and can't tell what's essential\n- Architecture and runbooks bury the README and CHANGELOG\n- Each new doc adds noise at the most visible location in the repo\n\n## Correct\n\n```\n✅ Curated root — only conventional files; everything else in docs/\n.\n├── README.md # required — project purpose, install, usage\n├── CHANGELOG.md # required from first release\n├── LICENSE # required\n├── CONTRIBUTING.md # if accepting external contributors\n├── SECURITY.md # if internet-facing service\n├── CODE_OF_CONDUCT.md # if open-source community\n└── docs/ # everything else\n ├── architecture/\n ├── adr/\n ├── guides/\n └── runbooks/\n```\n\n**Benefits:**\n- The root listing reads as a project index, not a junk drawer\n- Tools (GitHub, npm, Packagist, Dependabot) find the files they expect\n- New contributors see the curated set first; details live one click deeper in `docs/`\n\n## Allowed root-level docs\n\n| File | When required | What it contains |\n|---|---|---|\n| `README.md` | Always | Purpose, install, usage, license link |\n| `CHANGELOG.md` | After first release | One section per version (Keep-a-Changelog format) |\n| `LICENSE` | Always | The license text — plain, no `.md` extension by convention |\n| `CONTRIBUTING.md` | If external contributors | Branching, commit style, PR checklist |\n| `SECURITY.md` | If internet-facing | Vulnerability reporting policy and contact |\n| `CODE_OF_CONDUCT.md` | Open-source projects | Contributor Covenant or equivalent |\n| `AUTHORS.md` / `MAINTAINERS.md` | Multi-author projects | Maintainer list |\n\nAnything else — architecture, ADRs, guides, runbooks, API references — belongs in `docs/`.\n\n## Detection\n\n```bash\n# Files at root that aren't on the allow-list\nls *.md *.MD LICENSE 2>/dev/null | \\\n grep -vE '^(README|CHANGELOG|CONTRIBUTING|SECURITY|CODE_OF_CONDUCT|AUTHORS|MAINTAINERS|LICENSE)(\\.md)?

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…

\n```\n\nReference: [GitHub — About READMEs](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes) · [Keep a Changelog](https://keepachangelog.com/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3340,"content_sha256":"f24933b9ccdd0e94f55877b1818f83e8ca2fa86f4e4c0f75cea9987697e55e78"},{"filename":"rules/structure-subfolders.md","content":"---\ntitle: docs/ Sub-folder Layout\nimpact: HIGH\nimpactDescription: \"Different doc kinds have different audiences and lifecycles — separate them\"\ntags: structure, docs-folder, organization, diataxis\n---\n\n## docs/ Sub-folder Layout\n\n**Impact: HIGH (Different doc kinds have different audiences and lifecycles — separate them)**\n\nA flat `docs/` works for 5 files and falls apart at 20. Sub-folders by *purpose* (architecture, ADRs, guides, runbooks, archive) make docs scannable and let you apply different freshness/ownership rules per folder.\n\n## Incorrect\n\n```\n❌ Flat docs/ — everything mixed together\ndocs/\n├── overview.md\n├── deployment.md\n├── adr-001.md\n├── adr-002.md\n├── api.md\n├── incident-response.md\n├── data-model.md\n├── q3-launch-plan.md\n├── superseded-design.md\n└── onboarding.md\n```\n\n**Problems:**\n- Architecture, ADRs, guides, and runbooks all live in one bucket — no separation of concerns\n- Superseded docs (`superseded-design.md`) sit next to current docs — confusing\n- A reader looking for \"the runbook\" has to scan everything\n- Can't apply different rules (e.g., \"runbooks need an owner; archive doesn't\")\n\n## Correct\n\n```\n✅ Purpose-based sub-folders\ndocs/\n├── architecture/ # how the system is built (long-lived, slow-changing)\n│ ├── overview.md\n│ └── data-model.md\n├── adr/ # decisions made (append-only, numbered)\n│ ├── 0001-record-architecture-decisions.md\n│ └── 0002-choose-mysql-over-postgres.md\n├── guides/ # how-to for developers (medium-lived, task-oriented)\n│ ├── getting-started.md\n│ ├── deployment.md\n│ └── local-development.md\n├── runbooks/ # ops procedures (short-titled, action-focused)\n│ ├── deploy-production.md\n│ └── incident-response.md\n├── api/ # API references (often generated; OpenAPI/Swagger)\n│ └── openapi.yaml\n└── archive/ # superseded but kept for history\n ├── 2024/\n └── 2025/\n```\n\n**Benefits:**\n- Each folder has a clear purpose and audience\n- Archive is visually separated from current docs\n- Easy to apply per-folder rules (CODEOWNERS, freshness checks)\n- Maps naturally to [Diátaxis](https://diataxis.fr/) categories (tutorials/how-to/reference/explanation)\n\n## Diátaxis correspondence\n\n| Sub-folder | Diátaxis | Audience |\n|---|---|---|\n| `guides/getting-started.md` | Tutorial | First-time users |\n| `guides/deployment.md` | How-to | Engineers performing a task |\n| `architecture/` | Explanation | Engineers building understanding |\n| `api/` | Reference | Engineers looking up specifics |\n| `adr/` | Decision record | Engineers asking \"why?\" |\n| `runbooks/` | How-to (ops) | On-call engineers |\n\n## Add folders as needed\n\n- **`docs/security/`** — threat models, security architecture, audit reports\n- **`docs/onboarding/`** — new-hire orientation, codebase tour\n- **`docs/proposals/`** — RFCs / design proposals (before they become ADRs)\n- **`docs/meeting-notes/`** — only if you'll actually maintain them; otherwise use the issue tracker\n\nDon't pre-create empty folders. Add them when you have at least two docs that belong inside.\n\nReference: [Diátaxis](https://diataxis.fr/) · [adr.github.io](https://adr.github.io/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3394,"content_sha256":"739ab360c6817ce45dea05fea4f8904b15f7dd91e35c7b809d7a3f9053363864"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Project Documentation","type":"text"}]},{"type":"paragraph","content":[{"text":"End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports ","type":"text"},{"text":"bootstrap mode","type":"text","marks":[{"type":"strong"}]},{"text":" (set up docs in a new project), ","type":"text"},{"text":"audit mode","type":"text","marks":[{"type":"strong"}]},{"text":" (find what's missing, stale, bloated, or junk), and ","type":"text"},{"text":"reference mode","type":"text","marks":[{"type":"strong"}]},{"text":" (conventions lookup).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Metadata","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Version:","type":"text","marks":[{"type":"strong"}]},{"text":" 1.0.0","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scope:","type":"text","marks":[{"type":"strong"}]},{"text":" PHP / Laravel + Node / TypeScript / React projects","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rule Count:","type":"text","marks":[{"type":"strong"}]},{"text":" 25 rules across 6 categories","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"License:","type":"text","marks":[{"type":"strong"}]},{"text":" MIT","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"How to Use This Skill — Three Modes","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Mode 1: Bootstrap (new project / missing docs)","type":"text"}]},{"type":"paragraph","content":[{"text":"When the user asks \"set up docs\", \"what docs does this project need\", or starts a new project — walk through the bootstrap steps:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detect project type","type":"text","marks":[{"type":"strong"}]},{"text":" (Laravel? React? Both?) — ","type":"text"},{"text":"composer.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"package.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"artisan","type":"text","marks":[{"type":"code_inline"}]},{"text":" binary","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Inventory existing docs","type":"text","marks":[{"type":"strong"}]},{"text":" — list every ","type":"text"},{"text":".md","type":"text","marks":[{"type":"code_inline"}]},{"text":" file with its location and last-modified date","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Identify gaps","type":"text","marks":[{"type":"strong"}]},{"text":" — compare against the Essential Files checklist; report what's missing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Propose folder structure","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"docs/","type":"text","marks":[{"type":"code_inline"}]},{"text":" with sub-folders (","type":"text"},{"text":"architecture/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"adr/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"guides/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"runbooks/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"archive/","type":"text","marks":[{"type":"code_inline"}]},{"text":") based on project size","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Offer to scaffold templates","type":"text","marks":[{"type":"strong"}]},{"text":" — README, CHANGELOG, LICENSE, CONTRIBUTING, SECURITY, ADR-0001 — generate with user approval, do not auto-create","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Suggest CI gates","type":"text","marks":[{"type":"strong"}]},{"text":" — markdown-lint, broken-link checker (lychee), CHANGELOG-on-PR enforcement","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Mode 2: Audit (existing project cleanup)","type":"text"}]},{"type":"paragraph","content":[{"text":"When the user asks \"audit docs\", \"clean up markdown\", or \"what should I delete\" — produce a classified ledger.","type":"text"}]},{"type":"paragraph","content":[{"text":"For each ","type":"text"},{"text":".md","type":"text","marks":[{"type":"code_inline"}]},{"text":" file in the repo:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"KEEP","type":"text","marks":[{"type":"strong"}]},{"text":" — file is essential and current","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"UPDATE","type":"text","marks":[{"type":"strong"}]},{"text":" — file is essential but stale (e.g., README contradicts current setup)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ARCHIVE","type":"text","marks":[{"type":"strong"}]},{"text":" — superseded but historically useful — move to ","type":"text"},{"text":"docs/archive/\u003cyear>/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DELETE","type":"text","marks":[{"type":"strong"}]},{"text":" — AI-generated plan files, empty stubs, duplicates, orphaned drafts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"MOVE","type":"text","marks":[{"type":"strong"}]},{"text":" — wrong location or wrong name (e.g., ","type":"text"},{"text":"MyArchitectureNotes.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" at root → ","type":"text"},{"text":"docs/architecture/overview.md","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Output format:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"## Documentation Audit Ledger\n\n| File | Last modified | Verdict | Reason | Action |\n|------|---------------|---------|--------|--------|\n| PLAN.md | 2026-02-14 | DELETE | AI-generated plan, no longer referenced | rm PLAN.md |\n| README.md | 2024-08-01 | UPDATE | Setup steps reference removed Vite v3 | Update install section |\n| docs/old-architecture.md | 2024-11 | ARCHIVE | Superseded by docs/architecture/overview.md | mv to docs/archive/2024/ |\n| MyNotes.md | 2025-09 | DELETE | Personal notes; not project docs | rm MyNotes.md |\n\n## Summary\n- KEEP: X files\n- UPDATE: Y files (top priority: ...)\n- ARCHIVE: Z files\n- DELETE: N files\n- MOVE: M files","type":"text"}]},{"type":"paragraph","content":[{"text":"Never auto-delete.","type":"text","marks":[{"type":"strong"}]},{"text":" Always surface for user approval first.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Mode 3: Reference (conventions lookup)","type":"text"}]},{"type":"paragraph","content":[{"text":"When the user asks \"how should I name this\", \"where should this go\", or references the skill in a code-review context — look up the relevant rule(s) in ","type":"text"},{"text":"rules/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Apply","type":"text"}]},{"type":"paragraph","content":[{"text":"Reference this skill when:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Starting a new Laravel or Node/React project and need a docs baseline","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Onboarding a project with messy or AI-cluttered markdown files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setting up ","type":"text"},{"text":"docs/","type":"text","marks":[{"type":"code_inline"}]},{"text":" folder structure","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Naming a new doc file","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Deciding whether to delete a ","type":"text"},{"text":"PLAN.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"TODO.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"IMPLEMENTATION-SUMMARY.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Adding CI checks for markdown quality","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reviewing a PR that adds or modifies documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Quarterly \"docs hygiene\" sweep","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 1: Detect Project Type","type":"text"}]},{"type":"paragraph","content":[{"text":"Always check the project stack before recommending specifics.","type":"text","marks":[{"type":"strong"}]},{"text":" Bootstrap and naming guidance differ slightly per stack.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Signal","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Project Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"composer.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"artisan","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Laravel (PHP)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"README should cover ","type":"text"},{"text":"composer install","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"php artisan migrate","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".env.example","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"package.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (only)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Node / TypeScript / React","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"README should cover ","type":"text"},{"text":"npm install","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".nvmrc","type":"text","marks":[{"type":"code_inline"}]},{"text":", build scripts","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Both present","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Laravel + Inertia + React","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"README covers both PHP and Node setup paths","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"The rules themselves are mostly stack-agnostic — README format, ADR structure, naming conventions apply to any project.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Rule Categories by Priority","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Priority","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Category","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Impact","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Prefix","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Structure","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CRITICAL","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"structure-","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Naming","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CRITICAL","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"naming-","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Essential Files","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HIGH","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"essential-","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Quality","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HIGH","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"quality-","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"5","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cleanup","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HIGH","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cleanup-","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"6","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Lifecycle","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MEDIUM","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"lifecycle-","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Structure (CRITICAL)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"structure-root-files","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Which files belong at the repo root (README, CHANGELOG, LICENSE, etc.)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"structure-docs-folder","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"docs/","type":"text","marks":[{"type":"code_inline"}]},{"text":" as the home for everything beyond root files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"structure-subfolders","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Recommended ","type":"text"},{"text":"docs/","type":"text","marks":[{"type":"code_inline"}]},{"text":" layout: architecture/, adr/, guides/, runbooks/, archive/","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Naming (CRITICAL)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"naming-root-files","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"UPPERCASE.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for conventional root files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"naming-docs-files","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"kebab-case.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for files under ","type":"text"},{"text":"docs/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"naming-adr-files","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Numbered prefix: ","type":"text"},{"text":"0001-record-architecture-decisions.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"naming-anti-patterns","type":"text","marks":[{"type":"code_inline"}]},{"text":" — No dates, no ","type":"text"},{"text":"MyNotes.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", no ","type":"text"},{"text":"tmp/draft/final","type":"text","marks":[{"type":"code_inline"}]},{"text":" markers","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Essential Files (HIGH)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"essential-readme","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Every project needs a README with purpose, install, usage, license","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"essential-changelog","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Keep-a-Changelog format; one entry per release","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"essential-license","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"LICENSE","type":"text","marks":[{"type":"code_inline"}]},{"text":" file (or ","type":"text"},{"text":"LICENSE.md","type":"text","marks":[{"type":"code_inline"}]},{"text":") at repo root","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"essential-contributing","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"CONTRIBUTING.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" when accepting external contributors","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"essential-security","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"SECURITY.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" with vulnerability reporting policy","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. Quality (HIGH)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"quality-conciseness","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Cut bloat; length is a cost, not a virtue","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"quality-ai-slop","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Detect AI-generated content patterns (filler, sign-offs, generic praise)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"quality-headings","type":"text","marks":[{"type":"code_inline"}]},{"text":" — One H1, no skipped levels, descriptive heading text","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"quality-code-blocks","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Language tags, copy-pasteable commands, no untagged blocks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"quality-links","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Descriptive link text (not \"click here\"), relative paths, no broken links","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5. Cleanup (HIGH)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"cleanup-ai-junk","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Detect and remove AI-generated plan/summary files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"cleanup-duplicates","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Same content in multiple files; consolidate or delete copies","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"cleanup-orphans","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":".md","type":"text","marks":[{"type":"code_inline"}]},{"text":" files not linked from anywhere; archive or delete","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"cleanup-empty-stubs","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Files with TBD / TODO / placeholder content only","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6. Lifecycle (MEDIUM)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"lifecycle-freshness","type":"text","marks":[{"type":"code_inline"}]},{"text":" — \"Last verified\" dates on architecture docs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"lifecycle-archive","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Superseded docs go to ","type":"text"},{"text":"docs/archive/\u003cyear>/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"lifecycle-adr-process","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ADR creation triggers and lifecycle (proposed → accepted → superseded)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"lifecycle-changelog-discipline","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Add a CHANGELOG entry in the same PR as the change","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Essential Patterns","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Standard folder layout","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":".\n├── README.md # required\n├── CHANGELOG.md # required from first release\n├── LICENSE # required\n├── CONTRIBUTING.md # if external contributors\n├── SECURITY.md # if internet-facing\n├── CODE_OF_CONDUCT.md # if open source community\n├── .github/\n│ └── CODEOWNERS # team ownership\n└── docs/\n ├── architecture/\n │ ├── overview.md\n │ └── data-model.md\n ├── adr/\n │ ├── 0001-record-architecture-decisions.md\n │ ├── 0002-choose-mysql-over-postgres.md\n │ └── 0003-adopt-inertia-for-spa.md\n ├── guides/\n │ ├── getting-started.md\n │ ├── deployment.md\n │ └── local-development.md\n ├── runbooks/\n │ ├── deploy-production.md\n │ └── incident-response.md\n └── archive/\n └── 2024/\n └── old-architecture-notes.md","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"File naming at a glance","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"✓ README.md, CHANGELOG.md, LICENSE, CONTRIBUTING.md, SECURITY.md\n✓ docs/architecture/overview.md\n✓ docs/adr/0007-cache-strategy.md\n✓ docs/guides/deployment.md\n✓ docs/archive/2024/q3-launch-plan.md\n\n✗ Readme.md, Changelog.md (use UPPERCASE for conventional root files)\n✗ docs/Architecture/Overview.md (use kebab-case in docs/)\n✗ docs/Notes-2025-09-14.md (no dates in filenames)\n✗ MyArchitectureThoughts.md (no first-person, no PascalCase)\n✗ PLAN.md, TODO.md, TEMP.md (use issue tracker for transient state)\n✗ FINAL-deployment-guide-v2.md (no draft/final/v2 markers)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"CI: keep markdown honest","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/docs.yml\n- name: Lint markdown\n uses: DavidAnson/markdownlint-cli2-action@v23\n\n- name: Check links\n uses: lycheeverse/lychee-action@v2\n with:\n args: --no-progress --exclude-mail './**/*.md'","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"How to Use","type":"text"}]},{"type":"paragraph","content":[{"text":"Read individual rule files for detailed conventions and examples:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"rules/structure-root-files.md\nrules/naming-adr-files.md\nrules/essential-readme.md\nrules/cleanup-ai-junk.md\nrules/lifecycle-archive.md","type":"text"}]},{"type":"paragraph","content":[{"text":"Each rule file contains:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"YAML frontmatter with metadata (title, impact, tags)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Brief explanation of why it matters","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Incorrect example (anti-pattern)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Correct example (the convention)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detection / enforcement guidance where applicable","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep a Changelog","type":"text","marks":[{"type":"link","attrs":{"href":"https://keepachangelog.com/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Semantic Versioning","type":"text","marks":[{"type":"link","attrs":{"href":"https://semver.org/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Architecture Decision Records (ADR)","type":"text","marks":[{"type":"link","attrs":{"href":"https://adr.github.io/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Diátaxis — documentation framework","type":"text","marks":[{"type":"link","attrs":{"href":"https://diataxis.fr/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Choose a License","type":"text","marks":[{"type":"link","attrs":{"href":"https://choosealicense.com/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Markdownlint","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/DavidAnson/markdownlint","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Lychee — link checker","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/lycheeverse/lychee","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Full Compiled Document","type":"text"}]},{"type":"paragraph","content":[{"text":"For the complete guide with all rules expanded: ","type":"text"},{"text":"AGENTS.md","type":"text","marks":[{"type":"code_inline"}]}]}]},"metadata":{"date":"2026-11-02","name":"project-docs","author":"@skillopedia","source":{"stars":98,"repo_name":"agent-skills","origin_url":"https://github.com/AsyrafHussin/agent-skills/blob/HEAD/skills/project-docs/SKILL.md","repo_owner":"AsyrafHussin","body_sha256":"1c405f53a6231bb4f202e1ae3862397b4eda6987132824d0e660ab2dba405a34","cluster_key":"a1b610d1371d27ace11b9d64ec5adf9bab8867cd49a47751c80ab6456a516dda","clean_bundle":{"format":"clean-skill-bundle-v1","source":"AsyrafHussin/agent-skills/skills/project-docs/SKILL.md","text_bytes":200612,"source_commit":"1aa0ff717c10309226c9e678f00873976450fd76","skills_sh_rank":16326,"attachment_count":30,"text_attachments":30,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/project-docs/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"documents-office","category_label":"Documents"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"documents-office","metadata":{"author":"agent-skills","version":"1.0.0"},"import_tag":"skills-sh-top20k-2026-11","description":"Project documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects — bootstrapping essential docs, naming and folder conventions, freshness, and cleanup of AI-generated junk and stale files. Use when starting a new project, setting up docs/ structure, auditing markdown files, cleaning up the docs folder, or deciding which docs to keep, archive, or delete. Triggers on \"set up docs\", \"audit docs\", \"clean up markdown\", \"what docs does this project need\", \"organize docs folder\", \"find stale docs\"."}},"renderedAt":1793686176935}

Project Documentation End-to-end documentation lifecycle for PHP/Laravel and Node/TypeScript/React projects. Contains 25 rules across 6 categories covering folder structure, naming conventions, essential files, content quality (including AI-slop detection), cleanup of accumulated junk, and lifecycle. Supports bootstrap mode (set up docs in a new project), audit mode (find what's missing, stale, bloated, or junk), and reference mode (conventions lookup). Metadata - Version: 1.0.0 - Scope: PHP / Laravel + Node / TypeScript / React projects - Rule Count: 25 rules across 6 categories - License: M…