Documentation Completeness Audit Determine whether a documentation set covers everything it should by building an inventory of what needs documenting and comparing it to what exists . The output is a prioritized gap report — not new documentation. When to Use - After shipping a feature — verify docs cover the new surface area - Before a release — ensure no undocumented public APIs, CLI flags, or config options - When users or new hires report "I couldn't find docs for X" - Periodic health check on doc coverage - After running (structural) and (accuracy) to go wider Quick Reference | Resource…

'', before, re.MULTILINE)\n if m:\n return m.group(1).strip()\n return None\n\n\n# -- Config keys ------------------------------------------------------------\n\nCONFIG_PATTERNS = [\n # Python dict .get() with string key\n (re.compile(r'''\\.get\\(\\s*[\"']([a-z][\\w._-]*)[\"']'''), \"dict_get\"),\n # Python dict[\"key\"] access\n (re.compile(r'''\\[[\"']([a-z][\\w._-]*)[\"']\\]'''), \"dict_access\"),\n # TOML/INI section headers\n (re.compile(r'''^\\[([a-z][\\w._-]*)\\]\\s*

Documentation Completeness Audit Determine whether a documentation set covers everything it should by building an inventory of what needs documenting and comparing it to what exists . The output is a prioritized gap report — not new documentation. When to Use - After shipping a feature — verify docs cover the new surface area - Before a release — ensure no undocumented public APIs, CLI flags, or config options - When users or new hires report "I couldn't find docs for X" - Periodic health check on doc coverage - After running (structural) and (accuracy) to go wider Quick Reference | Resource…

'', re.MULTILINE), \"section\"),\n # YAML top-level keys (simplified)\n (re.compile(r'''^([a-z][\\w_-]*):\\s''', re.MULTILINE), \"yaml_key\"),\n # JS/TS config access: config.key or config[\"key\"]\n (re.compile(r'''config\\.([a-z][\\w_]*)'''), \"js_config\"),\n]\n\n# Strings too generic to be config keys\nIGNORE_CONFIG_KEYS: Set[str] = {\n \"name\", \"type\", \"value\", \"key\", \"data\", \"id\", \"get\", \"set\",\n \"items\", \"keys\", \"values\", \"pop\", \"update\", \"default\", \"format\",\n \"path\", \"file\", \"line\", \"text\", \"string\", \"result\", \"output\",\n \"input\", \"error\", \"message\", \"status\", \"code\", \"index\", \"count\",\n \"size\", \"length\", \"start\", \"end\", \"true\", \"false\", \"none\", \"null\",\n}\n\n# Only scan files that look like config loaders\nCONFIG_FILE_PATTERNS = [\n re.compile(r\"config\", re.IGNORECASE),\n re.compile(r\"settings\", re.IGNORECASE),\n re.compile(r\"options\", re.IGNORECASE),\n re.compile(r\"preferences\", re.IGNORECASE),\n]\n\n\ndef detect_config_keys(files: List[Path], root: Path, inv: Inventory) -> None:\n \"\"\"Detect configuration key access patterns in config-related files.\"\"\"\n seen: Set[str] = set()\n\n # Only scan files whose name suggests config handling\n config_files = [\n f for f in files\n if any(p.search(f.name) for p in CONFIG_FILE_PATTERNS)\n ]\n\n for fpath in config_files:\n lines = read_file_lines(fpath)\n relpath = str(fpath.relative_to(root))\n\n for lineno, line in enumerate(lines, 1):\n for pattern, kind in CONFIG_PATTERNS:\n for match in pattern.finditer(line):\n key = match.group(1)\n if key in IGNORE_CONFIG_KEYS or len(key) \u003c 3:\n continue\n if key not in seen:\n seen.add(key)\n inv.add(InventoryItem(\n topic=key,\n category=\"config_key\",\n source_file=relpath,\n source_line=lineno,\n detail=f\"pattern: {kind}\",\n audience=\"operator\",\n ))\n\n\n# -- HTTP endpoints ---------------------------------------------------------\n\nENDPOINT_PATTERNS = [\n # Python Flask/FastAPI decorators\n (re.compile(r'''@\\w+\\.(get|post|put|patch|delete|route)\\(\\s*[\"']([^\"']+)[\"']''', re.IGNORECASE), \"decorator\"),\n # Express.js\n (re.compile(r'''(?:app|router)\\.(get|post|put|patch|delete)\\(\\s*[\"']([^\"']+)[\"']'''), \"express\"),\n # Rust actix/axum\n (re.compile(r'''#\\[(get|post|put|patch|delete)\\(\\s*\"([^\"]+)\"'''), \"rust_macro\"),\n (re.compile(r'''\\.route\\(\\s*[\"']([^\"']+)[\"']\\s*,\\s*(?:get|post|put|patch|delete)'''), \"axum\"),\n # Go net/http\n (re.compile(r'''(?:Handle|HandleFunc)\\(\\s*[\"']([^\"']+)[\"']'''), \"go_http\"),\n]\n\n\ndef detect_http_endpoints(files: List[Path], root: Path, inv: Inventory) -> None:\n \"\"\"Detect HTTP route/endpoint definitions.\"\"\"\n seen: Set[str] = set()\n\n for fpath in files:\n lines = read_file_lines(fpath)\n relpath = str(fpath.relative_to(root))\n\n for lineno, line in enumerate(lines, 1):\n for pattern, kind in ENDPOINT_PATTERNS:\n for match in pattern.finditer(line):\n groups = match.groups()\n if kind == \"decorator\" or kind == \"express\" or kind == \"rust_macro\":\n method, path = groups[0].upper(), groups[1]\n elif kind == \"go_http\":\n method, path = \"ANY\", groups[0]\n elif kind == \"axum\":\n path, method = groups[0], \"ANY\"\n else:\n continue\n\n endpoint = f\"{method} {path}\"\n if endpoint not in seen:\n seen.add(endpoint)\n inv.add(InventoryItem(\n topic=endpoint,\n category=\"http_endpoint\",\n source_file=relpath,\n source_line=lineno,\n detail=f\"framework: {kind}\",\n audience=\"developer\",\n ))\n\n\n# -- Public exports ---------------------------------------------------------\n\nEXPORT_PATTERNS = [\n # Python __init__.py imports\n (re.compile(r'''from\\s+\\.\\w+\\s+import\\s+(\\w+)'''), \"python_reexport\"),\n # Python __all__\n (re.compile(r'''__all__\\s*=\\s*\\[([^\\]]+)\\]'''), \"python_all\"),\n # JS/TS named exports\n (re.compile(r'''export\\s+(?:const|let|var|function|class|interface|type|enum)\\s+(\\w+)'''), \"js_export\"),\n # JS/TS re-exports\n (re.compile(r'''export\\s+\\{([^}]+)\\}'''), \"js_reexport\"),\n # Rust pub items\n (re.compile(r'''pub\\s+(?:fn|struct|enum|trait|type|const|static|mod)\\s+(\\w+)'''), \"rust_pub\"),\n # Go exported (capitalized) functions\n (re.compile(r'''func\\s+(?:\\(\\w+\\s+\\*?\\w+\\)\\s+)?([A-Z]\\w+)\\('''), \"go_export\"),\n]\n\n\ndef detect_public_exports(files: List[Path], root: Path, inv: Inventory) -> None:\n \"\"\"Detect public module exports and API surface.\"\"\"\n seen: Set[str] = set()\n\n # For Python, focus on __init__.py and public modules\n # For JS/TS, focus on index files and files with 'export'\n # For Rust, focus on lib.rs and mod.rs\n export_files = [\n f for f in files\n if f.name in (\"__init__.py\", \"index.ts\", \"index.js\", \"lib.rs\", \"mod.rs\")\n or \"export\" in (read_file_lines(f)[:5] and \" \".join(read_file_lines(f)[:5]) or \"\")\n ]\n\n # Also include all Rust/Go files for pub/exported detection\n for f in files:\n if f.suffix in (\".rs\", \".go\") and f not in export_files:\n export_files.append(f)\n\n for fpath in export_files:\n lines = read_file_lines(fpath)\n relpath = str(fpath.relative_to(root))\n content = \"\\n\".join(lines)\n\n for pattern, kind in EXPORT_PATTERNS:\n for match in pattern.finditer(content):\n raw = match.group(1)\n\n # Handle multi-name exports (__all__, export { a, b })\n if kind in (\"python_all\", \"js_reexport\"):\n names = [\n n.strip().strip(\"\\\"'\")\n for n in raw.split(\",\")\n if n.strip().strip(\"\\\"'\")\n ]\n else:\n names = [raw]\n\n pos = match.start()\n lineno = content[:pos].count(\"\\n\") + 1\n\n for name in names:\n if name.startswith(\"_\") or name in seen:\n continue\n seen.add(name)\n inv.add(InventoryItem(\n topic=name,\n category=\"public_export\",\n source_file=relpath,\n source_line=lineno,\n detail=f\"kind: {kind}\",\n audience=\"developer\",\n ))\n\n\n# -- Error types ------------------------------------------------------------\n\nERROR_PATTERNS = [\n # Python exception classes\n (re.compile(r'''class\\s+(\\w*(?:Error|Exception|Failure|Fault)\\w*)\\s*\\('''), \"python\"),\n # JS/TS error classes\n (re.compile(r'''class\\s+(\\w*(?:Error|Exception)\\w*)\\s+extends'''), \"js\"),\n # Rust error enums/structs\n (re.compile(r'''(?:pub\\s+)?(?:enum|struct)\\s+(\\w*(?:Error|Err)\\w*)'''), \"rust\"),\n # Go error types\n (re.compile(r'''type\\s+(\\w*(?:Error|Err)\\w*)\\s+struct'''), \"go\"),\n # HTTP error status codes in route handlers\n (re.compile(r'''(?:status|code)\\s*[=:]\\s*(4\\d{2}|5\\d{2})'''), \"http_status\"),\n]\n\n\ndef detect_error_types(files: List[Path], root: Path, inv: Inventory) -> None:\n \"\"\"Detect custom error/exception type definitions.\"\"\"\n seen: Set[str] = set()\n\n for fpath in files:\n lines = read_file_lines(fpath)\n relpath = str(fpath.relative_to(root))\n\n for lineno, line in enumerate(lines, 1):\n for pattern, kind in ERROR_PATTERNS:\n for match in pattern.finditer(line):\n name = match.group(1)\n if name not in seen:\n seen.add(name)\n category = \"error_type\"\n if kind == \"http_status\":\n category = \"http_error_status\"\n inv.add(InventoryItem(\n topic=name,\n category=category,\n source_file=relpath,\n source_line=lineno,\n detail=f\"language: {kind}\",\n audience=\"developer\",\n ))\n\n\n# ---------------------------------------------------------------------------\n# Detector registry\n# ---------------------------------------------------------------------------\n\nDETECTORS = {\n \"env_vars\": detect_env_vars,\n \"cli_commands\": detect_cli_commands,\n \"config_keys\": detect_config_keys,\n \"http_endpoints\": detect_http_endpoints,\n \"public_exports\": detect_public_exports,\n \"error_types\": detect_error_types,\n}\n\n\n# ---------------------------------------------------------------------------\n# Main\n# ---------------------------------------------------------------------------\n\ndef run_inventory(\n root: Path,\n src_dirs: Optional[List[Path]] = None,\n detectors: Optional[List[str]] = None,\n) -> Inventory:\n \"\"\"Run the inventory extraction pipeline.\n\n Args:\n root: Project root directory (used for relative path display).\n src_dirs: Specific source directories to scan. If None, scans root.\n detectors: Specific detectors to run. If None, runs all.\n \"\"\"\n inv = Inventory(root=str(root))\n\n # Discover source files from specified dirs or root\n files: List[Path] = []\n scan_roots = src_dirs if src_dirs else [root]\n for scan_root in scan_roots:\n if scan_root.is_dir():\n files.extend(discover_files(scan_root))\n files = sorted(set(files))\n inv.files_scanned = len(files)\n\n # Run selected detectors\n detector_names = detectors or list(DETECTORS.keys())\n for name in detector_names:\n if name in DETECTORS:\n DETECTORS[name](files, root, inv)\n inv.detectors_run.append(name)\n\n return inv\n\n\n# ---------------------------------------------------------------------------\n# Coverage checking\n# ---------------------------------------------------------------------------\n\n\ndef discover_doc_files(docs_dir: Path) -> List[Path]:\n \"\"\"Find markdown files in a docs directory.\"\"\"\n files: List[Path] = []\n for dirpath, dirnames, filenames in os.walk(docs_dir):\n dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS]\n for fname in filenames:\n if fname.endswith((\".md\", \".mdx\", \".rst\", \".adoc\")):\n files.append(Path(dirpath) / fname)\n return sorted(files)\n\n\ndef check_coverage(inv: Inventory, docs_dir: Path) -> Dict[str, List[InventoryItem]]:\n \"\"\"Check which inventory items appear in documentation.\n\n Reads all markdown files in docs_dir and searches for each inventory\n item's topic string. Returns a dict with 'documented' and 'missing' lists.\n \"\"\"\n # Build a single searchable blob from all doc content\n doc_files = discover_doc_files(docs_dir)\n doc_content = \"\"\n for fpath in doc_files:\n try:\n doc_content += fpath.read_text(encoding=\"utf-8\", errors=\"replace\") + \"\\n\"\n except OSError:\n continue\n\n doc_content_lower = doc_content.lower()\n\n documented: List[InventoryItem] = []\n missing: List[InventoryItem] = []\n\n for item in inv.items:\n topic = item.topic\n # Search strategies by category\n found = False\n\n if item.category == \"env_var\":\n # Env vars: exact match (case-sensitive, they're uppercase)\n found = topic in doc_content\n elif item.category in (\"cli_command\", \"cli_flag\", \"cli_argument\"):\n # CLI: search for the command/flag name\n found = topic in doc_content or topic.lstrip(\"-\") in doc_content_lower\n elif item.category == \"config_key\":\n # Config keys: exact or dotted notation\n found = topic in doc_content_lower\n elif item.category == \"http_endpoint\":\n # Endpoints: search for the path portion\n parts = topic.split(\" \", 1)\n path = parts[1] if len(parts) > 1 else parts[0]\n found = path in doc_content\n elif item.category in (\"public_export\", \"error_type\"):\n # Symbols: search for the name\n found = topic in doc_content\n else:\n found = topic.lower() in doc_content_lower\n\n if found:\n documented.append(item)\n else:\n missing.append(item)\n\n return {\"documented\": documented, \"missing\": missing}\n\n\ndef main() -> None:\n parser = argparse.ArgumentParser(\n description=\"Extract documentable surface area from a codebase.\",\n )\n parser.add_argument(\n \"--root\",\n default=\".\",\n help=\"Project root directory (default: current directory)\",\n )\n parser.add_argument(\n \"--src\",\n action=\"append\",\n default=None,\n help=\"Source directory to scan (can be repeated; default: scan --root)\",\n )\n parser.add_argument(\n \"--docs\",\n default=None,\n help=\"Documentation directory to check coverage against\",\n )\n parser.add_argument(\n \"--json\",\n action=\"store_true\",\n help=\"Output as JSON (default: human-readable table)\",\n )\n parser.add_argument(\n \"--detectors\",\n default=None,\n help=\"Comma-separated list of detectors to run (default: all)\",\n )\n args = parser.parse_args()\n\n root = Path(args.root).resolve()\n if not root.is_dir():\n print(f\"Error: {root} is not a directory\", file=sys.stderr)\n sys.exit(1)\n\n src_dirs = [Path(s).resolve() for s in args.src] if args.src else None\n detector_list = args.detectors.split(\",\") if args.detectors else None\n inv = run_inventory(root, src_dirs, detector_list)\n\n # If --docs provided, check coverage\n coverage = None\n if args.docs:\n docs_dir = Path(args.docs).resolve()\n if not docs_dir.is_dir():\n print(f\"Error: {docs_dir} is not a directory\", file=sys.stderr)\n sys.exit(1)\n coverage = check_coverage(inv, docs_dir)\n\n if args.json:\n output: Dict[str, object] = {\n \"root\": inv.root,\n \"files_scanned\": inv.files_scanned,\n \"detectors_run\": inv.detectors_run,\n \"summary\": inv.summary(),\n \"items\": [asdict(item) for item in inv.items],\n }\n if coverage is not None:\n output[\"coverage\"] = {\n \"docs_dir\": args.docs,\n \"documented\": len(coverage[\"documented\"]),\n \"missing\": len(coverage[\"missing\"]),\n \"coverage_pct\": round(\n len(coverage[\"documented\"]) / max(len(inv.items), 1) * 100, 1\n ),\n \"missing_items\": [asdict(item) for item in coverage[\"missing\"]],\n }\n json.dump(output, sys.stdout, indent=2)\n print()\n else:\n print(f\"Inventory: {root}\")\n if src_dirs:\n print(f\"Source dirs: {', '.join(str(s) for s in src_dirs)}\")\n print(f\"Files scanned: {inv.files_scanned}\")\n print(f\"Detectors: {', '.join(inv.detectors_run)}\")\n print(f\"Items found: {len(inv.items)}\")\n print()\n\n summary = inv.summary()\n for category, count in sorted(summary.items()):\n print(f\" {category}: {count}\")\n print()\n\n if coverage is not None:\n total = len(inv.items)\n doc_count = len(coverage[\"documented\"])\n miss_count = len(coverage[\"missing\"])\n pct = round(doc_count / max(total, 1) * 100, 1)\n print(f\"Coverage against {args.docs}:\")\n print(f\" Documented: {doc_count}/{total} ({pct}%)\")\n print(f\" Missing: {miss_count}/{total}\")\n print()\n\n if coverage[\"missing\"]:\n # Group missing by category\n by_cat: Dict[str, List[InventoryItem]] = {}\n for item in coverage[\"missing\"]:\n by_cat.setdefault(item.category, []).append(item)\n\n print(\"## Missing from docs\")\n print()\n for category in sorted(by_cat.keys()):\n items = by_cat[category]\n print(f\"### {category} ({len(items)})\")\n print()\n for item in sorted(items, key=lambda i: i.topic):\n loc = f\"{item.source_file}:{item.source_line}\"\n detail = f\" ({item.detail})\" if item.detail else \"\"\n print(f\" {item.topic}{detail}\")\n print(f\" {loc}\")\n print()\n else:\n # No coverage check — just show full inventory\n by_category: Dict[str, List[InventoryItem]] = {}\n for item in inv.items:\n by_category.setdefault(item.category, []).append(item)\n\n for category in sorted(by_category.keys()):\n items = by_category[category]\n print(f\"## {category} ({len(items)})\")\n print()\n for item in sorted(items, key=lambda i: i.topic):\n loc = f\"{item.source_file}:{item.source_line}\"\n detail = f\" ({item.detail})\" if item.detail else \"\"\n print(f\" {item.topic}{detail}\")\n print(f\" {loc}\")\n print()\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":29708,"content_sha256":"1ea8507773df94d0e7946819633b90bf98a2a8d7b8a5d579ce2366f127b23f3f"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Documentation Completeness Audit","type":"text"}]},{"type":"paragraph","content":[{"text":"Determine whether a documentation set covers everything it should by building an inventory of what ","type":"text"},{"text":"needs","type":"text","marks":[{"type":"em"}]},{"text":" documenting and comparing it to what ","type":"text"},{"text":"exists","type":"text","marks":[{"type":"em"}]},{"text":". The output is a prioritized gap report — not new documentation.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After shipping a feature — verify docs cover the new surface area","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Before a release — ensure no undocumented public APIs, CLI flags, or config options","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When users or new hires report \"I couldn't find docs for X\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Periodic health check on doc coverage","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After running ","type":"text"},{"text":"doc-maintenance","type":"text","marks":[{"type":"code_inline"}]},{"text":" (structural) and ","type":"text"},{"text":"doc-claim-validator","type":"text","marks":[{"type":"code_inline"}]},{"text":" (accuracy) to go wider","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","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":"Resource","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Load when","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/coverage-model.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Defines what \"complete\" means per doc type","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Always (Phase 1)","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow Overview","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Phase 1: Inventory → Build the \"should exist\" list from code and config\nPhase 2: Map → Match inventory items to existing documentation\nPhase 3: Classify → Score each gap by audience impact\nPhase 4: Report → Produce the prioritized gap report","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Phase 1: Build the Inventory","type":"text"}]},{"type":"paragraph","content":[{"text":"Construct a list of everything that should be documented. Use four sources, checking all of them:","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Source 1: Public Code Surface","type":"text"}]},{"type":"paragraph","content":[{"text":"Run the bundled inventory script to extract documentable surface area deterministically:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 skills/doc-completeness-audit/scripts/inventory.py --root . --json > inventory.json\n\n# Or human-readable:\npython3 skills/doc-completeness-audit/scripts/inventory.py --root .\n\n# Run specific detectors only:\npython3 skills/doc-completeness-audit/scripts/inventory.py --root . --detectors env_vars,cli_commands","type":"text"}]},{"type":"paragraph","content":[{"text":"The script scans source files across Python, JavaScript/TypeScript, Rust, Go, Ruby, Java, and shell, extracting six categories:","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":"Detector","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"What it extracts","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"env_vars","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Environment variable references (","type":"text"},{"text":"os.environ","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"process.env","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"env::var","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc.)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cli_commands","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI commands and flags (argparse, click, clap, cobra, commander)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"config_keys","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Configuration key access in config-related files","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"http_endpoints","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HTTP route definitions (Flask, FastAPI, Express, Actix, Axum, net/http)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"public_exports","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Public module exports (","type":"text"},{"text":"__init__.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"export","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pub fn","type":"text","marks":[{"type":"code_inline"}]},{"text":", Go capitalized funcs)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"error_types","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Custom error/exception class definitions","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Event types, webhooks, callbacks","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Every event name and payload shape","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Dispatch an Explore agent to scan for these signals. Provide it with the project's primary language and entry points.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Source 2: User-Facing Features","type":"text"}]},{"type":"paragraph","content":[{"text":"Identify features a user interacts with:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"TUI screens, views, keybindings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CLI workflows (multi-step operations)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Integration points (hooks, plugins, extensions)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Authentication/authorization flows","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Error messages that imply user action","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Source 3: Operational Surface","type":"text"}]},{"type":"paragraph","content":[{"text":"Identify what operators and maintainers need:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Installation and setup procedures","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Upgrade and migration paths","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Backup and restore procedures","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Troubleshooting common errors","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Environment requirements and dependencies","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CI/CD integration points","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Source 4: Existing Docs Cross-References","type":"text"}]},{"type":"paragraph","content":[{"text":"Check existing docs for promises of documentation that doesn't exist:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"See [link]\" references to pages that don't exist","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Coming soon\" or \"TODO\" markers","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Table of contents entries without corresponding pages","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Navigation entries without targets","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Source 5: Architectural / Operational / Migration Topic Discovery (sonnet)","type":"text"}]},{"type":"paragraph","content":[{"text":"The first four sources catch ","type":"text"},{"text":"code-detectable","type":"text","marks":[{"type":"em"}]},{"text":" surface (env vars, CLI flags, endpoints, exported APIs, broken cross-references). They miss topics that exist as architectural patterns, user flows, ops procedures, or migration paths but don't surface as a single greppable symbol. Examples:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Architectural patterns the system implements (CQRS, event sourcing, saga) — should be documented but won't show up in inventory.py","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User flows implicit across UI surfaces — \"how to share a project\" may span multiple components and isn't a single CLI command","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Migration paths between versions — typically tribal knowledge until someone needs them","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Operational runbooks (incidents, rollbacks, capacity events)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Recovery procedures and disaster scenarios","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Dispatch one ","type":"text"},{"text":"general-purpose","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"sonnet","type":"text","marks":[{"type":"code_inline"}]},{"text":" agent for topic discovery:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"subagent_type: \"general-purpose\"\nmodel: \"sonnet\"\ndescription: \"Architectural/operational topic discovery\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Prompt: read README, top-level docs, and a sample of code (architecture files, integration boundaries, deployment configs, major feature directories). Identify topics that ","type":"text"},{"text":"should","type":"text","marks":[{"type":"em"}]},{"text":" be documented but aren't captured by the code-surface inventory. For each topic, name:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"topic","type":"text","marks":[{"type":"code_inline"}]},{"text":" — what needs documenting (one phrase)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"evidence","type":"text","marks":[{"type":"code_inline"}]},{"text":" — what in the codebase implies this topic exists (path:line citations)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"audience","type":"text","marks":[{"type":"code_inline"}]},{"text":" — who would read this (operators, contributors, advanced users)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"type","type":"text","marks":[{"type":"code_inline"}]},{"text":" — reference, tutorial, guide, explanation, runbook","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"confidence","type":"text","marks":[{"type":"code_inline"}]},{"text":" — high (clear evidence), medium (inferred), low (speculative)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Append the agent's output to the inventory list before Phase 2.","type":"text"}]},{"type":"paragraph","content":[{"text":"Output:","type":"text","marks":[{"type":"strong"}]},{"text":" A structured inventory list. Each item has:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"topic","type":"text","marks":[{"type":"code_inline"}]},{"text":" — what needs documenting","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"source","type":"text","marks":[{"type":"code_inline"}]},{"text":" — where the requirement was discovered (code path, config key, user flow, sonnet inference)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"audience","type":"text","marks":[{"type":"code_inline"}]},{"text":" — who needs this (end user, developer, operator)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"type","type":"text","marks":[{"type":"code_inline"}]},{"text":" — what kind of doc it needs (reference, tutorial, guide, explanation, runbook)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"confidence","type":"text","marks":[{"type":"code_inline"}]},{"text":" — high (deterministic) | medium | low (sonnet-inferred speculative)","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Phase 2: Map to Existing Documentation (per-docfile sonnet dispatch)","type":"text"}]},{"type":"paragraph","content":[{"text":"For each inventory item, determine whether it's documented and how well. \"Adequate coverage\" requires reading surrounding context — a grep hit doesn't tell you whether the topic is truly explained vs. just mentioned in passing. Orchestrator-side execution would require reading every doc ","type":"text"},{"text":"N","type":"text","marks":[{"type":"em"}]},{"text":" times (once per inventory item), which strains the context window.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Dispatch strategy","type":"text"}]},{"type":"paragraph","content":[{"text":"Two-phase mapping:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bulk grep pass (orchestrator)","type":"text","marks":[{"type":"strong"}]},{"text":" — for each inventory item, grep docs for the topic name. Build a candidate match map: which docs mention each topic.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Per-docfile sonnet pass","type":"text","marks":[{"type":"strong"}]},{"text":" — for each docfile that surfaced as a candidate match for any inventory item, dispatch one ","type":"text"},{"text":"general-purpose","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"sonnet","type":"text","marks":[{"type":"code_inline"}]},{"text":" agent. The agent receives the doc + the list of inventory items that grep'd to this doc, and judges each as Documented / Shallow / Misplaced.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"This keeps total agent calls ≈ N candidate docfiles (not N inventory items × M docs). For a typical project with 100 inventory items and 50 docs, the candidate map usually has 30–50 docs needing review.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Per-docfile prompt template","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"subagent_type: \"general-purpose\"\nmodel: \"sonnet\"\ndescription: \"Coverage mapping for \u003cdocfile>\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Prompt:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Read the doc at \u003cDOCFILE_PATH>. The following inventory items grep-matched\nthis doc — judge each:\n\n\u003cINVENTORY_ITEMS_FOR_THIS_DOC>\n\nFor each item, classify as one of:\n- Documented: dedicated section or page provides adequate coverage\n- Shallow: mentioned but insufficient (missing examples, edge cases,\n parameter listings; flag-in-table without explanation)\n- Misplaced: covered, but in the wrong doc type for the audience (API\n reference embedded in a tutorial; user-facing topic in dev-only docs)\n- No real match: grep matched but the doc doesn't actually cover the topic\n (incidental mention, different concept with the same word)\n\nOutput as YAML:\n\ndoc_path: \u003cpath>\nitems_reviewed: N\nclassifications:\n - item: \u003ctopic>\n classification: Documented | Shallow | Misplaced | No real match\n section: \u003cheading or line range where the topic is covered>\n evidence: \u003cquote or paraphrase of the relevant content>\n gap: \u003cif Shallow, what's missing; if Misplaced, where it should live>","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Items with no candidate match","type":"text"}]},{"type":"paragraph","content":[{"text":"Inventory items that grep'd 0 docs go directly to the \"Missing\" bucket without a sonnet review. The orchestrator handles these in Phase 3.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Why per-docfile rather than per-item","type":"text"}]},{"type":"paragraph","content":[{"text":"Per-item dispatch (one sonnet call per inventory item, reading every candidate doc fresh) blows up at any meaningful scale (100 items × 5 candidates = 500 calls). Per-docfile lets the agent see all related items in one pass and cross-reference within the doc — also higher precision than fragmented per-item judgments.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Phase 3: Classify Gaps by Impact","type":"text"}]},{"type":"paragraph","content":[{"text":"Not all gaps are equal. Score each gap using audience impact:","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Priority Framework","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":"Criteria","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Example","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"P0","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User cannot accomplish a core task without this","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No installation guide, undocumented required config","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"P1","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User can work around it but wastes significant time","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI flag exists but undocumented, error message without troubleshooting","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"P2","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Missing docs for secondary features or advanced use cases","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Plugin API undocumented, advanced config options missing","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"P3","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Missing docs for edge cases or rarely used features","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Obscure env var, deprecated feature migration path","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"P4","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nice to have — explanatory content, design rationale","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Architecture decision records, \"why\" behind defaults","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Audience Weighting","type":"text"}]},{"type":"paragraph","content":[{"text":"Apply a multiplier based on audience:","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":"Audience","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Weight","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rationale","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"New users / onboarding","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1.5x","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"First impressions; high abandonment risk","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Daily users","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1.0x","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Core audience","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Advanced users / contributors","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"0.8x","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Can read source when docs fail","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Internal operators","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"0.7x","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Can ask the team","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"A P2 gap for new users (P2 × 1.5 = 3.0) outranks a P1 gap for internal operators (P1 × 0.7 = 2.1).","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Phase 4: Produce the Gap Report","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Report Format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"# Documentation Completeness Audit\n\n**Audit date:** YYYY-MM-DD\n**Scope:** [directories or doc sets audited]\n**Inventory items:** N total\n**Coverage:** N documented / N shallow / N missing / N misplaced\n\n---\n\n## Summary\n\n[2-3 sentences: overall completeness assessment]\n\nCoverage by audience:\n| Audience | Documented | Shallow | Missing | Coverage % |\n|----------|-----------|---------|---------|------------|\n| New users | N | N | N | N% |\n| Daily users | N | N | N | N% |\n| Contributors | N | N | N | N% |\n| Operators | N | N | N | N% |\n\n---\n\n## P0 Gaps — Blocking\n\n| # | Topic | Audience | Source | Current State | What's Needed |\n|---|-------|----------|--------|---------------|---------------|\n| 1 | [topic] | [who] | [code path] | Missing | [what to write] |\n\n## P1 Gaps — High Impact\n\n| # | Topic | Audience | Source | Current State | What's Needed |\n|---|-------|----------|--------|---------------|---------------|\n\n## P2 Gaps — Moderate Impact\n\n| # | Topic | Audience | Source | Current State | What's Needed |\n|---|-------|----------|--------|---------------|---------------|\n\n## P3-P4 Gaps — Low Priority\n\n| # | Topic | Audience | Priority | Current State |\n|---|-------|----------|----------|---------------|\n\n---\n\n## Shallow Coverage Details\n\nFor each Shallow item, explain what's insufficient:\n\n### [Topic]\n**Current doc:** [path and section]\n**Problem:** [what's missing — examples, edge cases, complete reference, etc.]\n**Recommended action:** [specific improvement]\n\n---\n\n## Misplaced Documentation\n\n| Topic | Current Location | Recommended Location | Why |\n|-------|-----------------|---------------------|-----|\n\n---\n\n## Well-Documented (No Action Needed)\n\n[List topics with adequate coverage, grouped by audience, so the report\nshows the full picture and not just the gaps]","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Integration with Other Doc Skills","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill fits into the documentation health pipeline:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"doc-maintenance → Structural health (links, orphans, folders)\ndoc-claim-validator → Semantic accuracy (do claims match code?)\ndoc-completeness-audit → Topic coverage (is everything documented?)\ndoc-quality-review → Prose quality (is it well-written?)\ndoc-architecture-review → Information architecture (is it findable?)","type":"text"}]},{"type":"paragraph","content":[{"text":"Route gap remediation to the appropriate producer:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reference gaps → ","type":"text"},{"text":"reference-documentation","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tutorial gaps → ","type":"text"},{"text":"tutorial-design","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Explanation gaps → ","type":"text"},{"text":"documentation-production","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Anti-Patterns","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not count files as coverage — a file can exist and say nothing useful","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not manufacture gaps to look thorough — if coverage is good, say so","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not audit archived docs (","type":"text"},{"text":"docs/archive/","type":"text","marks":[{"type":"code_inline"}]},{"text":") — they are historical","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not require documentation for internal implementation details — only public surface","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not treat every function as needing its own doc page — aggregate by topic","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not conflate \"not documented\" with \"needs documenting\" — some things are correctly undocumented (internal helpers, deprecated code scheduled for removal)","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Bundled Resources","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Scripts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/inventory.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Extract documentable surface area from any codebase (env vars, CLI commands, config keys, HTTP endpoints, public exports, error types)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/coverage-model.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Defines coverage expectations per doc type and audience","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"doc-completeness-audit","tags":["documentation","audit","completeness","review"],"author":"@skillopedia","source":{"stars":15,"repo_name":"claude-ctx-plugin","origin_url":"https://github.com/nickcrew/claude-ctx-plugin/blob/HEAD/skills/doc-completeness-audit/SKILL.md","repo_owner":"nickcrew","body_sha256":"61839dc1f69714694ce3b00f292483b7de73caf5fd2e9734996e334b1ce058c7","cluster_key":"4164739b273a0a6a371f2ce677eda9d6e2b380dba5c2c28fa524a3857063b97f","clean_bundle":{"format":"clean-skill-bundle-v1","source":"nickcrew/claude-ctx-plugin/skills/doc-completeness-audit/SKILL.md","attachments":[{"id":"318cb841-36d8-5c52-bc70-f0ffe2c1a7cb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/318cb841-36d8-5c52-bc70-f0ffe2c1a7cb/attachment.md","path":"references/coverage-model.md","size":4090,"sha256":"469fee5af25d27075e08b70be7d4dcda65f32c8ab92d3f299886ae1768fc7c80","contentType":"text/markdown; charset=utf-8"},{"id":"9d85196a-21af-56d0-b4e8-4c6f71f1c948","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9d85196a-21af-56d0-b4e8-4c6f71f1c948/attachment.py","path":"scripts/inventory.py","size":29708,"sha256":"1ea8507773df94d0e7946819633b90bf98a2a8d7b8a5d579ce2366f127b23f3f","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"91f2a8c0d8382a1565d12df58fb953a0b93293f2d9a8a584145cc5789baede6e","attachment_count":2,"text_attachments":2,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/doc-completeness-audit/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"security","keywords":["doc gaps","audit coverage","completeness","doc","audit","doc completeness audit"],"triggers":["audit doc completeness","what docs are missing","doc coverage gaps","documentation gaps","are docs complete"],"import_tag":"clean-skills-v1","description":"Audit documentation completeness by mapping what a doc set should cover against what it actually covers. Produces a prioritized gap report by topic, not just by file. This skill should be used after shipping features, before releases, or when users report missing documentation.","dependencies":{"tools":["Grep","Glob","Read","Bash","Agent"],"skills":["doc-maintenance","doc-claim-validator"]},"token_estimate":"~3500"}},"renderedAt":1782980293423}

Documentation Completeness Audit Determine whether a documentation set covers everything it should by building an inventory of what needs documenting and comparing it to what exists . The output is a prioritized gap report — not new documentation. When to Use - After shipping a feature — verify docs cover the new surface area - Before a release — ensure no undocumented public APIs, CLI flags, or config options - When users or new hires report "I couldn't find docs for X" - Periodic health check on doc coverage - After running (structural) and (accuracy) to go wider Quick Reference | Resource…