{"id":"fc41970f-6ffe-42ce-8997-0d7e51f9ccb9","entityType":"agent","slug":"clawhub-athola-nm-sanctum-doc-consolidation","name":"doc-consolidation","canonicalUrl":"https://www.xpersona.co/agent/clawhub-athola-nm-sanctum-doc-consolidation","canonicalPath":"/agent/clawhub-athola-nm-sanctum-doc-consolidation","generatedAt":"2026-10-10T10:59:46.475Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T05:42:52.159Z","emptyReason":null},"description":"Merges ephemeral report and analysis artifacts into permanent documentation Skill: doc-consolidation Owner: athola Summary: Merges ephemeral report and analysis artifacts into permanent documentation Tags: latest:1.9.19 Version history: v1.9.19 | 2026-08-26T13:20:09.156Z | user Release v1.9.19 v1.9.17 | 2026-07-30T05:40:14.609Z | user Release v1.9.17 v1.9.16 | 2026-07-14T19:57:07.565Z | user Release v1.9.16 v1.9.14 | 2026-06-30T18:05:05.720Z | user Release v1.9.14 v1.9.13 | 2026-06-27T16:22:","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.6K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17emme0e2m3cpf7k2jvp3a84984b8z9:nm-sanctum-doc-consolidation","sourceUrl":"https://clawhub.ai/athola/nm-sanctum-doc-consolidation","homepage":"https://clawhub.ai/athola/skills/nm-sanctum-doc-consolidation","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/athola/nm-sanctum-doc-consolidation","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/athola/skills/nm-sanctum-doc-consolidation","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":64,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Merges ephemeral report and analysis artifacts into permanent documentation Skill: doc-consolidation Owner: athola Summary: Merges ephemeral report and analysis"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:42:52.159Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:42:52.159Z","emptyReason":null},"stars":null,"forks":null,"downloads":1644,"packageName":null,"latestVersion":"1.9.19","tractionLabel":"1.6K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:42:52.159Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T05:42:52.159Z","lastCrawledAt":"2026-10-10T05:42:52.159Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T05:42:52.159Z","lastVerifiedAt":null,"highlights":[{"version":"1.9.19","createdAt":"2026-08-26T13:20:09.156Z","changelog":"Release v1.9.19","fileCount":7,"zipByteSize":16466},{"version":"1.9.17","createdAt":"2026-07-30T05:40:14.609Z","changelog":"Release v1.9.17","fileCount":7,"zipByteSize":16514},{"version":"1.9.16","createdAt":"2026-07-14T19:57:07.565Z","changelog":"Release v1.9.16","fileCount":7,"zipByteSize":16672},{"version":"1.9.14","createdAt":"2026-06-30T18:05:05.720Z","changelog":"Release v1.9.14","fileCount":7,"zipByteSize":16542},{"version":"1.9.13","createdAt":"2026-06-27T16:22:59.495Z","changelog":"Release v1.9.13","fileCount":7,"zipByteSize":16472},{"version":"1.9.12","createdAt":"2026-06-19T03:18:21.252Z","changelog":"Release v1.9.12","fileCount":7,"zipByteSize":16500},{"version":"1.0.3","createdAt":"2026-06-18T15:20:08.608Z","changelog":"Release v1.9.12","fileCount":7,"zipByteSize":16516},{"version":"1.0.2","createdAt":"2026-05-09T02:19:45.894Z","changelog":"Release v1.9.5","fileCount":7,"zipByteSize":16457}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17emme0e2m3cpf7k2jvp3a84984b8z9:nm-sanctum-doc-consolidation","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T10:59:46.473Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-athola-nm-sanctum-doc-consolidation/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T05:42:52.159Z","emptyReason":null},"readme":"Skill: doc-consolidation\n\nOwner: athola\n\nSummary: Merges ephemeral report and analysis artifacts into permanent documentation\n\nTags: latest:1.9.19\n\nVersion history:\n\nv1.9.19 | 2026-08-26T13:20:09.156Z | user\n\nRelease v1.9.19\n\nv1.9.17 | 2026-07-30T05:40:14.609Z | user\n\nRelease v1.9.17\n\nv1.9.16 | 2026-07-14T19:57:07.565Z | user\n\nRelease v1.9.16\n\nv1.9.14 | 2026-06-30T18:05:05.720Z | user\n\nRelease v1.9.14\n\nv1.9.13 | 2026-06-27T16:22:59.495Z | user\n\nRelease v1.9.13\n\nv1.9.12 | 2026-06-19T03:18:21.252Z | user\n\nRelease v1.9.12\n\nv1.0.3 | 2026-06-18T15:20:08.608Z | user\n\nRelease v1.9.12\n\nv1.0.2 | 2026-05-09T02:19:45.894Z | user\n\nRelease v1.9.5\n\nv1.0.1 | 2026-05-06T14:21:06.691Z | user\n\nRelease v1.9.4\n\nv1.0.0 | 2026-04-15T17:01:55.906Z | auto\n\n- Initial public release of the doc-consolidation skill.\n- Extracts and merges valuable content from ephemeral LLM report and analysis files into permanent documentation.\n- Two-phase workflow: fast triage to generate and preview a consolidation plan, followed by safe execution.\n- Automatically detects candidate files, analyzes and categorizes content, matches it to destination docs, merges intelligently, and deletes sources.\n- Integrated strategies for weaving, replacing, appending, or creating new documentation sections.\n- Designed for improved git hygiene, knowledge retention, and reduced clutter when preparing PRs.\n\nArchive index:\n\nArchive v1.9.19: 7 files, 16466 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), skill-card.md (2037b), SKILL.md (9098b), _meta.json (148b)\n\nFile v1.9.19:SKILL.md\n\n---\nname: doc-consolidation\ndescription: Merges ephemeral report and analysis artifacts into permanent documentation\nversion: 1.9.8\ntriggers:\n  - docs\n  - consolidation\n  - cleanup\n  - git-hygiene\n  - knowledge-management\n  - LLM-generated markdown files have accumulated and need consolidation\nmetadata: {\"openclaw\": {\"homepage\": \"https://github.com/athola/claude-night-market/tree/master/plugins/sanctum\", \"emoji\": \"\\ud83d\\udcdd\"}}\nsource: claude-night-market\nsource_plugin: sanctum\n---\n\n> **Night Market Skill** — ported from [claude-night-market/sanctum](https://github.com/athola/claude-night-market/tree/master/plugins/sanctum). For the full experience with agents, hooks, and commands, install the Claude Code plugin.\n\n\n## Table of Contents\n\n- [When to Use](#when-to-use)\n- [Quick Start](#quick-start)\n- [Two-Phase Workflow](#two-phase-workflow)\n- [Phase 1: Triage (Fast Model)](#phase-1:-triage-(fast-model))\n- [Phase 2: Execute (Main Model)](#phase-2:-execute-(main-model))\n- [Workflow Details](#workflow-details)\n- [Step 1: Candidate Detection](#step-1:-candidate-detection)\n- [Step 2: Content Analysis](#step-2:-content-analysis)\n- [Step 3: Destination Routing](#step-3:-destination-routing)\n- [Step 4: Generate Plan](#step-4:-generate-plan)\n- [Source: API_REVIEW_REPORT.md](#source:-api_review_reportmd)\n- [Post-Consolidation](#post-consolidation)\n- [Step 5: Execute Merges](#step-5:-execute-merges)\n- [Fast Model Delegation](#fast-model-delegation)\n- [Content Categories](#content-categories)\n- [Merge Strategies](#merge-strategies)\n- [Intelligent Weave](#intelligent-weave)\n- [Replace Section](#replace-section)\n- [Append with Context](#append-with-context)\n- [Create New File](#create-new-file)\n- [Integration](#integration)\n- [Example Session](#example-session)\n- [Troubleshooting](#troubleshooting)\n- [No candidates found](#no-candidates-found)\n- [Low-quality extractions](#low-quality-extractions)\n- [Merge conflicts](#merge-conflicts)\n- [Related Skills](#related-skills)\n\n\n# Doc Consolidation\n\nExtracts valuable knowledge from ephemeral LLM outputs and merges it into permanent documentation.\n\n## When To Use\n\nUse this skill when:\n- You have untracked `*_REPORT.md` or `*_ANALYSIS.md` files from Claude sessions\n- Git status shows markdown files that shouldn't be committed but contain useful content\n- You want to preserve insights from code reviews, refactoring reports, or API audits\n- Preparing a PR and need to clean up working artifacts\n\nDo NOT use when:\n- Files are already in proper documentation locations\n  (`docs/`, `skills/`)\n- Files are intentionally temporary scratch notes\n- User explicitly wants to preserve the original report format\n- Source files have no extractable value (pure log output)\n\n## Formatting\n\nWhen merging content into permanent documentation, follow\n`Skill(leyline:markdown-formatting)` conventions: wrap prose\nat 80 chars (prefer sentence/clause boundaries), blank lines\naround headings, ATX headings only, blank line before lists,\nand reference-style links for long URLs.\n\n## Quick Start\n\n```\n/consolidate-docs\n```\n\nOr invoke directly:\n\n```\nI have some report files that need consolidating into permanent docs.\n```\n\n## Two-Phase Workflow\n\n### Phase 1: Triage (Fast Model)\n\nRead-only analysis to generate a consolidation plan:\n\n1. **Detect candidates** - Find untracked markdown files with LLM output markers\n2. **Analyze content** - Extract and categorize valuable sections\n3. **Route destinations** - Match content to existing docs or propose new files\n4. **Present plan** - Show user what will be consolidated and where\n\n**Checkpoint**: User reviews and approves plan before execution.\n\n### Phase 2: Execute (Main Model)\n\nAfter approval, performs the consolidation:\n\n1. **Merge content** - Weave into existing docs or create new files\n2. **Delete sources** - Remove ephemeral files after successful merge\n3. **Generate summary** - Report what was created/updated/deleted\n\n## Workflow Details\n\n### Step 1: Candidate Detection\n\nLoad: `@modules/candidate-detection.md`\n\nIdentifies files using:\n- Git status (untracked `.md` files)\n- Location (not in standard doc directories)\n- Naming (ALL_CAPS non-standard names)\n- Content markers (Executive Summary, Findings, Action Items)\n\n### Step 2: Content Analysis\n\nLoad: `@modules/content-analysis.md`\n\nFor each candidate:\n- Extract sections as content chunks\n- Categorize: Actionable Items, Decisions, Findings, Metrics, Migration Guides, API Changes\n- Score value: high/medium/low\n\n### Step 3: Destination Routing\n\nLoad: `@modules/destination-routing.md`\n\nFor each valuable chunk:\n- Semantic match against existing documentation\n- Apply default mappings if no good match\n- Determine merge strategy (weave, replace, append, create)\n\n### Step 4: Generate Plan\n\nPresent consolidation plan to user:\n\n```markdown\n# Consolidation Plan\n\n## Source: API_REVIEW_REPORT.md\n\n| Content | Category | Value | Destination | Action |\n|---------|----------|-------|-------------|--------|\n| API inventory | Findings | High | docs/api-overview.md | Create |\n| Action items | Actionable | High | docs/plans/2025-12-06-api.md | Create |\n\n### Post-Consolidation\n- Delete: API_REVIEW_REPORT.md\n\nProceed with consolidation? [Y/n]\n```\n\n### Step 5: Execute Merges\n\nLoad: `@modules/merge-execution.md`\n\nAfter user approval:\n- Group operations by destination file\n- Apply merge strategies\n- Validate results (frontmatter intact, structure preserved)\n- Delete source files\n- Generate execution summary\n\n## Fast Model Delegation\n\nPhase 1 tasks are delegated to haiku-class models for efficiency:\n\n```python\n# plugins/sanctum/scripts/consolidation_planner.py handles:\n- scan_for_candidates()\n- extract_content_chunks()\n- categorize_chunks()\n- score_value()\n- find_semantic_matches()\n```\n\nPhase 2 stays on the main model for careful merge execution.\n\n## Content Categories\n\n| Category | Description | Default Destination |\n|----------|-------------|---------------------|\n| Actionable Items | Tasks, TODOs, next steps | `docs/plans/YYYY-MM-DD-{topic}.md` |\n| Decisions Made | Architecture choices | `docs/adr/NNNN-{date}-{topic}.md` |\n| Findings/Insights | Audit results, analysis | Best-match existing doc |\n| Metrics/Baselines | Before/after comparisons | `docs/benchmarks/` |\n| Migration Guides | Step-by-step procedures | `docs/migration-guide.md` |\n| API Changes | Breaking changes, deprecations | CHANGELOG or api docs |\n\n## Merge Strategies\n\n### Intelligent Weave\nInsert content into matching existing sections, preserving style.\n\n  Use when: Destination has relevant section, content is additive.\n\n### Replace Section\nReplace existing section with more detailed new content.\n\n  Use when: New content 2x+ more detailed or has later date.\n\n### Append with Context\nAdd new section with date and source reference.\n\n  Use when: No matching section, content doesn't fit existing structure.\n\n### Create New File\nGenerate complete new document.\n\n  Use when: No suitable destination exists, content warrants standalone doc.\n\n## Integration\n\nWorks with other sanctum skills:\n\n- **git-workspace-review**: May suggest consolidation when untracked reports detected\n- **pr-prep**: Warns about untracked analysis files before PR creation\n- **doc-updates**: Can invoke for final polish after consolidation\n\n## Example Session\n\n```\nUser: I have API_REVIEW_REPORT.md from our review session. Can you consolidate it?\n\nClaude: I'll consolidate the report into permanent documentation.\n\n**Phase 1: Triage**\n\nScanning for candidates...\n- Found: API_REVIEW_REPORT.md (untracked, LLM markers detected)\n\nAnalyzing content...\n- Extracted 6 content chunks\n- High value: API inventory, action items, CLI recommendation\n- Medium value: security assessment\n- Low value: executive summary (generic)\n\nRouting destinations...\n- API inventory → docs/api-overview.md (CREATE)\n- Action items → docs/plans/2025-12-06-api-consistency.md (CREATE)\n- CLI naming → docs/adr/0002-cli-naming.md (CREATE ADR)\n- Security → Already covered in docs/security.md (SKIP)\n\n**Consolidation Plan**\n[table shown]\n\nProceed? [Y/n]\n\nUser: Y\n\nClaude: **Phase 2: Executing**\n\nCreated: docs/api-overview.md (1,247 bytes)\nCreated: docs/plans/2025-12-06-api-consistency.md (892 bytes)\nCreated: docs/adr/0002-2025-12-06-cli-naming.md (634 bytes)\nDeleted: API_REVIEW_REPORT.md\n\nConsolidation complete. Review the created files and commit when ready.\n```\n\n## Troubleshooting\n\n### No candidates found\n- Check git status for untracked files\n- Files may already be in standard locations\n- Try explicit file path: \"Consolidate API_REVIEW_REPORT.md\"\n\n### Low-quality extractions\n- Source file may lack structured sections\n- Content may be too generic to categorize\n- Try manual extraction for unstructured reports\n\n### Merge conflicts\n- Destination file structure changed\n- Try APPEND strategy instead of WEAVE\n- Manual intervention may be needed\n\n## Related Skills\n\n- `sanctum:doc-updates` - General documentation updates\n- `sanctum:git-workspace-review` - Pre-flight workspace analysis\n- `sanctum:pr-prep` - Pull request preparation\n- `imbue:catchup` - Understanding recent changes\n\nFile v1.9.19:_meta.json\n\n{\n  \"ownerId\": \"kn7d107jg9jv602h9ytsegydq184a42s\",\n  \"slug\": \"nm-sanctum-doc-consolidation\",\n  \"version\": \"1.9.19\",\n  \"publishedAt\": 1787750409156\n}\n\nFile v1.9.19:modules/candidate-detection.md\n\n# Candidate Detection Module\n\nIdentifies markdown files that are candidates for consolidation.\n\n## Detection Signals\n\nApply signals in priority order. A file is a candidate if it matches **any** signal.\n\n### Signal 1: Git-Untracked Location (Highest Priority)\n\n```bash\n# Find untracked .md files\ngit status --porcelain | grep '^??' | grep '\\.md$'\n```\n\n**Exclude standard locations:**\n- `docs/` - Already permanent documentation\n- `skills/` - Skill definitions\n- `modules/` - Skill modules\n- `commands/` - Slash commands\n- `agents/` - Agent definitions\n- `.github/` - GitHub templates\n\n**Exclude standard names:**\n- `README.md`, `README`\n- `LICENSE.md`, `LICENSE`\n- `CONTRIBUTING.md`\n- `CHANGELOG.md`, `HISTORY.md`\n- `SECURITY.md`\n- `CODE_OF_CONDUCT.md`\n\n### Signal 2: ALL_CAPS Naming Pattern\n\nFiles with ALL_CAPS names that aren't standard conventions:\n\n```\nMATCHES (candidates):\n- API_REVIEW_REPORT.md\n- REFACTORING_REPORT.md\n- MIGRATION_ANALYSIS.md\n- AUDIT_FINDINGS.md\n- *_REPORT.md\n- *_ANALYSIS.md\n- *_REVIEW.md\n- *_FINDINGS.md\n\nEXCLUDES (not candidates):\n- README.md\n- LICENSE.md\n- CONTRIBUTING.md\n- CHANGELOG.md\n- SECURITY.md\n- CODE_OF_CONDUCT.md\n```\n\n### Signal 3: Content Markers\n\nScan first 100 lines for LLM output markers:\n\n**Strong markers (any one = candidate):**\n- `**Date**:` or `Date:` at start of line\n- `## Executive Summary`\n- `## Summary` (at document start)\n- `## Findings`\n- `## Action Items`\n- `## Recommendations`\n- `## Conclusion`\n\n**Supporting markers (need 2+ to qualify):**\n- Markdown tables with `|` columns\n- `### High Priority` / `### Medium Priority` / `### Low Priority`\n- `- [ ]` checkbox lists\n- `## 1.` numbered top-level sections\n- `**Scope**:` or `**Status**:`\n- Lines starting with status markers\n\n## Detection Algorithm\n\n```python\ndef detect_candidates(repo_path: str) -> list[CandidateFile]:\n    candidates = []\n\n    # Get untracked markdown files\n    untracked = git_untracked_md_files(repo_path)\n\n    for file_path in untracked:\n        # Skip standard locations\n        if is_standard_location(file_path):\n            continue\n\n        # Skip standard names\n        if is_standard_name(file_path):\n            continue\n\n        score = 0\n        reasons = []\n\n        # Check naming pattern\n        if is_allcaps_nonstandard(file_path):\n            score += 3\n            reasons.append(\"ALL_CAPS non-standard name\")\n\n        # Check content markers\n        content = read_first_n_lines(file_path, 100)\n        strong, supporting = count_content_markers(content)\n\n        if strong > 0:\n            score += 3\n            reasons.append(f\"Strong markers: {strong}\")\n\n        if supporting >= 2:\n            score += 2\n            reasons.append(f\"Supporting markers: {supporting}\")\n\n        # Threshold: score >= 2\n        if score >= 2:\n            candidates.append(CandidateFile(\n                path=file_path,\n                score=score,\n                reasons=reasons\n            ))\n\n    return sorted(candidates, key=lambda c: c.score, reverse=True)\n```\n\n## Output Format\n\n```markdown\n## Detected Candidates\n\n| File | Score | Reasons |\n|------|-------|---------|\n| API_REVIEW_REPORT.md | 6 | ALL_CAPS name, Strong markers: 3, Supporting: 4 |\n| REFACTORING_REPORT.md | 5 | ALL_CAPS name, Strong markers: 2 |\n| analysis-notes.md | 2 | Supporting markers: 3 |\n\nProceeding with 3 candidates...\n```\n\n## Edge Cases\n\n### Nested untracked directories\nIf an entire directory is untracked, scan all `.md` files within:\n```bash\ngit status --porcelain | grep '^??' | while read status path; do\n  if [ -d \"$path\" ]; then\n    find \"$path\" -name \"*.md\"\n  fi\ndone\n```\n\n### Partially staged files\nFiles that are partially staged (`MM` or `AM` status) should be flagged for user attention - they may contain mixed committed/uncommitted content.\n\n### Renamed/moved files\nIf git shows a rename (`R` status), check if the destination is a standard location. If moving TO a standard location, not a candidate.\n\n## Validation\n\nBefore proceeding, confirm candidates with user:\n\n```markdown\nFound 3 consolidation candidates:\n\n1. **API_REVIEW_REPORT.md** (score: 6)\n   - ALL_CAPS non-standard name\n   - Contains: Executive Summary, Findings, Action Items\n\n2. **REFACTORING_REPORT.md** (score: 5)\n   - ALL_CAPS non-standard name\n   - Contains: Summary, Conclusion\n\n3. **analysis-notes.md** (score: 2)\n   - Contains: Multiple tables, checkbox lists\n\nAnalyze these files for consolidation? [Y/n/select specific]\n```\n\nFile v1.9.19:modules/content-analysis.md\n\n# Content Analysis Module\n\nExtracts and categorizes valuable content from candidate files.\n\n## Content Categories\n\n### Category Definitions\n\n| Category | Description | Indicators |\n|----------|-------------|------------|\n| **Actionable Items** | Tasks, TODOs, next steps that require action | `Action Items`, `Next Steps`, `TODO`, `- [ ]` checkboxes |\n| **Decisions Made** | Architecture choices, tradeoffs, rationale | `Decision`, `Chose`, `Tradeoff`, `Rationale`, `Why we` |\n| **Findings/Insights** | Audit results, analysis conclusions, observations | `Findings`, `Observations`, `Analysis`, `Discovered`, `Noted` |\n| **Metrics/Baselines** | Quantitative data, before/after, benchmarks | Tables with numbers, `Before`, `After`, percentages, `Improvement` |\n| **Migration Guides** | Step-by-step procedures, how-to instructions | `Steps`, `How to`, `Migration`, numbered lists with commands |\n| **API Changes** | Interface modifications, breaking changes, deprecations | `API`, `Breaking`, `Deprecated`, `New endpoint`, `Removed` |\n\n### Extraction Process\n\nFor each candidate file:\n\n1. **Parse structure** - Identify sections by headers (`##`, `###`)\n2. **Extract chunks** - Each section becomes a content chunk\n3. **Categorize** - Match chunk to best-fit category\n4. **Score value** - Assess high/medium/low\n\n## Value Scoring\n\n### High Value\nContent that is:\n- **Specific**: Contains concrete names, paths, numbers\n- **Actionable**: Reader can act on it directly\n- **Unique**: Not already documented elsewhere\n\nExamples:\n- Specific action items with owners\n- Concrete metrics (before: 287 lines, after: 255 lines)\n- Explicit decisions with rationale\n- Step-by-step procedures that worked\n\n### Medium Value\nContent that is:\n- **Somewhat specific**: General guidance with some detail\n- **Reference-worthy**: Useful for future lookups\n- **Partially covered**: Extends existing documentation\n\nExamples:\n- General recommendations without specifics\n- Findings that align with existing docs\n- Metrics without clear baseline comparison\n\n### Low Value\nContent that is:\n- **Generic**: Could apply to any project\n- **Redundant**: Already well-documented elsewhere\n- **Ephemeral**: Only relevant to the moment\n\nExamples:\n- Executive summaries (usually boilerplate)\n- Generic best practice reminders\n- Status statements (\"The review is complete\")\n\n## Chunk Extraction Algorithm\n\n```python\ndef extract_chunks(content: str) -> list[ContentChunk]:\n    chunks = []\n    current_section = None\n    current_content = []\n\n    for line in content.split('\\n'):\n        # New section header\n        if line.startswith('## '):\n            if current_section:\n                chunks.append(make_chunk(current_section, current_content))\n            current_section = line[3:].strip()\n            current_content = []\n        elif line.startswith('### '):\n            # Subsection - append to current or create new\n            if current_section:\n                current_content.append(line)\n            else:\n                current_section = line[4:].strip()\n                current_content = []\n        else:\n            current_content.append(line)\n\n    # Don't forget last section\n    if current_section:\n        chunks.append(make_chunk(current_section, current_content))\n\n    return chunks\n\ndef make_chunk(header: str, content: list[str]) -> ContentChunk:\n    text = '\\n'.join(content).strip()\n    category = categorize(header, text)\n    value = score_value(text, category)\n\n    return ContentChunk(\n        header=header,\n        content=text,\n        category=category,\n        value=value\n    )\n```\n\n## Categorization Rules\n\nMatch in order (first match wins):\n\n```python\nCATEGORY_PATTERNS = {\n    'actionable': [\n        r'action\\s*items?',\n        r'next\\s*steps?',\n        r'todo',\n        r'tasks?',\n        r'- \\[ \\]',  # Unchecked checkboxes\n    ],\n    'decisions': [\n        r'decision',\n        r'chose|chosen',\n        r'tradeoff',\n        r'rationale',\n        r'why\\s+we',\n        r'approach',\n    ],\n    'findings': [\n        r'finding',\n        r'observation',\n        r'analysis',\n        r'discovered',\n        r'audit',\n        r'review\\s+result',\n    ],\n    'metrics': [\n        r'\\d+%',\n        r'before.*after',\n        r'improvement',\n        r'reduction',\n        r'benchmark',\n        r'\\|\\s*\\d+\\s*\\|',  # Table with numbers\n    ],\n    'migration': [\n        r'migration',\n        r'step\\s*\\d',\n        r'how\\s+to',\n        r'procedure',\n        r'```bash',  # Code blocks with commands\n    ],\n    'api_changes': [\n        r'api',\n        r'breaking\\s+change',\n        r'deprecat',\n        r'endpoint',\n        r'interface',\n    ],\n}\n```\n\n## Output Format\n\n```markdown\n## Content Analysis: API_REVIEW_REPORT.md\n\n### Extracted Chunks\n\n| # | Section | Category | Value | Size |\n|---|---------|----------|-------|------|\n| 1 | Executive Summary | findings | low | 234 chars |\n| 2 | API Surface Inventory | findings | high | 1,847 chars |\n| 3 | Consistency Audit Findings | findings | high | 2,103 chars |\n| 4 | Action Items | actionable | high | 1,456 chars |\n| 5 | Recommendations | actionable | medium | 892 chars |\n| 6 | Conclusion | findings | low | 312 chars |\n\n### High-Value Content (4 chunks)\n- API Surface Inventory: Detailed plugin API table\n- Consistency Audit Findings: Specific issues with examples\n- Action Items: Concrete tasks with priorities\n- (included in routing)\n\n### Excluded (Low Value)\n- Executive Summary: Generic overview\n- Conclusion: Status statement only\n```\n\n## Special Handling\n\n### Tables\nPreserve markdown tables intact - they often contain valuable structured data:\n```python\ndef is_table(lines: list[str]) -> bool:\n    return any('|' in line and line.count('|') >= 2 for line in lines)\n```\n\n### Code Blocks\nPreserve code blocks, especially those showing:\n- Configuration examples\n- Command sequences\n- Before/after code comparisons\n\n### Checklists\nPreserve checkbox lists - they indicate actionable items:\n```markdown\n- [x] Completed item (historical record)\n- [ ] Pending item (action needed)\n```\n\n### Cross-references\nNote internal references for destination routing:\n```python\n# Links like \"See also: docs/security.md\" suggest destinations\nREFERENCE_PATTERN = r'see\\s+(?:also:?\\s*)?([^\\s,]+\\.md)'\n```\n\nFile v1.9.19:modules/destination-routing.md\n\n# Destination Routing Module\n\nMaps extracted content chunks to appropriate destinations in the documentation.\n\n## Routing Strategy\n\n### Priority Order\n\n1. **Semantic match** - Find existing doc that covers the topic\n2. **Default mapping**\n - Use category-based default destinations\n3. **Create new** - Only when no suitable destination exists\n\n### Preference: Existing Over New\n\nAlways prefer merging into existing documentation:\n- Keeps documentation consolidated\n- Avoids duplicate coverage\n- Maintains established structure\n\nCreate new files only when:\n- Content is substantial (>500 chars of high-value)\n- No existing doc covers the topic\n- Content warrants standalone treatment\n\n## Semantic Matching\n\n### Algorithm\n\n```python\ndef find_semantic_match(chunk: ContentChunk, existing_docs: list[str]) -> str | None:\n    \"\"\"Find best-matching existing document for a content chunk.\"\"\"\n\n    best_match = None\n    best_score = 0\n\n    for doc_path in existing_docs:\n        doc_content = read_file(doc_path)\n        score = compute_relevance(chunk, doc_content)\n\n        if score > best_score and score >= MATCH_THRESHOLD:\n            best_match = doc_path\n            best_score = score\n\n    return best_match\n\ndef compute_relevance(chunk: ContentChunk, doc_content: str) -> float:\n    \"\"\"Score relevance of chunk to document.\"\"\"\n    score = 0.0\n\n    # Header matching (highest weight)\n    doc_headers = extract_headers(doc_content)\n    if any(similar(chunk.header, h) for h in doc_headers):\n        score += 0.4\n\n    # Keyword overlap\n    chunk_keywords = extract_keywords(chunk.content)\n    doc_keywords = extract_keywords(doc_content)\n    overlap = len(chunk_keywords & doc_keywords) / len(chunk_keywords)\n    score += overlap * 0.3\n\n    # Category alignment\n    if doc_likely_category(doc_content) == chunk.category:\n        score += 0.2\n\n    # Reference mentions\n    if chunk mentions doc_path or doc mentions chunk source:\n        score += 0.1\n\n    return score\n\nMATCH_THRESHOLD = 0.5  # Minimum score to consider a match\n```\n\n### Existing Doc Discovery\n\nScan these locations for potential destinations:\n\n```python\nDOC_LOCATIONS = [\n    'docs/',\n    'docs/plans/',\n    'docs/adr/',\n    'README.md',\n    'CHANGELOG.md',\n]\n\n# Plugin-specific locations\nPLUGIN_DOC_LOCATIONS = [\n    '{plugin}/docs/',\n    '{plugin}/README.md',\n]\n```\n\n## Default Mappings\n\nWhen semantic matching finds no suitable destination:\n\n| Category | Default Destination | Notes |\n|----------|---------------------|-------|\n| Actionable Items | `docs/plans/YYYY-MM-DD-{topic}.md` | New plan file |\n| Decisions Made | `docs/adr/NNNN-YYYY-MM-DD-{topic}.md` | New ADR |\n| Findings/Insights | `docs/{topic}.md` | New doc or best-effort match |\n| Metrics/Baselines | `docs/benchmarks.md` or inline | Append if exists |\n| Migration Guides | `docs/migration-guide.md` | Append section |\n| API Changes | `CHANGELOG.md` or `docs/api.md` | Prefer CHANGELOG |\n\n### Topic Extraction\n\nDerive topic slug from content:\n\n```python\ndef extract_topic(chunk: ContentChunk, source_file: str) -> str:\n    \"\"\"Extract topic slug for file naming.\"\"\"\n\n    # Try chunk header first\n    if chunk.header:\n        return slugify(chunk.header)\n\n    # Try source file name\n    source_name = Path(source_file).stem\n    if '_REPORT' in source_name:\n        return slugify(source_name.replace('_REPORT', ''))\n\n    # Fall back to category\n    return chunk.category\n\ndef slugify(text: str) -> str:\n    \"\"\"Convert text to kebab-case slug.\"\"\"\n    text = text.lower()\n    text = re.sub(r'[^a-z0-9]+', '-', text)\n    text = text.strip('-')\n    return text[:50]  # Max length\n```\n\n## Merge Strategy Selection\n\nFor each chunk-destination pair, determine how to merge:\n\n### Decision Tree\n\n```\nIs destination a new file?\n├── Yes → CREATE_NEW\n└── No → Does destination have matching section?\n    ├── Yes → Is new content more detailed?\n    │   ├── Yes (2x+ detail OR newer date) → REPLACE_SECTION\n    │   └── No → INTELLIGENT_WEAVE\n    └── No → APPEND_WITH_CONTEXT\n```\n\n### Strategy Definitions\n\n**CREATE_NEW**\n- Generate complete new file with frontmatter\n- Use appropriate template for category\n- Include source attribution\n\n**INTELLIGENT_WEAVE**\n- Find matching section in destination\n- Insert content matching existing style\n- Preserve bullet/table/prose formatting\n\n**REPLACE_SECTION**\n- Back up existing content (in consolidation log)\n- Replace entire section\n- Add \"Updated: YYYY-MM-DD\" marker\n\n**APPEND_WITH_CONTEXT**\n- Add new section at logical location\n- Include header with date and source\n- Format: `## {Topic} (consolidated from {source}, {date})`\n\n## Output Format\n\n```markdown\n## Routing Plan\n\n### Source: API_REVIEW_REPORT.md\n\n| Chunk | Destination | Strategy | Rationale |\n|-------|-------------|----------|-----------|\n| API Surface Inventory | docs/api-overview.md | CREATE_NEW | No existing API docs, substantial content |\n| Consistency Findings | docs/architecture.md | APPEND_WITH_CONTEXT | Related topic, no matching section |\n| Action Items | docs/plans/2025-12-06-api-consistency.md | CREATE_NEW | Actionable, needs tracking |\n| CLI Recommendation | docs/adr/0002-cli-naming.md | CREATE_NEW | Decision warrants ADR |\n\n### Skipped (Low Value)\n| Chunk | Reason |\n|-------|--------|\n| Executive Summary | Generic, low value |\n| Conclusion | Status only, no lasting value |\n\n### Destination Summary\n- **New files**: 3\n- **Updates to existing**: 1\n- **Skipped**: 2\n```\n\n## ADR Generation\n\nFor decisions that warrant an ADR:\n\n```markdown\n# ADR-{NNNN}: {Decision Title}\n\n**Date**: {YYYY-MM-DD}\n**Status**: Accepted\n**Consolidated from**: {source_file}\n\n## Context\n\n{extracted context from source}\n\n## Decision\n\n{extracted decision}\n\n## Consequences\n\n{extracted consequences or \"To be determined\"}\n\n## References\n\n- Source: {source_file} (consolidated {date})\n```\n\n### ADR Numbering\n\n```python\ndef next_adr_number(adr_dir: str) -> int:\n    \"\"\"Find next available ADR number.\"\"\"\n    existing = glob(f\"{adr_dir}/[0-9][0-9][0-9][0-9]-*.md\")\n    if not existing:\n        return 1\n    numbers = [int(Path(p).name[:4]) for p in existing]\n    return max(numbers) + 1\n```\n\n## Validation\n\nBefore finalizing routing:\n\n1. **Check destination exists** (for updates)\n2. **Check write permissions**\n3. **Verify no circular references**\n4. **Confirm ADR numbering is unique**\n\n```python\ndef validate_routing(plan: RoutingPlan) -> list[str]:\n    \"\"\"Return list of validation errors.\"\"\"\n    errors = []\n\n    for route in plan.routes:\n        if route.strategy != 'CREATE_NEW':\n            if not Path(route.destination).exists():\n                errors.append(f\"Destination not found: {route.destination}\")\n\n        if route.strategy == 'CREATE_NEW':\n            if Path(route.destination).exists():\n                errors.append(f\"Would overwrite existing: {route.destination}\")\n\n    return errors\n```\n\nFile v1.9.19:modules/merge-execution.md\n\n# Merge Execution Module\n\nExecutes the approved consolidation plan, performing actual file operations.\n\n## Execution Order\n\n1. **Group by destination** - Minimize file I/O\n2. **Process creates first** - New files before updates\n3. **Process updates** - Apply merges to existing files\n4. **Delete sources** - Remove after successful consolidation\n5. **Generate summary** - Report all changes\n\n## Pre-Execution Checks\n\nBefore any file operations:\n\n```python\ndef pre_execution_checks(plan: ConsolidationPlan) -> list[str]:\n    \"\"\"Validate plan is safe to execute. Returns errors.\"\"\"\n    errors = []\n\n    # Check all destinations are writable\n    for route in plan.routes:\n        dest_dir = Path(route.destination).parent\n        if not dest_dir.exists():\n            # Will create - check parent is writable\n            if not os.access(dest_dir.parent, os.W_OK):\n                errors.append(f\"Cannot create directory: {dest_dir}\")\n        elif not os.access(route.destination, os.W_OK):\n            errors.append(f\"Cannot write to: {route.destination}\")\n\n    # Check sources exist and are readable\n    for source in plan.sources:\n        if not Path(source).exists():\n            errors.append(f\"Source not found: {source}\")\n\n    # Check for conflicting operations\n    destinations = [r.destination for r in plan.routes]\n    if len(destinations) != len(set(destinations)):\n        # Multiple chunks going to same file - need ordering\n        pass  # This is fine, handled by grouping\n\n    return errors\n```\n\n## Strategy Implementations\n\n### CREATE_NEW\n\n```python\ndef execute_create_new(route: Route) -> ExecutionResult:\n    \"\"\"Create a new file with content.\"\"\"\n\n    # validate directory exists\n    dest_path = Path(route.destination)\n    dest_path.parent.mkdir(parents=True, exist_ok=True)\n\n    # Generate content based on category\n    if route.chunk.category == 'decisions':\n        content = generate_adr_content(route)\n    elif route.chunk.category == 'actionable':\n        content = generate_plan_content(route)\n    else:\n        content = generate_doc_content(route)\n\n    # Write file\n    dest_path.write_text(content)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='created',\n        bytes_written=len(content),\n    )\n```\n\n### INTELLIGENT_WEAVE\n\n```python\ndef execute_intelligent_weave(route: Route) -> ExecutionResult:\n    \"\"\"Insert content into matching section of existing file.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Find matching section\n    section_pattern = find_matching_section(original, route.chunk.header)\n\n    if not section_pattern:\n        # Fall back to append\n        return execute_append_with_context(route)\n\n    # Analyze existing style\n    style = analyze_section_style(original, section_pattern)\n\n    # Format new content to match\n    formatted = format_to_match_style(route.chunk.content, style)\n\n    # Insert at appropriate location within section\n    updated = insert_in_section(original, section_pattern, formatted)\n\n    # Validate result\n    if not validate_markdown(updated):\n        raise ExecutionError(f\"Weave produced invalid markdown for {route.destination}\")\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='weaved',\n        section=section_pattern.header,\n        bytes_added=len(formatted),\n    )\n\ndef analyze_section_style(content: str, section: SectionMatch) -> Style:\n    \"\"\"Determine formatting style of existing section.\"\"\"\n    section_content = extract_section_content(content, section)\n\n    return Style(\n        uses_bullets=bool(re.search(r'^[-*]\\s', section_content, re.M)),\n        uses_numbers=bool(re.search(r'^\\d+\\.\\s', section_content, re.M)),\n        uses_tables=bool(re.search(r'^\\|.*\\|$', section_content, re.M)),\n        indent_style=detect_indent(section_content),\n        has_blank_lines='\\n\\n' in section_content,\n    )\n```\n\n### REPLACE_SECTION\n\n```python\ndef execute_replace_section(route: Route) -> ExecutionResult:\n    \"\"\"Replace entire section with new content.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Find section boundaries\n    section = find_section_boundaries(original, route.target_section)\n\n    if not section:\n        raise ExecutionError(f\"Section '{route.target_section}' not found in {route.destination}\")\n\n    # Log what we're replacing (for rollback if needed)\n    replaced_content = original[section.start:section.end]\n    log_replacement(route.destination, route.target_section, replaced_content)\n\n    # Build replacement with update marker\n    replacement = f\"{section.header}\\n\\n\"\n    replacement += f\"*Updated: {date.today().isoformat()} (consolidated from {route.source})*\\n\\n\"\n    replacement += route.chunk.content\n\n    # Replace in document\n    updated = original[:section.start] + replacement + original[section.end:]\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='replaced',\n        section=route.target_section,\n        bytes_before=len(replaced_content),\n        bytes_after=len(replacement),\n    )\n```\n\n### APPEND_WITH_CONTEXT\n\n```python\ndef execute_append_with_context(route: Route) -> ExecutionResult:\n    \"\"\"Add new section at end of document.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Determine section level (match document)\n    header_level = detect_header_level(original)\n\n    # Build new section\n    new_section = f\"\\n\\n{'#' * header_level} {route.chunk.header}\"\n    new_section += f\" (consolidated {date.today().isoformat()})\\n\\n\"\n    new_section += f\"*Source: {route.source}*\\n\\n\"\n    new_section += route.chunk.content\n\n    # Append\n    updated = original.rstrip() + new_section + \"\\n\"\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='appended',\n        section=route.chunk.header,\n        bytes_added=len(new_section),\n    )\n```\n\n## Source Deletion\n\nAfter all merges complete successfully:\n\n```python\ndef delete_sources(plan: ConsolidationPlan, results: list[ExecutionResult]) -> list[str]:\n    \"\"\"Delete source files after successful consolidation.\"\"\"\n\n    # Only delete if ALL operations succeeded\n    if any(r.status == 'failed' for r in results):\n        return []  # Don't delete anything\n\n    deleted = []\n    for source in plan.sources:\n        source_path = Path(source)\n        if source_path.exists():\n            source_path.unlink()\n            deleted.append(source)\n\n    return deleted\n```\n\n## Rollback Support\n\nMaintain log for potential rollback:\n\n```python\nCONSOLIDATION_LOG = '.consolidation-log.json'\n\ndef log_operation(operation: dict):\n    \"\"\"Log operation for potential rollback.\"\"\"\n    log_path = Path(CONSOLIDATION_LOG)\n\n    if log_path.exists():\n        log = json.loads(log_path.read_text())\n    else:\n        log = {'operations': [], 'timestamp': datetime.now().isoformat()}\n\n    log['operations'].append(operation)\n    log_path.write_text(json.dumps(log, indent=2))\n\ndef rollback_last():\n    \"\"\"Rollback most recent consolidation.\"\"\"\n    log_path = Path(CONSOLIDATION_LOG)\n    if not log_path.exists():\n        raise RollbackError(\"No consolidation log found\")\n\n    log = json.loads(log_path.read_text())\n\n    # Reverse operations\n    for op in reversed(log['operations']):\n        if op['action'] == 'created':\n            Path(op['destination']).unlink()\n        elif op['action'] == 'replaced':\n            restore_section(op['destination'], op['section'], op['original'])\n        elif op['action'] == 'deleted':\n            # Cannot restore deleted sources automatically\n            print(f\"WARNING: Cannot restore deleted source: {op['source']}\")\n\n    log_path.unlink()\n```\n\n## Execution Summary\n\nGenerate detailed summary:\n\n```markdown\n# Consolidation Complete\n\n**Timestamp**: 2025-12-06T14:32:15\n**Duration**: 2.3s\n\n## Created Files (3)\n\n| File | Size | Category |\n|------|------|----------|\n| docs/api-overview.md | 1,847 bytes | findings |\n| docs/plans/2025-12-06-api-consistency.md | 1,456 bytes | actionable |\n| docs/adr/0002-2025-12-06-cli-naming.md | 634 bytes | decisions |\n\n## Updated Files (1)\n\n| File | Section | Strategy | Change |\n|------|---------|----------|--------|\n| docs/architecture.md | Consistency | APPEND | +892 bytes |\n\n## Deleted Sources (1)\n\n- ~~API_REVIEW_REPORT.md~~ (deleted)\n\n## Verification Checklist\n\n- [ ] Review created files for accuracy\n- [ ] Check weaved content fits naturally\n- [ ] Run documentation build (if applicable)\n- [ ] Commit changes\n\n**Suggested commit message:**\n```\ndocs: consolidate API review findings\n\n- Created api-overview.md with plugin API inventory\n- Created plan for API consistency improvements\n- Added ADR for CLI naming convention\n- Updated architecture.md with consistency findings\n\nConsolidated from: API_REVIEW_REPORT.md\n```\n```\n\n## Error Handling\n\n```python\nclass ExecutionError(Exception):\n    \"\"\"Error during merge execution.\"\"\"\n    pass\n\ndef execute_with_recovery(plan: ConsolidationPlan) -> ExecutionSummary:\n    \"\"\"Execute plan with error recovery.\"\"\"\n    results = []\n\n    try:\n        # Execute creates\n        for route in plan.creates:\n            result = execute_create_new(route)\n            log_operation(result.to_dict())\n            results.append(result)\n\n        # Execute updates\n        for route in plan.updates:\n            if route.strategy == 'INTELLIGENT_WEAVE':\n                result = execute_intelligent_weave(route)\n            elif route.strategy == 'REPLACE_SECTION':\n                result = execute_replace_section(route)\n            else:\n                result = execute_append_with_context(route)\n\n            log_operation(result.to_dict())\n            results.append(result)\n\n        # Delete sources\n        deleted = delete_sources(plan, results)\n        for source in deleted:\n            log_operation({'action': 'deleted', 'source': source})\n\n        return ExecutionSummary(results=results, deleted=deleted, status='success')\n\n    except Exception as e:\n        # Log failure but don't auto-rollback\n        return ExecutionSummary(\n            results=results,\n            status='partial_failure',\n            error=str(e),\n            message=\"Some operations failed. Use rollback if needed.\"\n        )\n```\n\nFile v1.9.19:skill-card.md\n\n## Description:\n\nMerges ephemeral report and analysis artifacts into permanent documentation.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[athola](https://clawhub.ai/user/athola)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and documentation maintainers use this skill to identify temporary LLM-generated markdown reports, extract durable findings or action items, route them into permanent documentation, and remove approved source reports after consolidation.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Consolidated content can move inaccurate or low-value LLM-generated analysis into permanent documentation.\n\nMitigation: Review the consolidation plan and destination mapping before execution, and check generated or updated docs before committing.\n\nRisk: Source markdown reports are removed permanently after successful consolidation.\n\nMitigation: Review the deletion list before approving execution and use version control or backups when source reports must be retained.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/athola/skills/nm-sanctum-doc-consolidation)\n- [Project homepage from ClawHub metadata](https://github.com/athola/claude-night-market/tree/master/plugins/sanctum)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance, files]\n\n**Output Format:** [Markdown guidance with plans, tables, code blocks, and file-change summaries]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May create or update documentation files and may delete approved source report files after consolidation.]\n\n## Skill Version(s):\n\n1.9.19 (source: server release metadata; artifact frontmatter reports 1.9.8)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.9.17: 7 files, 16514 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), skill-card.md (2158b), SKILL.md (9098b), _meta.json (148b)\n\nFile v1.9.17:SKILL.md\n\n---\nname: doc-consolidation\ndescription: Merges ephemeral report and analysis artifacts into permanent documentation\nversion: 1.9.8\ntriggers:\n  - docs\n  - consolidation\n  - cleanup\n  - git-hygiene\n  - knowledge-management\n  - LLM-generated markdown files have accumulated and need consolidation\nmetadata: {\"openclaw\": {\"homepage\": \"https://github.com/athola/claude-night-market/tree/master/plugins/sanctum\", \"emoji\": \"\\ud83d\\udcdd\"}}\nsource: claude-night-market\nsource_plugin: sanctum\n---\n\n> **Night Market Skill** — ported from [claude-night-market/sanctum](https://github.com/athola/claude-night-market/tree/master/plugins/sanctum). For the full experience with agents, hooks, and commands, install the Claude Code plugin.\n\n\n## Table of Contents\n\n- [When to Use](#when-to-use)\n- [Quick Start](#quick-start)\n- [Two-Phase Workflow](#two-phase-workflow)\n- [Phase 1: Triage (Fast Model)](#phase-1:-triage-(fast-model))\n- [Phase 2: Execute (Main Model)](#phase-2:-execute-(main-model))\n- [Workflow Details](#workflow-details)\n- [Step 1: Candidate Detection](#step-1:-candidate-detection)\n- [Step 2: Content Analysis](#step-2:-content-analysis)\n- [Step 3: Destination Routing](#step-3:-destination-routing)\n- [Step 4: Generate Plan](#step-4:-generate-plan)\n- [Source: API_REVIEW_REPORT.md](#source:-api_review_reportmd)\n- [Post-Consolidation](#post-consolidation)\n- [Step 5: Execute Merges](#step-5:-execute-merges)\n- [Fast Model Delegation](#fast-model-delegation)\n- [Content Categories](#content-categories)\n- [Merge Strategies](#merge-strategies)\n- [Intelligent Weave](#intelligent-weave)\n- [Replace Section](#replace-section)\n- [Append with Context](#append-with-context)\n- [Create New File](#create-new-file)\n- [Integration](#integration)\n- [Example Session](#example-session)\n- [Troubleshooting](#troubleshooting)\n- [No candidates found](#no-candidates-found)\n- [Low-quality extractions](#low-quality-extractions)\n- [Merge conflicts](#merge-conflicts)\n- [Related Skills](#related-skills)\n\n\n# Doc Consolidation\n\nExtracts valuable knowledge from ephemeral LLM outputs and merges it into permanent documentation.\n\n## When To Use\n\nUse this skill when:\n- You have untracked `*_REPORT.md` or `*_ANALYSIS.md` files from Claude sessions\n- Git status shows markdown files that shouldn't be committed but contain useful content\n- You want to preserve insights from code reviews, refactoring reports, or API audits\n- Preparing a PR and need to clean up working artifacts\n\nDo NOT use when:\n- Files are already in proper documentation locations\n  (`docs/`, `skills/`)\n- Files are intentionally temporary scratch notes\n- User explicitly wants to preserve the original report format\n- Source files have no extractable value (pure log output)\n\n## Formatting\n\nWhen merging content into permanent documentation, follow\n`Skill(leyline:markdown-formatting)` conventions: wrap prose\nat 80 chars (prefer sentence/clause boundaries), blank lines\naround headings, ATX headings only, blank line before lists,\nand reference-style links for long URLs.\n\n## Quick Start\n\n```\n/consolidate-docs\n```\n\nOr invoke directly:\n\n```\nI have some report files that need consolidating into permanent docs.\n```\n\n## Two-Phase Workflow\n\n### Phase 1: Triage (Fast Model)\n\nRead-only analysis to generate a consolidation plan:\n\n1. **Detect candidates** - Find untracked markdown files with LLM output markers\n2. **Analyze content** - Extract and categorize valuable sections\n3. **Route destinations** - Match content to existing docs or propose new files\n4. **Present plan** - Show user what will be consolidated and where\n\n**Checkpoint**: User reviews and approves plan before execution.\n\n### Phase 2: Execute (Main Model)\n\nAfter approval, performs the consolidation:\n\n1. **Merge content** - Weave into existing docs or create new files\n2. **Delete sources** - Remove ephemeral files after successful merge\n3. **Generate summary** - Report what was created/updated/deleted\n\n## Workflow Details\n\n### Step 1: Candidate Detection\n\nLoad: `@modules/candidate-detection.md`\n\nIdentifies files using:\n- Git status (untracked `.md` files)\n- Location (not in standard doc directories)\n- Naming (ALL_CAPS non-standard names)\n- Content markers (Executive Summary, Findings, Action Items)\n\n### Step 2: Content Analysis\n\nLoad: `@modules/content-analysis.md`\n\nFor each candidate:\n- Extract sections as content chunks\n- Categorize: Actionable Items, Decisions, Findings, Metrics, Migration Guides, API Changes\n- Score value: high/medium/low\n\n### Step 3: Destination Routing\n\nLoad: `@modules/destination-routing.md`\n\nFor each valuable chunk:\n- Semantic match against existing documentation\n- Apply default mappings if no good match\n- Determine merge strategy (weave, replace, append, create)\n\n### Step 4: Generate Plan\n\nPresent consolidation plan to user:\n\n```markdown\n# Consolidation Plan\n\n## Source: API_REVIEW_REPORT.md\n\n| Content | Category | Value | Destination | Action |\n|---------|----------|-------|-------------|--------|\n| API inventory | Findings | High | docs/api-overview.md | Create |\n| Action items | Actionable | High | docs/plans/2025-12-06-api.md | Create |\n\n### Post-Consolidation\n- Delete: API_REVIEW_REPORT.md\n\nProceed with consolidation? [Y/n]\n```\n\n### Step 5: Execute Merges\n\nLoad: `@modules/merge-execution.md`\n\nAfter user approval:\n- Group operations by destination file\n- Apply merge strategies\n- Validate results (frontmatter intact, structure preserved)\n- Delete source files\n- Generate execution summary\n\n## Fast Model Delegation\n\nPhase 1 tasks are delegated to haiku-class models for efficiency:\n\n```python\n# plugins/sanctum/scripts/consolidation_planner.py handles:\n- scan_for_candidates()\n- extract_content_chunks()\n- categorize_chunks()\n- score_value()\n- find_semantic_matches()\n```\n\nPhase 2 stays on the main model for careful merge execution.\n\n## Content Categories\n\n| Category | Description | Default Destination |\n|----------|-------------|---------------------|\n| Actionable Items | Tasks, TODOs, next steps | `docs/plans/YYYY-MM-DD-{topic}.md` |\n| Decisions Made | Architecture choices | `docs/adr/NNNN-{date}-{topic}.md` |\n| Findings/Insights | Audit results, analysis | Best-match existing doc |\n| Metrics/Baselines | Before/after comparisons | `docs/benchmarks/` |\n| Migration Guides | Step-by-step procedures | `docs/migration-guide.md` |\n| API Changes | Breaking changes, deprecations | CHANGELOG or api docs |\n\n## Merge Strategies\n\n### Intelligent Weave\nInsert content into matching existing sections, preserving style.\n\n  Use when: Destination has relevant section, content is additive.\n\n### Replace Section\nReplace existing section with more detailed new content.\n\n  Use when: New content 2x+ more detailed or has later date.\n\n### Append with Context\nAdd new section with date and source reference.\n\n  Use when: No matching section, content doesn't fit existing structure.\n\n### Create New File\nGenerate complete new document.\n\n  Use when: No suitable destination exists, content warrants standalone doc.\n\n## Integration\n\nWorks with other sanctum skills:\n\n- **git-workspace-review**: May suggest consolidation when untracked reports detected\n- **pr-prep**: Warns about untracked analysis files before PR creation\n- **doc-updates**: Can invoke for final polish after consolidation\n\n## Example Session\n\n```\nUser: I have API_REVIEW_REPORT.md from our review session. Can you consolidate it?\n\nClaude: I'll consolidate the report into permanent documentation.\n\n**Phase 1: Triage**\n\nScanning for candidates...\n- Found: API_REVIEW_REPORT.md (untracked, LLM markers detected)\n\nAnalyzing content...\n- Extracted 6 content chunks\n- High value: API inventory, action items, CLI recommendation\n- Medium value: security assessment\n- Low value: executive summary (generic)\n\nRouting destinations...\n- API inventory → docs/api-overview.md (CREATE)\n- Action items → docs/plans/2025-12-06-api-consistency.md (CREATE)\n- CLI naming → docs/adr/0002-cli-naming.md (CREATE ADR)\n- Security → Already covered in docs/security.md (SKIP)\n\n**Consolidation Plan**\n[table shown]\n\nProceed? [Y/n]\n\nUser: Y\n\nClaude: **Phase 2: Executing**\n\nCreated: docs/api-overview.md (1,247 bytes)\nCreated: docs/plans/2025-12-06-api-consistency.md (892 bytes)\nCreated: docs/adr/0002-2025-12-06-cli-naming.md (634 bytes)\nDeleted: API_REVIEW_REPORT.md\n\nConsolidation complete. Review the created files and commit when ready.\n```\n\n## Troubleshooting\n\n### No candidates found\n- Check git status for untracked files\n- Files may already be in standard locations\n- Try explicit file path: \"Consolidate API_REVIEW_REPORT.md\"\n\n### Low-quality extractions\n- Source file may lack structured sections\n- Content may be too generic to categorize\n- Try manual extraction for unstructured reports\n\n### Merge conflicts\n- Destination file structure changed\n- Try APPEND strategy instead of WEAVE\n- Manual intervention may be needed\n\n## Related Skills\n\n- `sanctum:doc-updates` - General documentation updates\n- `sanctum:git-workspace-review` - Pre-flight workspace analysis\n- `sanctum:pr-prep` - Pull request preparation\n- `imbue:catchup` - Understanding recent changes\n\nFile v1.9.17:_meta.json\n\n{\n  \"ownerId\": \"kn7d107jg9jv602h9ytsegydq184a42s\",\n  \"slug\": \"nm-sanctum-doc-consolidation\",\n  \"version\": \"1.9.17\",\n  \"publishedAt\": 1785390014609\n}\n\nFile v1.9.17:modules/candidate-detection.md\n\n# Candidate Detection Module\n\nIdentifies markdown files that are candidates for consolidation.\n\n## Detection Signals\n\nApply signals in priority order. A file is a candidate if it matches **any** signal.\n\n### Signal 1: Git-Untracked Location (Highest Priority)\n\n```bash\n# Find untracked .md files\ngit status --porcelain | grep '^??' | grep '\\.md$'\n```\n\n**Exclude standard locations:**\n- `docs/` - Already permanent documentation\n- `skills/` - Skill definitions\n- `modules/` - Skill modules\n- `commands/` - Slash commands\n- `agents/` - Agent definitions\n- `.github/` - GitHub templates\n\n**Exclude standard names:**\n- `README.md`, `README`\n- `LICENSE.md`, `LICENSE`\n- `CONTRIBUTING.md`\n- `CHANGELOG.md`, `HISTORY.md`\n- `SECURITY.md`\n- `CODE_OF_CONDUCT.md`\n\n### Signal 2: ALL_CAPS Naming Pattern\n\nFiles with ALL_CAPS names that aren't standard conventions:\n\n```\nMATCHES (candidates):\n- API_REVIEW_REPORT.md\n- REFACTORING_REPORT.md\n- MIGRATION_ANALYSIS.md\n- AUDIT_FINDINGS.md\n- *_REPORT.md\n- *_ANALYSIS.md\n- *_REVIEW.md\n- *_FINDINGS.md\n\nEXCLUDES (not candidates):\n- README.md\n- LICENSE.md\n- CONTRIBUTING.md\n- CHANGELOG.md\n- SECURITY.md\n- CODE_OF_CONDUCT.md\n```\n\n### Signal 3: Content Markers\n\nScan first 100 lines for LLM output markers:\n\n**Strong markers (any one = candidate):**\n- `**Date**:` or `Date:` at start of line\n- `## Executive Summary`\n- `## Summary` (at document start)\n- `## Findings`\n- `## Action Items`\n- `## Recommendations`\n- `## Conclusion`\n\n**Supporting markers (need 2+ to qualify):**\n- Markdown tables with `|` columns\n- `### High Priority` / `### Medium Priority` / `### Low Priority`\n- `- [ ]` checkbox lists\n- `## 1.` numbered top-level sections\n- `**Scope**:` or `**Status**:`\n- Lines starting with status markers\n\n## Detection Algorithm\n\n```python\ndef detect_candidates(repo_path: str) -> list[CandidateFile]:\n    candidates = []\n\n    # Get untracked markdown files\n    untracked = git_untracked_md_files(repo_path)\n\n    for file_path in untracked:\n        # Skip standard locations\n        if is_standard_location(file_path):\n            continue\n\n        # Skip standard names\n        if is_standard_name(file_path):\n            continue\n\n        score = 0\n        reasons = []\n\n        # Check naming pattern\n        if is_allcaps_nonstandard(file_path):\n            score += 3\n            reasons.append(\"ALL_CAPS non-standard name\")\n\n        # Check content markers\n        content = read_first_n_lines(file_path, 100)\n        strong, supporting = count_content_markers(content)\n\n        if strong > 0:\n            score += 3\n            reasons.append(f\"Strong markers: {strong}\")\n\n        if supporting >= 2:\n            score += 2\n            reasons.append(f\"Supporting markers: {supporting}\")\n\n        # Threshold: score >= 2\n        if score >= 2:\n            candidates.append(CandidateFile(\n                path=file_path,\n                score=score,\n                reasons=reasons\n            ))\n\n    return sorted(candidates, key=lambda c: c.score, reverse=True)\n```\n\n## Output Format\n\n```markdown\n## Detected Candidates\n\n| File | Score | Reasons |\n|------|-------|---------|\n| API_REVIEW_REPORT.md | 6 | ALL_CAPS name, Strong markers: 3, Supporting: 4 |\n| REFACTORING_REPORT.md | 5 | ALL_CAPS name, Strong markers: 2 |\n| analysis-notes.md | 2 | Supporting markers: 3 |\n\nProceeding with 3 candidates...\n```\n\n## Edge Cases\n\n### Nested untracked directories\nIf an entire directory is untracked, scan all `.md` files within:\n```bash\ngit status --porcelain | grep '^??' | while read status path; do\n  if [ -d \"$path\" ]; then\n    find \"$path\" -name \"*.md\"\n  fi\ndone\n```\n\n### Partially staged files\nFiles that are partially staged (`MM` or `AM` status) should be flagged for user attention - they may contain mixed committed/uncommitted content.\n\n### Renamed/moved files\nIf git shows a rename (`R` status), check if the destination is a standard location. If moving TO a standard location, not a candidate.\n\n## Validation\n\nBefore proceeding, confirm candidates with user:\n\n```markdown\nFound 3 consolidation candidates:\n\n1. **API_REVIEW_REPORT.md** (score: 6)\n   - ALL_CAPS non-standard name\n   - Contains: Executive Summary, Findings, Action Items\n\n2. **REFACTORING_REPORT.md** (score: 5)\n   - ALL_CAPS non-standard name\n   - Contains: Summary, Conclusion\n\n3. **analysis-notes.md** (score: 2)\n   - Contains: Multiple tables, checkbox lists\n\nAnalyze these files for consolidation? [Y/n/select specific]\n```\n\nFile v1.9.17:modules/content-analysis.md\n\n# Content Analysis Module\n\nExtracts and categorizes valuable content from candidate files.\n\n## Content Categories\n\n### Category Definitions\n\n| Category | Description | Indicators |\n|----------|-------------|------------|\n| **Actionable Items** | Tasks, TODOs, next steps that require action | `Action Items`, `Next Steps`, `TODO`, `- [ ]` checkboxes |\n| **Decisions Made** | Architecture choices, tradeoffs, rationale | `Decision`, `Chose`, `Tradeoff`, `Rationale`, `Why we` |\n| **Findings/Insights** | Audit results, analysis conclusions, observations | `Findings`, `Observations`, `Analysis`, `Discovered`, `Noted` |\n| **Metrics/Baselines** | Quantitative data, before/after, benchmarks | Tables with numbers, `Before`, `After`, percentages, `Improvement` |\n| **Migration Guides** | Step-by-step procedures, how-to instructions | `Steps`, `How to`, `Migration`, numbered lists with commands |\n| **API Changes** | Interface modifications, breaking changes, deprecations | `API`, `Breaking`, `Deprecated`, `New endpoint`, `Removed` |\n\n### Extraction Process\n\nFor each candidate file:\n\n1. **Parse structure** - Identify sections by headers (`##`, `###`)\n2. **Extract chunks** - Each section becomes a content chunk\n3. **Categorize** - Match chunk to best-fit category\n4. **Score value** - Assess high/medium/low\n\n## Value Scoring\n\n### High Value\nContent that is:\n- **Specific**: Contains concrete names, paths, numbers\n- **Actionable**: Reader can act on it directly\n- **Unique**: Not already documented elsewhere\n\nExamples:\n- Specific action items with owners\n- Concrete metrics (before: 287 lines, after: 255 lines)\n- Explicit decisions with rationale\n- Step-by-step procedures that worked\n\n### Medium Value\nContent that is:\n- **Somewhat specific**: General guidance with some detail\n- **Reference-worthy**: Useful for future lookups\n- **Partially covered**: Extends existing documentation\n\nExamples:\n- General recommendations without specifics\n- Findings that align with existing docs\n- Metrics without clear baseline comparison\n\n### Low Value\nContent that is:\n- **Generic**: Could apply to any project\n- **Redundant**: Already well-documented elsewhere\n- **Ephemeral**: Only relevant to the moment\n\nExamples:\n- Executive summaries (usually boilerplate)\n- Generic best practice reminders\n- Status statements (\"The review is complete\")\n\n## Chunk Extraction Algorithm\n\n```python\ndef extract_chunks(content: str) -> list[ContentChunk]:\n    chunks = []\n    current_section = None\n    current_content = []\n\n    for line in content.split('\\n'):\n        # New section header\n        if line.startswith('## '):\n            if current_section:\n                chunks.append(make_chunk(current_section, current_content))\n            current_section = line[3:].strip()\n            current_content = []\n        elif line.startswith('### '):\n            # Subsection - append to current or create new\n            if current_section:\n                current_content.append(line)\n            else:\n                current_section = line[4:].strip()\n                current_content = []\n        else:\n            current_content.append(line)\n\n    # Don't forget last section\n    if current_section:\n        chunks.append(make_chunk(current_section, current_content))\n\n    return chunks\n\ndef make_chunk(header: str, content: list[str]) -> ContentChunk:\n    text = '\\n'.join(content).strip()\n    category = categorize(header, text)\n    value = score_value(text, category)\n\n    return ContentChunk(\n        header=header,\n        content=text,\n        category=category,\n        value=value\n    )\n```\n\n## Categorization Rules\n\nMatch in order (first match wins):\n\n```python\nCATEGORY_PATTERNS = {\n    'actionable': [\n        r'action\\s*items?',\n        r'next\\s*steps?',\n        r'todo',\n        r'tasks?',\n        r'- \\[ \\]',  # Unchecked checkboxes\n    ],\n    'decisions': [\n        r'decision',\n        r'chose|chosen',\n        r'tradeoff',\n        r'rationale',\n        r'why\\s+we',\n        r'approach',\n    ],\n    'findings': [\n        r'finding',\n        r'observation',\n        r'analysis',\n        r'discovered',\n        r'audit',\n        r'review\\s+result',\n    ],\n    'metrics': [\n        r'\\d+%',\n        r'before.*after',\n        r'improvement',\n        r'reduction',\n        r'benchmark',\n        r'\\|\\s*\\d+\\s*\\|',  # Table with numbers\n    ],\n    'migration': [\n        r'migration',\n        r'step\\s*\\d',\n        r'how\\s+to',\n        r'procedure',\n        r'```bash',  # Code blocks with commands\n    ],\n    'api_changes': [\n        r'api',\n        r'breaking\\s+change',\n        r'deprecat',\n        r'endpoint',\n        r'interface',\n    ],\n}\n```\n\n## Output Format\n\n```markdown\n## Content Analysis: API_REVIEW_REPORT.md\n\n### Extracted Chunks\n\n| # | Section | Category | Value | Size |\n|---|---------|----------|-------|------|\n| 1 | Executive Summary | findings | low | 234 chars |\n| 2 | API Surface Inventory | findings | high | 1,847 chars |\n| 3 | Consistency Audit Findings | findings | high | 2,103 chars |\n| 4 | Action Items | actionable | high | 1,456 chars |\n| 5 | Recommendations | actionable | medium | 892 chars |\n| 6 | Conclusion | findings | low | 312 chars |\n\n### High-Value Content (4 chunks)\n- API Surface Inventory: Detailed plugin API table\n- Consistency Audit Findings: Specific issues with examples\n- Action Items: Concrete tasks with priorities\n- (included in routing)\n\n### Excluded (Low Value)\n- Executive Summary: Generic overview\n- Conclusion: Status statement only\n```\n\n## Special Handling\n\n### Tables\nPreserve markdown tables intact - they often contain valuable structured data:\n```python\ndef is_table(lines: list[str]) -> bool:\n    return any('|' in line and line.count('|') >= 2 for line in lines)\n```\n\n### Code Blocks\nPreserve code blocks, especially those showing:\n- Configuration examples\n- Command sequences\n- Before/after code comparisons\n\n### Checklists\nPreserve checkbox lists - they indicate actionable items:\n```markdown\n- [x] Completed item (historical record)\n- [ ] Pending item (action needed)\n```\n\n### Cross-references\nNote internal references for destination routing:\n```python\n# Links like \"See also: docs/security.md\" suggest destinations\nREFERENCE_PATTERN = r'see\\s+(?:also:?\\s*)?([^\\s,]+\\.md)'\n```\n\nFile v1.9.17:modules/destination-routing.md\n\n# Destination Routing Module\n\nMaps extracted content chunks to appropriate destinations in the documentation.\n\n## Routing Strategy\n\n### Priority Order\n\n1. **Semantic match** - Find existing doc that covers the topic\n2. **Default mapping**\n - Use category-based default destinations\n3. **Create new** - Only when no suitable destination exists\n\n### Preference: Existing Over New\n\nAlways prefer merging into existing documentation:\n- Keeps documentation consolidated\n- Avoids duplicate coverage\n- Maintains established structure\n\nCreate new files only when:\n- Content is substantial (>500 chars of high-value)\n- No existing doc covers the topic\n- Content warrants standalone treatment\n\n## Semantic Matching\n\n### Algorithm\n\n```python\ndef find_semantic_match(chunk: ContentChunk, existing_docs: list[str]) -> str | None:\n    \"\"\"Find best-matching existing document for a content chunk.\"\"\"\n\n    best_match = None\n    best_score = 0\n\n    for doc_path in existing_docs:\n        doc_content = read_file(doc_path)\n        score = compute_relevance(chunk, doc_content)\n\n        if score > best_score and score >= MATCH_THRESHOLD:\n            best_match = doc_path\n            best_score = score\n\n    return best_match\n\ndef compute_relevance(chunk: ContentChunk, doc_content: str) -> float:\n    \"\"\"Score relevance of chunk to document.\"\"\"\n    score = 0.0\n\n    # Header matching (highest weight)\n    doc_headers = extract_headers(doc_content)\n    if any(similar(chunk.header, h) for h in doc_headers):\n        score += 0.4\n\n    # Keyword overlap\n    chunk_keywords = extract_keywords(chunk.content)\n    doc_keywords = extract_keywords(doc_content)\n    overlap = len(chunk_keywords & doc_keywords) / len(chunk_keywords)\n    score += overlap * 0.3\n\n    # Category alignment\n    if doc_likely_category(doc_content) == chunk.category:\n        score += 0.2\n\n    # Reference mentions\n    if chunk mentions doc_path or doc mentions chunk source:\n        score += 0.1\n\n    return score\n\nMATCH_THRESHOLD = 0.5  # Minimum score to consider a match\n```\n\n### Existing Doc Discovery\n\nScan these locations for potential destinations:\n\n```python\nDOC_LOCATIONS = [\n    'docs/',\n    'docs/plans/',\n    'docs/adr/',\n    'README.md',\n    'CHANGELOG.md',\n]\n\n# Plugin-specific locations\nPLUGIN_DOC_LOCATIONS = [\n    '{plugin}/docs/',\n    '{plugin}/README.md',\n]\n```\n\n## Default Mappings\n\nWhen semantic matching finds no suitable destination:\n\n| Category | Default Destination | Notes |\n|----------|---------------------|-------|\n| Actionable Items | `docs/plans/YYYY-MM-DD-{topic}.md` | New plan file |\n| Decisions Made | `docs/adr/NNNN-YYYY-MM-DD-{topic}.md` | New ADR |\n| Findings/Insights | `docs/{topic}.md` | New doc or best-effort match |\n| Metrics/Baselines | `docs/benchmarks.md` or inline | Append if exists |\n| Migration Guides | `docs/migration-guide.md` | Append section |\n| API Changes | `CHANGELOG.md` or `docs/api.md` | Prefer CHANGELOG |\n\n### Topic Extraction\n\nDerive topic slug from content:\n\n```python\ndef extract_topic(chunk: ContentChunk, source_file: str) -> str:\n    \"\"\"Extract topic slug for file naming.\"\"\"\n\n    # Try chunk header first\n    if chunk.header:\n        return slugify(chunk.header)\n\n    # Try source file name\n    source_name = Path(source_file).stem\n    if '_REPORT' in source_name:\n        return slugify(source_name.replace('_REPORT', ''))\n\n    # Fall back to category\n    return chunk.category\n\ndef slugify(text: str) -> str:\n    \"\"\"Convert text to kebab-case slug.\"\"\"\n    text = text.lower()\n    text = re.sub(r'[^a-z0-9]+', '-', text)\n    text = text.strip('-')\n    return text[:50]  # Max length\n```\n\n## Merge Strategy Selection\n\nFor each chunk-destination pair, determine how to merge:\n\n### Decision Tree\n\n```\nIs destination a new file?\n├── Yes → CREATE_NEW\n└── No → Does destination have matching section?\n    ├── Yes → Is new content more detailed?\n    │   ├── Yes (2x+ detail OR newer date) → REPLACE_SECTION\n    │   └── No → INTELLIGENT_WEAVE\n    └── No → APPEND_WITH_CONTEXT\n```\n\n### Strategy Definitions\n\n**CREATE_NEW**\n- Generate complete new file with frontmatter\n- Use appropriate template for category\n- Include source attribution\n\n**INTELLIGENT_WEAVE**\n- Find matching section in destination\n- Insert content matching existing style\n- Preserve bullet/table/prose formatting\n\n**REPLACE_SECTION**\n- Back up existing content (in consolidation log)\n- Replace entire section\n- Add \"Updated: YYYY-MM-DD\" marker\n\n**APPEND_WITH_CONTEXT**\n- Add new section at logical location\n- Include header with date and source\n- Format: `## {Topic} (consolidated from {source}, {date})`\n\n## Output Format\n\n```markdown\n## Routing Plan\n\n### Source: API_REVIEW_REPORT.md\n\n| Chunk | Destination | Strategy | Rationale |\n|-------|-------------|----------|-----------|\n| API Surface Inventory | docs/api-overview.md | CREATE_NEW | No existing API docs, substantial content |\n| Consistency Findings | docs/architecture.md | APPEND_WITH_CONTEXT | Related topic, no matching section |\n| Action Items | docs/plans/2025-12-06-api-consistency.md | CREATE_NEW | Actionable, needs tracking |\n| CLI Recommendation | docs/adr/0002-cli-naming.md | CREATE_NEW | Decision warrants ADR |\n\n### Skipped (Low Value)\n| Chunk | Reason |\n|-------|--------|\n| Executive Summary | Generic, low value |\n| Conclusion | Status only, no lasting value |\n\n### Destination Summary\n- **New files**: 3\n- **Updates to existing**: 1\n- **Skipped**: 2\n```\n\n## ADR Generation\n\nFor decisions that warrant an ADR:\n\n```markdown\n# ADR-{NNNN}: {Decision Title}\n\n**Date**: {YYYY-MM-DD}\n**Status**: Accepted\n**Consolidated from**: {source_file}\n\n## Context\n\n{extracted context from source}\n\n## Decision\n\n{extracted decision}\n\n## Consequences\n\n{extracted consequences or \"To be determined\"}\n\n## References\n\n- Source: {source_file} (consolidated {date})\n```\n\n### ADR Numbering\n\n```python\ndef next_adr_number(adr_dir: str) -> int:\n    \"\"\"Find next available ADR number.\"\"\"\n    existing = glob(f\"{adr_dir}/[0-9][0-9][0-9][0-9]-*.md\")\n    if not existing:\n        return 1\n    numbers = [int(Path(p).name[:4]) for p in existing]\n    return max(numbers) + 1\n```\n\n## Validation\n\nBefore finalizing routing:\n\n1. **Check destination exists** (for updates)\n2. **Check write permissions**\n3. **Verify no circular references**\n4. **Confirm ADR numbering is unique**\n\n```python\ndef validate_routing(plan: RoutingPlan) -> list[str]:\n    \"\"\"Return list of validation errors.\"\"\"\n    errors = []\n\n    for route in plan.routes:\n        if route.strategy != 'CREATE_NEW':\n            if not Path(route.destination).exists():\n                errors.append(f\"Destination not found: {route.destination}\")\n\n        if route.strategy == 'CREATE_NEW':\n            if Path(route.destination).exists():\n                errors.append(f\"Would overwrite existing: {route.destination}\")\n\n    return errors\n```\n\nFile v1.9.17:modules/merge-execution.md\n\n# Merge Execution Module\n\nExecutes the approved consolidation plan, performing actual file operations.\n\n## Execution Order\n\n1. **Group by destination** - Minimize file I/O\n2. **Process creates first** - New files before updates\n3. **Process updates** - Apply merges to existing files\n4. **Delete sources** - Remove after successful consolidation\n5. **Generate summary** - Report all changes\n\n## Pre-Execution Checks\n\nBefore any file operations:\n\n```python\ndef pre_execution_checks(plan: ConsolidationPlan) -> list[str]:\n    \"\"\"Validate plan is safe to execute. Returns errors.\"\"\"\n    errors = []\n\n    # Check all destinations are writable\n    for route in plan.routes:\n        dest_dir = Path(route.destination).parent\n        if not dest_dir.exists():\n            # Will create - check parent is writable\n            if not os.access(dest_dir.parent, os.W_OK):\n                errors.append(f\"Cannot create directory: {dest_dir}\")\n        elif not os.access(route.destination, os.W_OK):\n            errors.append(f\"Cannot write to: {route.destination}\")\n\n    # Check sources exist and are readable\n    for source in plan.sources:\n        if not Path(source).exists():\n            errors.append(f\"Source not found: {source}\")\n\n    # Check for conflicting operations\n    destinations = [r.destination for r in plan.routes]\n    if len(destinations) != len(set(destinations)):\n        # Multiple chunks going to same file - need ordering\n        pass  # This is fine, handled by grouping\n\n    return errors\n```\n\n## Strategy Implementations\n\n### CREATE_NEW\n\n```python\ndef execute_create_new(route: Route) -> ExecutionResult:\n    \"\"\"Create a new file with content.\"\"\"\n\n    # validate directory exists\n    dest_path = Path(route.destination)\n    dest_path.parent.mkdir(parents=True, exist_ok=True)\n\n    # Generate content based on category\n    if route.chunk.category == 'decisions':\n        content = generate_adr_content(route)\n    elif route.chunk.category == 'actionable':\n        content = generate_plan_content(route)\n    else:\n        content = generate_doc_content(route)\n\n    # Write file\n    dest_path.write_text(content)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='created',\n        bytes_written=len(content),\n    )\n```\n\n### INTELLIGENT_WEAVE\n\n```python\ndef execute_intelligent_weave(route: Route) -> ExecutionResult:\n    \"\"\"Insert content into matching section of existing file.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Find matching section\n    section_pattern = find_matching_section(original, route.chunk.header)\n\n    if not section_pattern:\n        # Fall back to append\n        return execute_append_with_context(route)\n\n    # Analyze existing style\n    style = analyze_section_style(original, section_pattern)\n\n    # Format new content to match\n    formatted = format_to_match_style(route.chunk.content, style)\n\n    # Insert at appropriate location within section\n    updated = insert_in_section(original, section_pattern, formatted)\n\n    # Validate result\n    if not validate_markdown(updated):\n        raise ExecutionError(f\"Weave produced invalid markdown for {route.destination}\")\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='weaved',\n        section=section_pattern.header,\n        bytes_added=len(formatted),\n    )\n\ndef analyze_section_style(content: str, section: SectionMatch) -> Style:\n    \"\"\"Determine formatting style of existing section.\"\"\"\n    section_content = extract_section_content(content, section)\n\n    return Style(\n        uses_bullets=bool(re.search(r'^[-*]\\s', section_content, re.M)),\n        uses_numbers=bool(re.search(r'^\\d+\\.\\s', section_content, re.M)),\n        uses_tables=bool(re.search(r'^\\|.*\\|$', section_content, re.M)),\n        indent_style=detect_indent(section_content),\n        has_blank_lines='\\n\\n' in section_content,\n    )\n```\n\n### REPLACE_SECTION\n\n```python\ndef execute_replace_section(route: Route) -> ExecutionResult:\n    \"\"\"Replace entire section with new content.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Find section boundaries\n    section = find_section_boundaries(original, route.target_section)\n\n    if not section:\n        raise ExecutionError(f\"Section '{route.target_section}' not found in {route.destination}\")\n\n    # Log what we're replacing (for rollback if needed)\n    replaced_content = original[section.start:section.end]\n    log_replacement(route.destination, route.target_section, replaced_content)\n\n    # Build replacement with update marker\n    replacement = f\"{section.header}\\n\\n\"\n    replacement += f\"*Updated: {date.today().isoformat()} (consolidated from {route.source})*\\n\\n\"\n    replacement += route.chunk.content\n\n    # Replace in document\n    updated = original[:section.start] + replacement + original[section.end:]\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='replaced',\n        section=route.target_section,\n        bytes_before=len(replaced_content),\n        bytes_after=len(replacement),\n    )\n```\n\n### APPEND_WITH_CONTEXT\n\n```python\ndef execute_append_with_context(route: Route) -> ExecutionResult:\n    \"\"\"Add new section at end of document.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Determine section level (match document)\n    header_level = detect_header_level(original)\n\n    # Build new section\n    new_section = f\"\\n\\n{'#' * header_level} {route.chunk.header}\"\n    new_section += f\" (consolidated {date.today().isoformat()})\\n\\n\"\n    new_section += f\"*Source: {route.source}*\\n\\n\"\n    new_section += route.chunk.content\n\n    # Append\n    updated = original.rstrip() + new_section + \"\\n\"\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='appended',\n        section=route.chunk.header,\n        bytes_added=len(new_section),\n    )\n```\n\n## Source Deletion\n\nAfter all merges complete successfully:\n\n```python\ndef delete_sources(plan: ConsolidationPlan, results: list[ExecutionResult]) -> list[str]:\n    \"\"\"Delete source files after successful consolidation.\"\"\"\n\n    # Only delete if ALL operations succeeded\n    if any(r.status == 'failed' for r in results):\n        return []  # Don't delete anything\n\n    deleted = []\n    for source in plan.sources:\n        source_path = Path(source)\n        if source_path.exists():\n            source_path.unlink()\n            deleted.append(source)\n\n    return deleted\n```\n\n## Rollback Support\n\nMaintain log for potential rollback:\n\n```python\nCONSOLIDATION_LOG = '.consolidation-log.json'\n\ndef log_operation(operation: dict):\n    \"\"\"Log operation for potential rollback.\"\"\"\n    log_path = Path(CONSOLIDATION_LOG)\n\n    if log_path.exists():\n        log = json.loads(log_path.read_text())\n    else:\n        log = {'operations': [], 'timestamp': datetime.now().isoformat()}\n\n    log['operations'].append(operation)\n    log_path.write_text(json.dumps(log, indent=2))\n\ndef rollback_last():\n    \"\"\"Rollback most recent consolidation.\"\"\"\n    log_path = Path(CONSOLIDATION_LOG)\n    if not log_path.exists():\n        raise RollbackError(\"No consolidation log found\")\n\n    log = json.loads(log_path.read_text())\n\n    # Reverse operations\n    for op in reversed(log['operations']):\n        if op['action'] == 'created':\n            Path(op['destination']).unlink()\n        elif op['action'] == 'replaced':\n            restore_section(op['destination'], op['section'], op['original'])\n        elif op['action'] == 'deleted':\n            # Cannot restore deleted sources automatically\n            print(f\"WARNING: Cannot restore deleted source: {op['source']}\")\n\n    log_path.unlink()\n```\n\n## Execution Summary\n\nGenerate detailed summary:\n\n```markdown\n# Consolidation Complete\n\n**Timestamp**: 2025-12-06T14:32:15\n**Duration**: 2.3s\n\n## Created Files (3)\n\n| File | Size | Category |\n|------|------|----------|\n| docs/api-overview.md | 1,847 bytes | findings |\n| docs/plans/2025-12-06-api-consistency.md | 1,456 bytes | actionable |\n| docs/adr/0002-2025-12-06-cli-naming.md | 634 bytes | decisions |\n\n## Updated Files (1)\n\n| File | Section | Strategy | Change |\n|------|---------|----------|--------|\n| docs/architecture.md | Consistency | APPEND | +892 bytes |\n\n## Deleted Sources (1)\n\n- ~~API_REVIEW_REPORT.md~~ (deleted)\n\n## Verification Checklist\n\n- [ ] Review created files for accuracy\n- [ ] Check weaved content fits naturally\n- [ ] Run documentation build (if applicable)\n- [ ] Commit changes\n\n**Suggested commit message:**\n```\ndocs: consolidate API review findings\n\n- Created api-overview.md with plugin API inventory\n- Created plan for API consistency improvements\n- Added ADR for CLI naming convention\n- Updated architecture.md with consistency findings\n\nConsolidated from: API_REVIEW_REPORT.md\n```\n```\n\n## Error Handling\n\n```python\nclass ExecutionError(Exception):\n    \"\"\"Error during merge execution.\"\"\"\n    pass\n\ndef execute_with_recovery(plan: ConsolidationPlan) -> ExecutionSummary:\n    \"\"\"Execute plan with error recovery.\"\"\"\n    results = []\n\n    try:\n        # Execute creates\n        for route in plan.creates:\n            result = execute_create_new(route)\n            log_operation(result.to_dict())\n            results.append(result)\n\n        # Execute updates\n        for route in plan.updates:\n            if route.strategy == 'INTELLIGENT_WEAVE':\n                result = execute_intelligent_weave(route)\n            elif route.strategy == 'REPLACE_SECTION':\n                result = execute_replace_section(route)\n            else:\n                result = execute_append_with_context(route)\n\n            log_operation(result.to_dict())\n            results.append(result)\n\n        # Delete sources\n        deleted = delete_sources(plan, results)\n        for source in deleted:\n            log_operation({'action': 'deleted', 'source': source})\n\n        return ExecutionSummary(results=results, deleted=deleted, status='success')\n\n    except Exception as e:\n        # Log failure but don't auto-rollback\n        return ExecutionSummary(\n            results=results,\n            status='partial_failure',\n            error=str(e),\n            message=\"Some operations failed. Use rollback if needed.\"\n        )\n```\n\nFile v1.9.17:skill-card.md\n\n## Description: <br>\nMerges ephemeral report and analysis artifacts into permanent documentation. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[athola](https://clawhub.ai/user/athola) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers and documentation maintainers use this skill to identify temporary LLM-generated Markdown reports, route valuable sections into durable documentation, and clean up source artifacts after approval. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: Approved consolidation can delete source Markdown reports after successful merges, and rollback may not restore those originals automatically. <br>\nMitigation: Review the consolidation plan before approval, especially destination files and source files marked for deletion; keep external backups or commit/restore points for important reports. <br>\nRisk: Misrouted or low-value report content can introduce inaccurate or misleading material into permanent documentation. <br>\nMitigation: Review proposed destinations, merge strategies, and generated documentation changes before committing the results. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/athola/skills/nm-sanctum-doc-consolidation) <br>\n- [Clawdis homepage](https://github.com/athola/claude-night-market/tree/master/plugins/sanctum) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [Text, Markdown, Files, Shell commands, Guidance] <br>\n**Output Format:** [Markdown plans, documentation edits, and execution summaries] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Requires user approval before merge execution; source Markdown files may be deleted after successful consolidation.] <br>\n\n## Skill Version(s): <br>\n1.9.17 (source: ClawHub release evidence) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nArchive v1.9.16: 7 files, 16672 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), skill-card.md (2646b), SKILL.md (9098b), _meta.json (148b)\n\nFile v1.9.16:SKILL.md\n\n---\nname: doc-consolidation\ndescription: Merges ephemeral report and analysis artifacts into permanent documentation\nversion: 1.9.8\ntriggers:\n  - docs\n  - consolidation\n  - cleanup\n  - git-hygiene\n  - knowledge-management\n  - LLM-generated markdown files have accumulated and need consolidation\nmetadata: {\"openclaw\": {\"homepage\": \"https://github.com/athola/claude-night-market/tree/master/plugins/sanctum\", \"emoji\": \"\\ud83d\\udcdd\"}}\nsource: claude-night-market\nsource_plugin: sanctum\n---\n\n> **Night Market Skill** — ported from [claude-night-market/sanctum](https://github.com/athola/claude-night-market/tree/master/plugins/sanctum). For the full experience with agents, hooks, and commands, install the Claude Code plugin.\n\n\n## Table of Contents\n\n- [When to Use](#when-to-use)\n- [Quick Start](#quick-start)\n- [Two-Phase Workflow](#two-phase-workflow)\n- [Phase 1: Triage (Fast Model)](#phase-1:-triage-(fast-model))\n- [Phase 2: Execute (Main Model)](#phase-2:-execute-(main-model))\n- [Workflow Details](#workflow-details)\n- [Step 1: Candidate Detection](#step-1:-candidate-detection)\n- [Step 2: Content Analysis](#step-2:-content-analysis)\n- [Step 3: Destination Routing](#step-3:-destination-routing)\n- [Step 4: Generate Plan](#step-4:-generate-plan)\n- [Source: API_REVIEW_REPORT.md](#source:-api_review_reportmd)\n- [Post-Consolidation](#post-consolidation)\n- [Step 5: Execute Merges](#step-5:-execute-merges)\n- [Fast Model Delegation](#fast-model-delegation)\n- [Content Categories](#content-categories)\n- [Merge Strategies](#merge-strategies)\n- [Intelligent Weave](#intelligent-weave)\n- [Replace Section](#replace-section)\n- [Append with Context](#append-with-context)\n- [Create New File](#create-new-file)\n- [Integration](#integration)\n- [Example Session](#example-session)\n- [Troubleshooting](#troubleshooting)\n- [No candidates found](#no-candidates-found)\n- [Low-quality extractions](#low-quality-extractions)\n- [Merge conflicts](#merge-conflicts)\n- [Related Skills](#related-skills)\n\n\n# Doc Consolidation\n\nExtracts valuable knowledge from ephemeral LLM outputs and merges it into permanent documentation.\n\n## When To Use\n\nUse this skill when:\n- You have untracked `*_REPORT.md` or `*_ANALYSIS.md` files from Claude sessions\n- Git status shows markdown files that shouldn't be committed but contain useful content\n- You want to preserve insights from code reviews, refactoring reports, or API audits\n- Preparing a PR and need to clean up working artifacts\n\nDo NOT use when:\n- Files are already in proper documentation locations\n  (`docs/`, `skills/`)\n- Files are intentionally temporary scratch notes\n- User explicitly wants to preserve the original report format\n- Source files have no extractable value (pure log output)\n\n## Formatting\n\nWhen merging content into permanent documentation, follow\n`Skill(leyline:markdown-formatting)` conventions: wrap prose\nat 80 chars (prefer sentence/clause boundaries), blank lines\naround headings, ATX headings only, blank line before lists,\nand reference-style links for long URLs.\n\n## Quick Start\n\n```\n/consolidate-docs\n```\n\nOr invoke directly:\n\n```\nI have some report files that need consolidating into permanent docs.\n```\n\n## Two-Phase Workflow\n\n### Phase 1: Triage (Fast Model)\n\nRead-only analysis to generate a consolidation plan:\n\n1. **Detect candidates** - Find untracked markdown files with LLM output markers\n2. **Analyze content** - Extract and categorize valuable sections\n3. **Route destinations** - Match content to existing docs or propose new files\n4. **Present plan** - Show user what will be consolidated and where\n\n**Checkpoint**: User reviews and approves plan before execution.\n\n### Phase 2: Execute (Main Model)\n\nAfter approval, performs the consolidation:\n\n1. **Merge content** - Weave into existing docs or create new files\n2. **Delete sources** - Remove ephemeral files after successful merge\n3. **Generate summary** - Report what was created/updated/deleted\n\n## Workflow Details\n\n### Step 1: Candidate Detection\n\nLoad: `@modules/candidate-detection.md`\n\nIdentifies files using:\n- Git status (untracked `.md` files)\n- Location (not in standard doc directories)\n- Naming (ALL_CAPS non-standard names)\n- Content markers (Executive Summary, Findings, Action Items)\n\n### Step 2: Content Analysis\n\nLoad: `@modules/content-analysis.md`\n\nFor each candidate:\n- Extract sections as content chunks\n- Categorize: Actionable Items, Decisions, Findings, Metrics, Migration Guides, API Changes\n- Score value: high/medium/low\n\n### Step 3: Destination Routing\n\nLoad: `@modules/destination-routing.md`\n\nFor each valuable chunk:\n- Semantic match against existing documentation\n- Apply default mappings if no good match\n- Determine merge strategy (weave, replace, append, create)\n\n### Step 4: Generate Plan\n\nPresent consolidation plan to user:\n\n```markdown\n# Consolidation Plan\n\n## Source: API_REVIEW_REPORT.md\n\n| Content | Category | Value | Destination | Action |\n|---------|----------|-------|-------------|--------|\n| API inventory | Findings | High | docs/api-overview.md | Create |\n| Action items | Actionable | High | docs/plans/2025-12-06-api.md | Create |\n\n### Post-Consolidation\n- Delete: API_REVIEW_REPORT.md\n\nProceed with consolidation? [Y/n]\n```\n\n### Step 5: Execute Merges\n\nLoad: `@modules/merge-execution.md`\n\nAfter user approval:\n- Group operations by destination file\n- Apply merge strategies\n- Validate results (frontmatter intact, structure preserved)\n- Delete source files\n- Generate execution summary\n\n## Fast Model Delegation\n\nPhase 1 tasks are delegated to haiku-class models for efficiency:\n\n```python\n# plugins/sanctum/scripts/consolidation_planner.py handles:\n- scan_for_candidates()\n- extract_content_chunks()\n- categorize_chunks()\n- score_value()\n- find_semantic_matches()\n```\n\nPhase 2 stays on the main model for careful merge execution.\n\n## Content Categories\n\n| Category | Description | Default Destination |\n|----------|-------------|---------------------|\n| Actionable Items | Tasks, TODOs, next steps | `docs/plans/YYYY-MM-DD-{topic}.md` |\n| Decisions Made | Architecture choices | `docs/adr/NNNN-{date}-{topic}.md` |\n| Findings/Insights | Audit results, analysis | Best-match existing doc |\n| Metrics/Baselines | Before/after comparisons | `docs/benchmarks/` |\n| Migration Guides | Step-by-step procedures | `docs/migration-guide.md` |\n| API Changes | Breaking changes, deprecations | CHANGELOG or api docs |\n\n## Merge Strategies\n\n### Intelligent Weave\nInsert content into matching existing sections, preserving style.\n\n  Use when: Destination has relevant section, content is additive.\n\n### Replace Section\nReplace existing section with more detailed new content.\n\n  Use when: New content 2x+ more detailed or has later date.\n\n### Append with Context\nAdd new section with date and source reference.\n\n  Use when: No matching section, content doesn't fit existing structure.\n\n### Create New File\nGenerate complete new document.\n\n  Use when: No suitable destination exists, content warrants standalone doc.\n\n## Integration\n\nWorks with other sanctum skills:\n\n- **git-workspace-review**: May suggest consolidation when untracked reports detected\n- **pr-prep**: Warns about untracked analysis files before PR creation\n- **doc-updates**: Can invoke for final polish after consolidation\n\n## Example Session\n\n```\nUser: I have API_REVIEW_REPORT.md from our review session. Can you consolidate it?\n\nClaude: I'll consolidate the report into permanent documentation.\n\n**Phase 1: Triage**\n\nScanning for candidates...\n- Found: API_REVIEW_REPORT.md (untracked, LLM markers detected)\n\nAnalyzing content...\n- Extracted 6 content chunks\n- High value: API inventory, action items, CLI recommendation\n- Medium value: security assessment\n- Low value: executive summary (generic)\n\nRouting destinations...\n- API inventory → docs/api-overview.md (CREATE)\n- Action items → docs/plans/2025-12-06-api-consistency.md (CREATE)\n- CLI naming → docs/adr/0002-cli-naming.md (CREATE ADR)\n- Security → Already covered in docs/security.md (SKIP)\n\n**Consolidation Plan**\n[table shown]\n\nProceed? [Y/n]\n\nUser: Y\n\nClaude: **Phase 2: Executing**\n\nCreated: docs/api-overview.md (1,247 bytes)\nCreated: docs/plans/2025-12-06-api-consistency.md (892 bytes)\nCreated: docs/adr/0002-2025-12-06-cli-naming.md (634 bytes)\nDeleted: API_REVIEW_REPORT.md\n\nConsolidation complete. Review the created files and commit when ready.\n```\n\n## Troubleshooting\n\n### No candidates found\n- Check git status for untracked files\n- Files may already be in standard locations\n- Try explicit file path: \"Consolidate API_REVIEW_REPORT.md\"\n\n### Low-quality extractions\n- Source file may lack structured sections\n- Content may be too generic to categorize\n- Try manual extraction for unstructured reports\n\n### Merge conflicts\n- Destination file structure changed\n- Try APPEND strategy instead of WEAVE\n- Manual intervention may be needed\n\n## Related Skills\n\n- `sanctum:doc-updates` - General documentation updates\n- `sanctum:git-workspace-review` - Pre-flight workspace analysis\n- `sanctum:pr-prep` - Pull request preparation\n- `imbue:catchup` - Understanding recent changes\n\nFile v1.9.16:_meta.json\n\n{\n  \"ownerId\": \"kn7d107jg9jv602h9ytsegydq184a42s\",\n  \"slug\": \"nm-sanctum-doc-consolidation\",\n  \"version\": \"1.9.16\",\n  \"publishedAt\": 1784059027565\n}\n\nFile v1.9.16:modules/candidate-detection.md\n\n# Candidate Detection Module\n\nIdentifies markdown files that are candidates for consolidation.\n\n## Detection Signals\n\nApply signals in priority order. A file is a candidate if it matches **any** signal.\n\n### Signal 1: Git-Untracked Location (Highest Priority)\n\n```bash\n# Find untracked .md files\ngit status --porcelain | grep '^??' | grep '\\.md$'\n```\n\n**Exclude standard locations:**\n- `docs/` - Already permanent documentation\n- `skills/` - Skill definitions\n- `modules/` - Skill modules\n- `commands/` - Slash commands\n- `agents/` - Agent definitions\n- `.github/` - GitHub templates\n\n**Exclude standard names:**\n- `README.md`, `README`\n- `LICENSE.md`, `LICENSE`\n- `CONTRIBUTING.md`\n- `CHANGELOG.md`, `HISTORY.md`\n- `SECURITY.md`\n- `CODE_OF_CONDUCT.md`\n\n### Signal 2: ALL_CAPS Naming Pattern\n\nFiles with ALL_CAPS names that aren't standard conventions:\n\n```\nMATCHES (candidates):\n- API_REVIEW_REPORT.md\n- REFACTORING_REPORT.md\n- MIGRATION_ANALYSIS.md\n- AUDIT_FINDINGS.md\n- *_REPORT.md\n- *_ANALYSIS.md\n- *_REVIEW.md\n- *_FINDINGS.md\n\nEXCLUDES (not candidates):\n- README.md\n- LICENSE.md\n- CONTRIBUTING.md\n- CHANGELOG.md\n- SECURITY.md\n- CODE_OF_CONDUCT.md\n```\n\n### Signal 3: Content Markers\n\nScan first 100 lines for LLM output markers:\n\n**Strong markers (any one = candidate):**\n- `**Date**:` or `Date:` at start of line\n- `## Executive Summary`\n- `## Summary` (at document start)\n- `## Findings`\n- `## Action Items`\n- `## Recommendations`\n- `## Conclusion`\n\n**Supporting markers (need 2+ to qualify):**\n- Markdown tables with `|` columns\n- `### High Priority` / `### Medium Priority` / `### Low Priority`\n- `- [ ]` checkbox lists\n- `## 1.` numbered top-level sections\n- `**Scope**:` or `**Status**:`\n- Lines starting with status markers\n\n## Detection Algorithm\n\n```python\ndef detect_candidates(repo_path: str) -> list[CandidateFile]:\n    candidates = []\n\n    # Get untracked markdown files\n    untracked = git_untracked_md_files(repo_path)\n\n    for file_path in untracked:\n        # Skip standard locations\n        if is_standard_location(file_path):\n            continue\n\n        # Skip standard names\n        if is_standard_name(file_path):\n            continue\n\n        score = 0\n        reasons = []\n\n        # Check naming pattern\n        if is_allcaps_nonstandard(file_path):\n            score += 3\n            reasons.append(\"ALL_CAPS non-standard name\")\n\n        # Check content markers\n        content = read_first_n_lines(file_path, 100)\n        strong, supporting = count_content_markers(content)\n\n        if strong > 0:\n            score += 3\n            reasons.append(f\"Strong markers: {strong}\")\n\n        if supporting >= 2:\n            score += 2\n            reasons.append(f\"Supporting markers: {supporting}\")\n\n        # Threshold: score >= 2\n        if score >= 2:\n            candidates.append(CandidateFile(\n                path=file_path,\n                score=score,\n                reasons=reasons\n            ))\n\n    return sorted(candidates, key=lambda c: c.score, reverse=True)\n```\n\n## Output Format\n\n```markdown\n## Detected Candidates\n\n| File | Score | Reasons |\n|------|-------|---------|\n| API_REVIEW_REPORT.md | 6 | ALL_CAPS name, Strong markers: 3, Supporting: 4 |\n| REFACTORING_REPORT.md | 5 | ALL_CAPS name, Strong markers: 2 |\n| analysis-notes.md | 2 | Supporting markers: 3 |\n\nProceeding with 3 candidates...\n```\n\n## Edge Cases\n\n### Nested untracked directories\nIf an entire directory is untracked, scan all `.md` files within:\n```bash\ngit status --porcelain | grep '^??' | while read status path; do\n  if [ -d \"$path\" ]; then\n    find \"$path\" -name \"*.md\"\n  fi\ndone\n```\n\n### Partially staged files\nFiles that are partially staged (`MM` or `AM` status) should be flagged for user attention - they may contain mixed committed/uncommitted content.\n\n### Renamed/moved files\nIf git shows a rename (`R` status), check if the destination is a standard location. If moving TO a standard location, not a candidate.\n\n## Validation\n\nBefore proceeding, confirm candidates with user:\n\n```markdown\nFound 3 consolidation candidates:\n\n1. **API_REVIEW_REPORT.md** (score: 6)\n   - ALL_CAPS non-standard name\n   - Contains: Executive Summary, Findings, Action Items\n\n2. **REFACTORING_REPORT.md** (score: 5)\n   - ALL_CAPS non-standard name\n   - Contains: Summary, Conclusion\n\n3. **analysis-notes.md** (score: 2)\n   - Contains: Multiple tables, checkbox lists\n\nAnalyze these files for consolidation? [Y/n/select specific]\n```\n\nFile v1.9.16:modules/content-analysis.md\n\n# Content Analysis Module\n\nExtracts and categorizes valuable content from candidate files.\n\n## Content Categories\n\n### Category Definitions\n\n| Category | Description | Indicators |\n|----------|-------------|------------|\n| **Actionable Items** | Tasks, TODOs, next steps that require action | `Action Items`, `Next Steps`, `TODO`, `- [ ]` checkboxes |\n| **Decisions Made** | Architecture choices, tradeoffs, rationale | `Decision`, `Chose`, `Tradeoff`, `Rationale`, `Why we` |\n| **Findings/Insights** | Audit results, analysis conclusions, observations | `Findings`, `Observations`, `Analysis`, `Discovered`, `Noted` |\n| **Metrics/Baselines** | Quantitative data, before/after, benchmarks | Tables with numbers, `Before`, `After`, percentages, `Improvement` |\n| **Migration Guides** | Step-by-step procedures, how-to instructions | `Steps`, `How to`, `Migration`, numbered lists with commands |\n| **API Changes** | Interface modifications, breaking changes, deprecations | `API`, `Breaking`, `Deprecated`, `New endpoint`, `Removed` |\n\n### Extraction Process\n\nFor each candidate file:\n\n1. **Parse structure** - Identify sections by headers (`##`, `###`)\n2. **Extract chunks** - Each section becomes a content chunk\n3. **Categorize** - Match chunk to best-fit category\n4. **Score value** - Assess high/medium/low\n\n## Value Scoring\n\n### High Value\nContent that is:\n- **Specific**: Contains concrete names, paths, numbers\n- **Actionable**: Reader can act on it directly\n- **Unique**: Not already documented elsewhere\n\nExamples:\n- Specific action items with owners\n- Concrete metrics (before: 287 lines, after: 255 lines)\n- Explicit decisions with rationale\n- Step-by-step procedures that worked\n\n### Medium Value\nContent that is:\n- **Somewhat specific**: General guidance with some detail\n- **Reference-worthy**: Useful for future lookups\n- **Partially covered**: Extends existing documentation\n\nExamples:\n- General recommendations without specifics\n- Findings that align with existing docs\n- Metrics without clear baseline comparison\n\n### Low Value\nContent that is:\n- **Generic**: Could apply to any project\n- **Redundant**: Already well-documented elsewhere\n- **Ephemeral**: Only relevant to the moment\n\nExamples:\n- Executive summaries (usually boilerplate)\n- Generic best practice reminders\n- Status statements (\"The review is complete\")\n\n## Chunk Extraction Algorithm\n\n```python\ndef extract_chunks(content: str) -> list[ContentChunk]:\n    chunks = []\n    current_section = None\n    current_content = []\n\n    for line in content.split('\\n'):\n        # New section header\n        if line.startswith('## '):\n            if current_section:\n                chunks.append(make_chunk(current_section, current_content))\n            current_section = line[3:].strip()\n            current_content = []\n        elif line.startswith('### '):\n            # Subsection - append to current or create new\n            if current_section:\n                current_content.append(line)\n            else:\n                current_section = line[4:].strip()\n                current_content = []\n        else:\n            current_content.append(line)\n\n    # Don't forget last section\n    if current_section:\n        chunks.append(make_chunk(current_section, current_content))\n\n    return chunks\n\ndef make_chunk(header: str, content: list[str]) -> ContentChunk:\n    text = '\\n'.join(content).strip()\n    category = categorize(header, text)\n    value = score_value(text, category)\n\n    return ContentChunk(\n        header=header,\n        content=text,\n        category=category,\n        value=value\n    )\n```\n\n## Categorization Rules\n\nMatch in order (first match wins):\n\n```python\nCATEGORY_PATTERNS = {\n    'actionable': [\n        r'action\\s*items?',\n        r'next\\s*steps?',\n        r'todo',\n        r'tasks?',\n        r'- \\[ \\]',  # Unchecked checkboxes\n    ],\n    'decisions': [\n        r'decision',\n        r'chose|chosen',\n        r'tradeoff',\n        r'rationale',\n        r'why\\s+we',\n        r'approach',\n    ],\n    'findings': [\n        r'finding',\n        r'observation',\n        r'analysis',\n        r'discovered',\n        r'audit',\n        r'review\\s+result',\n    ],\n    'metrics': [\n        r'\\d+%',\n        r'before.*after',\n        r'improvement',\n        r'reduction',\n        r'benchmark',\n        r'\\|\\s*\\d+\\s*\\|',  # Table with numbers\n    ],\n    'migration': [\n        r'migration',\n        r'step\\s*\\d',\n        r'how\\s+to',\n        r'procedure',\n        r'```bash',  # Code blocks with commands\n    ],\n    'api_changes': [\n        r'api',\n        r'breaking\\s+change',\n        r'deprecat',\n        r'endpoint',\n        r'interface',\n    ],\n}\n```\n\n## Output Format\n\n```markdown\n## Content Analysis: API_REVIEW_REPORT.md\n\n### Extracted Chunks\n\n| # | Section | Category | Value | Size |\n|---|---------|----------|-------|------|\n| 1 | Executive Summary | findings | low | 234 chars |\n| 2 | API Surface Inventory | findings | high | 1,847 chars |\n| 3 | Consistency Audit Findings | findings | high | 2,103 chars |\n| 4 | Action Items | actionable | high | 1,456 chars |\n| 5 | Recommendations | actionable | medium | 892 chars |\n| 6 | Conclusion | findings | low | 312 chars |\n\n### High-Value Content (4 chunks)\n- API Surface Inventory: Detailed plugin API table\n- Consistency Audit Findings: Specific issues with examples\n- Action Items: Concrete tasks with priorities\n- (included in routing)\n\n### Excluded (Low Value)\n- Executive Summary: Generic overview\n- Conclusion: Status statement only\n```\n\n## Special Handling\n\n### Tables\nPreserve markdown tables intact - they often contain valuable structured data:\n```python\ndef is_table(lines: list[str]) -> bool:\n    return any('|' in line and line.count('|') >= 2 for line in lines)\n```\n\n### Code Blocks\nPreserve code blocks, especially those showing:\n- Configuration examples\n- Command sequences\n- Before/after code comparisons\n\n### Checklists\nPreserve checkbox lists - they indicate actionable items:\n```markdown\n- [x] Completed item (historical record)\n- [ ] Pending item (action needed)\n```\n\n### Cross-references\nNote internal references for destination routing:\n```python\n# Links like \"See also: docs/security.md\" suggest destinations\nREFERENCE_PATTERN = r'see\\s+(?:also:?\\s*)?([^\\s,]+\\.md)'\n```\n\nFile v1.9.16:modules/destination-routing.md\n\n# Destination Routing Module\n\nMaps extracted content chunks to appropriate destinations in the documentation.\n\n## Routing Strategy\n\n### Priority Order\n\n1. **Semantic match** - Find existing doc that covers the topic\n2. **Default mapping**\n - Use category-based default destinations\n3. **Create new** - Only when no suitable destination exists\n\n### Preference: Existing Over New\n\nAlways prefer merging into existing documentation:\n- Keeps documentation consolidated\n- Avoids duplicate coverage\n- Maintains established structure\n\nCreate new files only when:\n- Content is substantial (>500 chars of high-value)\n- No existing doc covers the topic\n- Content warrants standalone treatment\n\n## Semantic Matching\n\n### Algorithm\n\n```python\ndef find_semantic_match(chunk: ContentChunk, existing_docs: list[str]) -> str | None:\n    \"\"\"Find best-matching existing document for a content chunk.\"\"\"\n\n    best_match = None\n    best_score = 0\n\n    for doc_path in existing_docs:\n        doc_content = read_file(doc_path)\n        score = compute_relevance(chunk, doc_content)\n\n        if score > best_score and score >= MATCH_THRESHOLD:\n            best_match = doc_path\n            best_score = score\n\n    return best_match\n\ndef compute_relevance(chunk: ContentChunk, doc_content: str) -> float:\n    \"\"\"Score relevance of chunk to document.\"\"\"\n    score = 0.0\n\n    # Header matching (highest weight)\n    doc_headers = extract_headers(doc_content)\n    if any(similar(chunk.header, h) for h in doc_headers):\n        score += 0.4\n\n    # Keyword overlap\n    chunk_keywords = extract_keywords(chunk.content)\n    doc_keywords = extract_keywords(doc_content)\n    overlap = len(chunk_keywords & doc_keywords) / len(chunk_keywords)\n    score += overlap * 0.3\n\n    # Category alignment\n    if doc_likely_category(doc_content) == chunk.category:\n        score += 0.2\n\n    # Reference mentions\n    if chunk mentions doc_path or doc mentions chunk source:\n        score += 0.1\n\n    return score\n\nMATCH_THRESHOLD = 0.5  # Minimum score to consider a match\n```\n\n### Existing Doc Discovery\n\nScan these locations for potential destinations:\n\n```python\nDOC_LOCATIONS = [\n    'docs/',\n    'docs/plans/',\n    'docs/adr/',\n    'README.md',\n    'CHANGELOG.md',\n]\n\n# Plugin-specific locations\nPLUGIN_DOC_LOCATIONS = [\n    '{plugin}/docs/',\n    '{plugin}/README.md',\n]\n```\n\n## Default Mappings\n\nWhen semantic matching finds no suitable destination:\n\n| Category | Default Destination | Notes |\n|----------|---------------------|-------|\n| Actionable Items | `docs/plans/YYYY-MM-DD-{topic}.md` | New plan file |\n| Decisions Made | `docs/adr/NNNN-YYYY-MM-DD-{topic}.md` | New ADR |\n| Findings/Insights | `docs/{topic}.md` | New doc or best-effort match |\n| Metrics/Baselines | `docs/benchmarks.md` or inline | Append if exists |\n| Migration Guides | `docs/migration-guide.md` | Append section |\n| API Changes | `CHANGELOG.md` or `docs/api.md` | Prefer CHANGELOG |\n\n### Topic Extraction\n\nDerive topic slug from content:\n\n```python\ndef extract_topic(chunk: ContentChunk, source_file: str) -> str:\n    \"\"\"Extract topic slug for file naming.\"\"\"\n\n    # Try chunk header first\n    if chunk.header:\n        return slugify(chunk.header)\n\n    # Try source file name\n    source_name = Path(source_file).stem\n    if '_REPORT' in source_name:\n        return slugify(source_name.replace('_REPORT', ''))\n\n    # Fall back to category\n    return chunk.category\n\ndef slugify(text: str) -> str:\n    \"\"\"Convert text to kebab-case slug.\"\"\"\n    text = text.lower()\n    text = re.sub(r'[^a-z0-9]+', '-', text)\n    text = text.strip('-')\n    return text[:50]  # Max length\n```\n\n## Merge Strategy Selection\n\nFor each chunk-destination pair, determine how to merge:\n\n### Decision Tree\n\n```\nIs destination a new file?\n├── Yes → CREATE_NEW\n└── No → Does destination have matching section?\n    ├── Yes → Is new content more detailed?\n    │   ├── Yes (2x+ detail OR newer date) → REPLACE_SECTION\n    │   └── No → INTELLIGENT_WEAVE\n    └── No → APPEND_WITH_CONTEXT\n```\n\n### Strategy Definitions\n\n**CREATE_NEW**\n- Generate complete new file with frontmatter\n- Use appropriate template for category\n- Include source attribution\n\n**INTELLIGENT_WEAVE**\n- Find matching section in destination\n- Insert content matching existing style\n- Preserve bullet/table/prose formatting\n\n**REPLACE_SECTION**\n- Back up existing content (in consolidation log)\n- Replace entire section\n- Add \"Updated: YYYY-MM-DD\" marker\n\n**APPEND_WITH_CONTEXT**\n- Add new section at logical location\n- Include header with date and source\n- Format: `## {Topic} (consolidated from {source}, {date})`\n\n## Output Format\n\n```markdown\n## Routing Plan\n\n### Source: API_REVIEW_REPORT.md\n\n| Chunk | Destination | Strategy | Rationale |\n|-------|-------------|----------|-----------|\n| API Surface Inventory | docs/api-overview.md | CREATE_NEW | No existing API docs, substantial content |\n| Consistency Findings | docs/architecture.md | APPEND_WITH_CONTEXT | Related topic, no matching section |\n| Action Items | docs/plans/2025-12-06-api-consistency.md | CREATE_NEW | Actionable, needs tracking |\n| CLI Recommendation | docs/adr/0002-cli-naming.md | CREATE_NEW | Decision warrants ADR |\n\n### Skipped (Low Value)\n| Chunk | Reason |\n|-------|--------|\n| Executive Summary | Generic, low value |\n| Conclusion | Status only, no lasting value |\n\n### Destination Summary\n- **New files**: 3\n- **Updates to existing**: 1\n- **Skipped**: 2\n```\n\n## ADR Generation\n\nFor decisions that warrant an ADR:\n\n```markdown\n# ADR-{NNNN}: {Decision Title}\n\n**Date**: {YYYY-MM-DD}\n**Status**: Accepted\n**Consolidated from**: {source_file}\n\n## Context\n\n{extracted context from source}\n\n## Decision\n\n{extracted decision}\n\n## Consequences\n\n{extracted consequences or \"To be determined\"}\n\n## References\n\n- Source: {source_file} (consolidated {date})\n```\n\n### ADR Numbering\n\n```python\ndef next_adr_number(adr_dir: str) -> int:\n    \"\"\"Find next available ADR number.\"\"\"\n    existing = glob(f\"{adr_dir}/[0-9][0-9][0-9][0-9]-*.md\")\n    if not existing:\n        return 1\n    numbers = [int(Path(p).name[:4]) for p in existing]\n    return max(numbers) + 1\n```\n\n## Validation\n\nBefore finalizing routing:\n\n1. **Check destination exists** (for updates)\n2. **Check write permissions**\n3. **Verify no circular references**\n4. **Confirm ADR numbering is unique**\n\n```python\ndef validate_routing(plan: RoutingPlan) -> list[str]:\n    \"\"\"Return list of validation errors.\"\"\"\n    errors = []\n\n    for route in plan.routes:\n        if route.strategy != 'CREATE_NEW':\n            if not Path(route.destination).exists():\n                errors.append(f\"Destination not found: {route.destination}\")\n\n        if route.strategy == 'CREATE_NEW':\n            if Path(route.destination).exists():\n                errors.append(f\"Would overwrite existing: {route.destination}\")\n\n    return errors\n```\n\nFile v1.9.16:modules/merge-execution.md\n\n# Merge Execution Module\n\nExecutes the approved consolidation plan, performing actual file operations.\n\n## Execution Order\n\n1. **Group by destination** - Minimize file I/O\n2. **Process creates first** - New files before updates\n3. **Process updates** - Apply merges to existing files\n4. **Delete sources** - Remove after successful consolidation\n5. **Generate summary** - Report all changes\n\n## Pre-Execution Checks\n\nBefore any file operations:\n\n```python\ndef pre_execution_checks(plan: ConsolidationPlan) -> list[str]:\n    \"\"\"Validate plan is safe to execute. Returns errors.\"\"\"\n    errors = []\n\n    # Check all destinations are writable\n    for route in plan.routes:\n        dest_dir = Path(route.destination).parent\n        if not dest_dir.exists():\n            # Will create - check parent is writable\n            if not os.access(dest_dir.parent, os.W_OK):\n                errors.append(f\"Cannot create directory: {dest_dir}\")\n        elif not os.access(route.destination, os.W_OK):\n            errors.append(f\"Cannot write to: {route.destination}\")\n\n    # Check sources exist and are readable\n    for source in plan.sources:\n        if not Path(source).exists():\n            errors.append(f\"Source not found: {source}\")\n\n    # Check for conflicting operations\n    destinations = [r.destination for r in plan.routes]\n    if len(destinations) != len(set(destinations)):\n        # Multiple chunks going to same file - need ordering\n        pass  # This is fine, handled by grouping\n\n    return errors\n```\n\n## Strategy Implementations\n\n### CREATE_NEW\n\n```python\ndef execute_create_new(route: Route) -> ExecutionResult:\n    \"\"\"Create a new file with content.\"\"\"\n\n    # validate directory exists\n    dest_path = Path(route.destination)\n    dest_path.parent.mkdir(parents=True, exist_ok=True)\n\n    # Generate content based on category\n    if route.chunk.category == 'decisions':\n        content = generate_adr_content(route)\n    elif route.chunk.category == 'actionable':\n        content = generate_plan_content(route)\n    else:\n        content = generate_doc_content(route)\n\n    # Write file\n    dest_path.write_text(content)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='created',\n        bytes_written=len(content),\n    )\n```\n\n### INTELLIGENT_WEAVE\n\n```python\ndef execute_intelligent_weave(route: Route) -> ExecutionResult:\n    \"\"\"Insert content into matching section of existing file.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Find matching section\n    section_pattern = find_matching_section(original, route.chunk.header)\n\n    if not section_pattern:\n        # Fall back to append\n        return execute_append_with_context(route)\n\n    # Analyze existing style\n    style = analyze_section_style(original, section_pattern)\n\n    # Format new content to match\n    formatted = format_to_match_style(route.chunk.content, style)\n\n    # Insert at appropriate location within section\n    updated = insert_in_section(original, section_pattern, formatted)\n\n    # Validate result\n    if not validate_markdown(updated):\n        raise ExecutionError(f\"Weave produced invalid markdown for {route.destination}\")\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='weaved',\n        section=section_pattern.header,\n        bytes_added=len(formatted),\n    )\n\ndef analyze_section_style(content: str, section: SectionMatch) -> Style:\n    \"\"\"Determine formatting style of existing section.\"\"\"\n    section_content = extract_section_content(content, section)\n\n    return Style(\n        uses_bullets=bool(re.search(r'^[-*]\\s', section_content, re.M)),\n        uses_numbers=bool(re.search(r'^\\d+\\.\\s', section_content, re.M)),\n        uses_tables=bool(re.search(r'^\\|.*\\|$', section_content, re.M)),\n        indent_style=detect_indent(section_content),\n        has_blank_lines='\\n\\n' in section_content,\n    )\n```\n\n### REPLACE_SECTION\n\n```python\ndef execute_replace_section(route: Route) -> ExecutionResult:\n    \"\"\"Replace entire section with new content.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Find section boundaries\n    section = find_section_boundaries(original, route.target_section)\n\n    if not section:\n        raise ExecutionError(f\"Section '{route.target_section}' not found in {route.destination}\")\n\n    # Log what we're replacing (for rollback if needed)\n    replaced_content = original[section.start:section.end]\n    log_replacement(route.destination, route.target_section, replaced_content)\n\n    # Build replacement with update marker\n    replacement = f\"{section.header}\\n\\n\"\n    replacement += f\"*Updated: {date.today().isoformat()} (consolidated from {route.source})*\\n\\n\"\n    replacement += route.chunk.content\n\n    # Replace in document\n    updated = original[:section.start] + replacement + original[section.end:]\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='replaced',\n        section=route.target_section,\n        bytes_before=len(replaced_content),\n        bytes_after=len(replacement),\n    )\n```\n\n### APPEND_WITH_CONTEXT\n\n```python\ndef execute_append_with_context(route: Route) -> ExecutionResult:\n    \"\"\"Add new section at end of document.\"\"\"\n\n    dest_path = Path(route.destination)\n    original = dest_path.read_text()\n\n    # Determine section level (match document)\n    header_level = detect_header_level(original)\n\n    # Build new section\n    new_section = f\"\\n\\n{'#' * header_level} {route.chunk.header}\"\n    new_section += f\" (consolidated {date.today().isoformat()})\\n\\n\"\n    new_section += f\"*Source: {route.source}*\\n\\n\"\n    new_section += route.chunk.content\n\n    # Append\n    updated = original.rstrip() + new_section + \"\\n\"\n\n    dest_path.write_text(updated)\n\n    return ExecutionResult(\n        destination=route.destination,\n        action='appended',\n        section=route.chunk.header,\n        bytes_added=len(new_section),\n    )\n```\n\n## Source Deletion\n\nAfter all merges complete successfully:\n\n```python\ndef delete_sources(plan: ConsolidationPlan, results: list[ExecutionResult]) -> list[str]:\n    \"\"\"Delete source files after successful consolidation.\"\"\"\n\n    # Only delete if ALL operations succeeded\n    if any(r.status == 'failed' for r in results):\n        return []  # Don't delete anything\n\n    deleted = []\n    for source in plan.sources:\n        source_path = Path(source)\n        if source_path.exists():\n            source_path.unlink()\n            deleted.append(source)\n\n    return deleted\n```\n\n## Rollback Support\n\nMaintain log for potential rollback:\n\n```python\nCONSOLIDATION_LOG = '.consolidation-log.json'\n\ndef log_operation(operation: dict):\n    \"\"\"Log operation for potential rollback.\"\"\"\n    log_path = Path(CONSOLIDATION_LOG)\n\n    if log_path.exists():\n        log = json.loads(log_path.read_text())\n    else:\n        log = {'operations': [], 'timestamp': datetime.now().isoformat()}\n\n    log['operations'].append(operation)\n    log_path.write_text(json.dumps(log, indent=2))\n\ndef rollback_last():\n    \"\"\"Rollback most recent consolidation.\"\"\"\n    log_path = Path(CONSOLIDATION_LOG)\n    if not log_path.exists():\n        raise RollbackError(\"No consolidation log found\")\n\n    log = json.loads(log_path.read_text())\n\n    # Reverse operations\n    for op in reversed(log['operations']):\n        if op['action'] == 'created':\n            Path(op['destination']).unlink()\n        elif op['action'] == 'replaced':\n            restore_section(op['destination'], op['section'], op['original'])\n        elif op['action'] == 'deleted':\n            # Cannot restore deleted sources automatically\n            print(f\"WARNING: Cannot restore deleted source: {op['source']}\")\n\n    log_path.unlink()\n```\n\n## Execution Summary\n\nGenerate detailed summary:\n\n```markdown\n# Consolidation Complete\n\n**Timestamp**: 2025-12-06T14:32:15\n**Duration**: 2.3s\n\n## Created Files (3)\n\n| File | Size | Category |\n|------|------|----------|\n| docs/api-overview.md | 1,847 bytes | findings |\n| docs/plans/2025-12-06-api-consistency.md | 1,456 bytes | actionable |\n| docs/adr/0002-2025-12-06-cli-naming.md | 634 bytes | decisions |\n\n## Updated Files (1)\n\n| File | Section | Strategy | Change |\n|------|---------|----------|--------|\n| docs/architecture.md | Consistency | APPEND | +892 bytes |\n\n## Deleted Sources (1)\n\n- ~~API_REVIEW_REPORT.md~~ (deleted)\n\n## Verification Checklist\n\n- [ ] Review created files for accuracy\n- [ ] Check weaved content fits naturally\n- [ ] Run documentation build (if applicable)\n- [ ] Commit changes\n\n**Suggested commit message:**\n```\ndocs: consolidate API review findings\n\n- Created api-overview.md with plugin API inventory\n- Created plan for API consistency improvements\n- Added ADR for CLI naming convention\n- Updated architecture.md with consistency findings\n\nConsolidated from: API_REVIEW_REPORT.md\n```\n```\n\n## Error Handling\n\n```python\nclass ExecutionError(Exception):\n    \"\"\"Error during merge execution.\"\"\"\n    pass\n\ndef execute_with_recovery(plan: ConsolidationPlan) -> ExecutionSummary:\n    \"\"\"Execute plan with error recovery.\"\"\"\n    results = []\n\n    try:\n        # Execute creates\n        for route in plan.creates:\n            result = execute_create_new(route)\n            log_operation(result.to_dict())\n            results.append(result)\n\n        # Execute updates\n        for route in plan.updates:\n            if route.strategy == 'INTELLIGENT_WEAVE':\n                result = execute_intelligent_weave(route)\n            elif route.strategy == 'REPLACE_SECTION':\n                result = execute_replace_section(route)\n            else:\n                result = execute_append_with_context(route)\n\n            log_operation(result.to_dict())\n            results.append(result)\n\n        # Delete sources\n        deleted = delete_sources(plan, results)\n        for source in deleted:\n            log_operation({'action': 'deleted', 'source': source})\n\n        return ExecutionSummary(results=results, deleted=deleted, status='success')\n\n    except Exception as e:\n        # Log failure but don't auto-rollback\n        return ExecutionSummary(\n            results=results,\n            status='partial_failure',\n            error=str(e),\n            message=\"Some operations failed. Use rollback if needed.\"\n        )\n```\n\nFile v1.9.16:skill-card.md\n\n## Description: <br>\nMerges ephemeral report and analysis artifacts into permanent documentation. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[athola](https://clawhub.ai/user/athola) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers and documentation maintainers use this skill to identify temporary LLM-generated markdown reports, extract durable findings or decisions, and consolidate them into permanent project documentation after review. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: Approved execution can modify repository documentation and delete temporary source report files. <br>\nMitigation: Review the consolidation plan, source files, and destinations before approval, and keep a backup or Git commit for valuable reports. <br>\nRisk: Candidate detection may select files that are untracked, partially staged, or intentionally temporary. <br>\nMitigation: Confirm the candidate list and exclude files that are already permanent documentation, scratch notes, or should preserve their original report format. <br>\nRisk: Merged content can introduce inaccurate, stale, or poorly placed guidance into permanent documentation. <br>\nMitigation: Inspect generated diffs, verify the merged content against the source report, and run any available documentation checks before committing. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill listing](https://clawhub.ai/athola/skills/nm-sanctum-doc-consolidation) <br>\n- [Project homepage from ClawHub metadata](https://github.com/athola/claude-night-market/tree/master/plugins/sanctum) <br>\n- [Candidate detection module](artifact/modules/candidate-detection.md) <br>\n- [Content analysis module](artifact/modules/content-analysis.md) <br>\n- [Destination routing module](artifact/modules/destination-routing.md) <br>\n- [Merge execution module](artifact/modules/merge-execution.md) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, code, shell commands, guidance] <br>\n**Output Format:** [Markdown prose with tables and inline code blocks] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [May create or update documentation files and delete approved temporary source reports.] <br>\n\n## Skill Version(s): <br>\n1.9.16 (source: server release evidence) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nArchive v1.9.14: 7 files, 16542 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), skill-card.md (2259b), SKILL.md (9098b), _meta.json (148b)\n\nFile v1.9.14:SKILL.md\n\n---\nname: doc-consolidation\ndescription: Merges ephemeral report and analysis artifacts into permanent documentation\nversion: 1.9.8\ntriggers:\n  - docs\n  - consolidation\n  - cleanup\n  - git-hygiene\n  - knowledge-management\n  - LLM-generated markdown files have accumulated and need consolidation\nmetadata: {\"openclaw\": {\"homepage\": \"https://github.com/athola/claude-night-market/tree/master/plugins/sanctum\", \"emoji\": \"\\ud83d\\udcdd\"}}\nsource: claude-night-market\nsource_plugin: sanctum\n---\n\n> **Night Market Skill** — ported from [claude-night-market/sanctum](https://github.com/athola/claude-night-market/tree/master/plugins/sanctum). For the full experience with agents, hooks, and commands, install the Claude Code plugin.\n\n\n## Table of Contents\n\n- [When to Use](#when-to-use)\n- [Quick Start](#quick-start)\n- [Two-Phase Workflow](#two-phase-workflow)\n- [Phase 1: Triage (Fast Model)](#phase-1:-triage-(fast-model))\n- [Phase 2: Execute (Main Model)](#phase-2:-execute-(main-model))\n- [Workflow Details](#workflow-details)\n- [Step 1: Candidate Detection](#step-1:-candidate-detection)\n- [Step 2: Content Analysis](#step-2:-content-analysis)\n- [Step 3: Destination Routing](#step-3:-destination-routing)\n- [Step 4: Generate Plan](#step-4:-generate-plan)\n- [Source: API_REVIEW_REPORT.md](#source:-api_review_reportmd)\n- [Post-Consolidation](#post-consolidation)\n- [Step 5: Execute Merges](#step-5:-execute-merges)\n- [Fast Model Delegation](#fast-model-delegation)\n- [Content Categories](#content-categories)\n- [Merge Strategies](#merge-strategies)\n- [Intelligent Weave](#intelligent-weave)\n- [Replace Section](#replace-section)\n- [Append with Context](#append-with-context)\n- [Create New File](#create-new-file)\n- [Integration](#integration)\n- [Example Session](#example-session)\n- [Troubleshooting](#troubleshooting)\n- [No candidates found](#no-candidates-found)\n- [Low-quality extractions](#low-quality-extractions)\n- [Merge conflicts](#merge-conflicts)\n- [Related Skills](#related-skills)\n\n\n# Doc Consolidation\n\nExtracts valuable knowledge from ephemeral LLM outputs and merges it into permanent documentation.\n\n## When To Use\n\nUse this skill when:\n- You have untracked `*_REPORT.md` or `*_ANALYSIS.md` files from Claude sessions\n- Git status shows markdown files that shouldn't be committed but contain useful content\n- You want to preserve insights from code reviews, refactoring reports, or API audits\n- Preparing a PR and need to clean up working artifacts\n\nDo NOT use when:\n- Files are already in proper documentation locations\n  (`docs/`, `skills/`)\n- Files are intentionally temporary scratch notes\n- User explicitly wants to preserve the original report format\n- Source files have no extractable value (pure log output)\n\n## Formatting\n\nWhen merging content into permanent documentation, follow\n`Skill(leyline:markdown-formatting)` conventions: wrap prose\nat 80 chars (prefer sentence/clause boundaries), blank lines\naround headings, ATX headings only, blank line before lists,\nand reference-style links for long URLs.\n\n## Quick Start\n\n```\n/consolidate-docs\n```\n\nOr invoke directly:\n\n```\nI have some report files that need consolidating into permanent docs.\n```\n\n## Two-Phase Workflow\n\n### Phase 1: Triage (Fast Model)\n\nRead-only analysis to generate a consolidation plan:\n\n1. **Detect candidates** - Find untracked markdown files with LLM output markers\n2. **Analyze content** - Extract and categorize valuable sections\n3. **Route destinations** - Match content to existing docs or propose new files\n4. **Present plan** - Show user what will be consolidated and where\n\n**Checkpoint**: User reviews and approves plan before execution.\n\n### Phase 2: Execute (Main Model)\n\nAfter approval, performs the consolidation:\n\n1. **Merge content** - Weave into existing docs or create new files\n2. **Delete sources** - Remove ephemeral files after successful merge\n3. **Generate summary** - Report what was created/updated/deleted\n\n## Workflow Details\n\n### Step 1: Candidate Detection\n\nLoad: `@modules/candidate-detection.md`\n\nIdentifies files using:\n- Git status (untracked `.md` files)\n- Location (not in standard doc directories)\n- Naming (ALL_CAPS non-standard names)\n- Content markers (Executive Summary, Findings, Action Items)\n\n### Step 2: Content Analysis\n\nLoad: `@modules/content-analysis.md`\n\nFor each candidate:\n- Extract sections as content chunks\n- Categorize: Actionable Items, Decisions, Findings, Metrics, Migration Guides, API Changes\n- Score value: high/medium/low\n\n### Step 3: Destination Routing\n\nLoad: `@modules/destination-routing.md`\n\nFor each valuable chunk:\n- Semantic match against existing documentation\n- Apply default mappings if no good match\n- Determine merge strategy (weave, replace, append, create)\n\n### Step 4: Generate Plan\n\nPresent consolidation plan to user:\n\n```markdown\n# Consolidation Plan\n\n## Source: API_REVIEW_REPORT.md\n\n| Content | Category | Value | Destination | Action |\n|---------|----------|-------|-------------|--------|\n| API inventory | Findings | High | docs/api-overview.md | Create |\n| Action items | Actionable | High | docs/plans/2025-12-06-api.md | Create |\n\n### Post-Consolidation\n- Delete: API_REVIEW_REPORT.md\n\nProceed with consolidation? [Y/n]\n```\n\n### Step 5: Execute Merges\n\nLoad: `@modules/merge-execution.md`\n\nAfter user approval:\n- Group operations by destination file\n- Apply merge strategies\n- Validate results (frontmatter intact, structure preserved)\n- Delete source files\n- Generate execution summary\n\n## Fast Model Delegation\n\nPhase 1 tasks are delegated to haiku-class models for efficiency:\n\n```python\n# plugins/sanctum/scripts/consolidation_planner.py handles:\n- scan_for_candidates()\n- extract_content_chunks()\n- categorize_chunks()\n- score_value()\n- find_semantic_matches()\n```\n\nPhase 2 stays on the main model for careful merge execution.\n\n## Content Categories\n\n| Category | Description | Default Destination |\n|----------|-------------|---------------------|\n| Actionable Items | Tasks, TODOs, next steps | `docs/plans/YYYY-MM-DD-{topic}.md` |\n| Decisions Made | Architecture choices | `docs/adr/NNNN-{date}-{topic}.md` |\n| Findings/Insights | Audit results, analysis | Best-match existing doc |\n| Metrics/Baselines | Before/after comparisons | `docs/benchmarks/` |\n| Migration Guides | Step-by-step procedures | `docs/migration-guide.md` |\n| API Changes | Breaking changes, deprecations | CHANGELOG or api docs |\n\n## Merge Strategies\n\n### Intelligent Weave\nInsert content into matching existing sections, preserving style.\n\n  Use when: Destination has relevant section, content is additive.\n\n### Replace Section\nReplace existing section with more detailed new content.\n\n  Use when: New content 2x+ more detailed or has later date.\n\n### Append with Context\nAdd new section with date and source reference.\n\n  Use when: No matching section, content doesn't fit existing structure.\n\n### Create New File\nGenerate complete new document.\n\n  Use when: No suitable destination exists, content warrants standalone doc.\n\n## Integration\n\nWorks with other sanctum skills:\n\n- **git-workspace-review**: May suggest consolidation when untracked reports detected\n- **pr-prep**: Warns about untracked analysis files before PR creation\n- **doc-updates**: Can invoke for final polish after consolidation\n\n## Example Session\n\n```\nUser: I have API_REVIEW_REPORT.md from our review session. Can you consolidate it?\n\nClaude: I'll consolidate the report into permanent documentation.\n\n**Phase 1: Triage**\n\nScanning for candidates...\n- Found: API_REVIEW_REPORT.md (untracked, LLM markers detected)\n\nAnalyzing content...\n- Extracted 6 content chunks\n- High value: API inventory, action items, CLI recommendation\n- Medium value: security assessment\n- Low value: executive summary (generic)\n\nRouting destinations...\n- API inventory → docs/api-overview.md (CREATE)\n- Action items → docs/plans/2025-12-06-api-consistency.md (CREATE)\n- CLI naming → docs/adr/0002-cli-naming.md (CREATE ADR)\n- Security → Already covered in docs/security.md (SKIP)\n\n**Consolidation Plan**\n[table shown]\n\nProceed? [Y/n]\n\nUser: Y\n\nClaude: **Phase 2: Executing**\n\nCreated: docs/api-overview.md (1,247 bytes)\nCreated: docs/plans/2025-12-06-api-consistency.md (892 bytes)\nCreated: docs/adr/0002-2025-12-06-cli-naming.md (634 bytes)\nDeleted: API_REVIEW_REPORT.md\n\nConsolidation complete. Review the created files and commit when ready.\n```\n\n## Troubleshooting\n\n### No candidates found\n- Check git status for untracked files\n- Files may already be in standard locations\n- Try explicit file path: \"Consolidate API_REVIEW_REPORT.md\"\n\n### Low-quality extractions\n- Source file may lack structured sections\n- Content may be too generic to categorize\n- Try manual extraction for unstructured reports\n\n### Merge conflicts\n- Destination file structure changed\n- Try APPEND strategy instead of WEAVE\n- Manual intervention may be needed\n\n## Related Skills\n\n- `sanctum:doc-updates` - General documentation updates\n- `sanctum:git-workspace-review` - Pre-flight workspace analysis\n- `sanctum:pr-prep` - Pull request preparation\n- `imbue:catchup` - Understanding recent changes\n\nFile v1.9.14:_meta.json\n\n{\n  \"ownerId\": \"kn7d107jg9jv602h9ytsegydq184a42s\",\n  \"slug\": \"nm-sanctum-doc-consolidation\",\n  \"version\": \"1.9.14\",\n  \"publishedAt\": 1782842705720\n}\n\nFile v1.9.14:modules/candidate-detection.md\n\n# Candidate Detection Module\n\nIdentifies markdown files that are candidates for consolidation.\n\n## Detection Signals\n\nApply signals in priority order. A file is a candidate if it matches **any** signal.\n\n### Signal 1: Git-Untracked Location (Highest Priority)\n\n```bash\n# Find untracked .md files\ngit status --porcelain | grep '^??' | grep '\\.md$'\n```\n\n**Exclude standard locations:**\n- `docs/` - Already permanent documentation\n- `skills/` - Skill definitions\n- `modules/` - Skill modules\n- `commands/` - Slash commands\n- `agents/` - Agent definitions\n- `.github/` - GitHub templates\n\n**Exclude standard names:**\n- `README.md`, `README`\n- `LICENSE.md`, `LICENSE`\n- `CONTRIBUTING.md`\n- `CHANGELOG.md`, `HISTORY.md`\n- `SECURITY.md`\n- `CODE_OF_CONDUCT.md`\n\n### Signal 2: ALL_CAPS Naming Pattern\n\nFiles with ALL_CAPS names that aren't standard conventions:\n\n```\nMATCHES (candidates):\n- API_REVIEW_REPORT.md\n- REFACTORING_REPORT.md\n- MIGRATION_ANALYSIS.md\n- AUDIT_FINDINGS.md\n- *_REPORT.md\n- *_ANALYSIS.md\n- *_REVIEW.md\n- *_FINDINGS.md\n\nEXCLUDES (not candidates):\n- README.md\n- LICENSE.md\n- CONTRIBUTING.md\n- CHANGELOG.md\n- SECURITY.md\n- CODE_OF_CONDUCT.md\n```\n\n### Signal 3: Content Markers\n\nScan first 100 lines for LLM output markers:\n\n**Strong markers (any one = candidate):**\n- `**Date**:` or `Date:` at start of line\n- `## Executive Summary`\n- `## Summary` (at document start)\n- `## Findings`\n- `## Action Items`\n- `## Recommendations`\n- `## Conclusion`\n\n**Supporting markers (need 2+ to qualify):**\n- Markdown tables with `|` columns\n- `### High Priority` / `### Medium Priority` / `### Low Priority`\n- `- [ ]` checkbox lists\n- `## 1.` numbered top-level sections\n- `**Scope**:` or `**Status**:`\n- Lines starting with status markers\n\n## Detection Algorithm\n\n```python\ndef detect_candidates(repo_path: str) -> list[CandidateFile]:\n    candidates = []\n\n    # Get untracked markdown files\n    untracked = git_untracked_md_files(repo_path)\n\n    for file_path in untracked:\n        # Skip standard locations\n        if is_standard_location(file_path):\n            continue\n\n        # Skip standard names\n        if is_standard_name(file_path):\n            continue\n\n        score = 0\n        reasons = []\n\n        # Check naming pattern\n        if is_allcaps_nonstandard(file_path):\n            score += 3\n            reasons.append(\"ALL_CAPS non-standard name\")\n\n        # Check content markers\n        content = read_first_n_lines(file_path, 100)\n        strong, supporting = count_content_markers(content)\n\n        if strong > 0:\n            score += 3\n            reasons.append(f\"Strong markers: {strong}\")\n\n        if supporting >= 2:\n            score += 2\n            reasons.append(f\"Supporting markers: {supporting}\")\n\n        # Threshold: score >= 2\n        if score >= 2:\n            candidates.append(CandidateFile(\n                path=file_path,\n                score=score,\n                reasons=reasons\n            ))\n\n    return sorted(candidates, key=lambda c: c.score, reverse=True)\n```\n\n## Output Format\n\n```markdown\n## Detected Candidates\n\n| File | Score | Reasons |\n|------|-------|---------|\n| API_REVIEW_REPORT.md | 6 | ALL_CAPS name, Strong markers: 3, Supporting: 4 |\n| REFACTORING_REPORT.md | 5 | ALL_CAPS name, Strong markers: 2 |\n| analysis-notes.md | 2 | Supporting markers: 3 |\n\nProceeding with 3 candidates...\n```\n\n## Edge Cases\n\n### Nested untracked directories\nIf an entire directory is untracked, scan all `.md` files within:\n```bash\ngit status --porcelain | grep '^??' | while read status path; do\n  if [ -d \"$path\" ]; then\n    find \"$path\" -name \"*.md\"\n  fi\ndone\n```\n\n### Partially staged files\nFiles that are partially staged (`MM` or `AM` status) should be flagged for user attention - they may contain mixed committed/uncommitted content.\n\n### Renamed/moved files\nIf git shows a rename (`R` status), check if the destination is a standard location. If moving TO a standard location, not a candidate.\n\n## Validation\n\nBefore proceeding, confirm candidates with user:\n\n```markdown\nFound 3 consolidation candidates:\n\n1. **API_REVIEW_REPORT.md** (score: 6)\n   - ALL_CAPS non-standard name\n   - Contains: Executive Summary, Findings, Action Items\n\n2. **REFACTORING_REPORT.md** (score: 5)\n   - ALL_CAPS non-standard name\n   - Contains: Summary, Conclusion\n\n3. **analysis-notes.md** (score: 2)\n   - Contains: Multiple tables, checkbox lists\n\nAnalyze these files for consolidation? [Y/n/select specific]\n```\n\nFile v1.9.14:modules/content-analysis.md\n\n# Content Analysis Module\n\nExtracts and categorizes valuable content from candidate files.\n\n## Content Categories\n\n### Category Definitions\n\n| Category | Description | Indicators |\n|----------|-------------|------------|\n| **Actionable Items** | Tasks, TODOs, next steps that require action | `Action Items`, `Next Steps`, `TODO`, `- [ ]` checkboxes |\n| **Decisions Made** | Architecture choices, tradeoffs, rationale | `Decision`, `Chose`, `Tradeoff`, `Rationale`, `Why we` |\n| **Findings/Insights** | Audit results, analysis conclusions, observations | `Findings`, `Observations`, `Analysis`, `Discovered`, `Noted` |\n| **Metrics/Baselines** | Quantitative data, before/after, benchmarks | Tables with numbers, `Before`, `After`, percentages, `Improvement` |\n| **Migration Guides** | Step-by-step procedures, how-to instructions | `Steps`, `How to`, `Migration`, numbered lists with commands |\n| **API Changes** | Interface modifications, breaking changes, deprecations | `API`, `Breaking`, `Deprecated`, `New endpoint`, `Removed` |\n\n### Extraction Process\n\nFor each candidate file:\n\n1. **Parse structure** - Identify sections by headers (`##`, `###`)\n2. **Extract chunks** - Each section becomes a content chunk\n3. **Categorize** - Match chunk to best-fit category\n4. **Score value** - Assess high/medium/low\n\n## Value Scoring\n\n### High Value\nContent that is:\n- **Specific**: Contains concrete names, paths, numbers\n- **Actionable**: Reader can act on it directly\n- **Unique**: Not already documented elsewhere\n\nExamples:\n- Specific action items with owners\n- Concrete metrics (before: 287 lines, after: 255 lines)\n- Explicit decisions with rationale\n- Step-by-step procedures that worked\n\n### Medium Value\nContent that is:\n- **Somewhat specific**: General guidance with some detail\n- **Reference-worthy**: Useful for future lookups\n- **Partially covered**: Extends existing documentation\n\nExamples:\n- General recommendations without specifics\n- Findings that align with existing docs\n- Metrics without clear baseline comparison\n\n### Low Value\nContent that is:\n- **Generic**: Could apply to any project\n- **Redundant**: Already well-documented elsewhere\n- **Ephemeral**: Only relevant to the moment\n\nExamples:\n- Executive summaries (usually boilerplate)\n- Generic best practice reminders\n- Status statements (\"The review is complete\")\n\n## Chunk Extraction Algorithm\n\n```python\ndef extract_chunks(content: str) -> list[ContentChunk]:\n    chunks = []\n    current_section = None\n    current_content = []\n\n    for line in content.split('\\n'):\n        # New section header\n        if line.startswith('## '):\n            if current_section:\n                chunks.append(make_chunk(current_section, current_content))\n            current_section = line[3:].strip()\n            current_content = []\n        elif line.startswith('### '):\n            # Subsection - append to current or create new\n            if current_section:\n                current_content.append(line)\n            else:\n                current_section = line[4:].strip()\n                current_content = []\n        else:\n            current_content.append(line)\n\n    # Don't forget last section\n    if current_section:\n        chunks.append(make_chunk(current_section, current_content))\n\n    return chunks\n\ndef make_chunk(header: str, content: list[str]) -> ContentChunk:\n    text = '\\n'.join(content).strip()\n    category = categorize(header, text)\n    value = score_value(text, category)\n\n    return ContentChunk(\n        header=header,\n        content=text,\n        category=category,\n        value=value\n    )\n```\n\n## Categorization Rules\n\nMatch in order (first match wins):\n\n```python\nCATEGORY_PATTERNS = {\n    'actionable': [\n        r'action\\s*items?',\n        r'next\\s*steps?',\n        r'todo',\n        r'tasks?',\n        r'- \\[ \\]',  # Unchecked checkboxes\n    ],\n    'decisions': [\n        r'decision',\n        r'chose|chosen',\n        r'tradeoff',\n        r'rationale',\n        r'why\\s+we',\n        r'approach',\n    ],\n    'findings': [\n        r'finding',\n        r'observation',\n        r'analysis',\n        r'discovered',\n        r'audit',\n        r'review\\s+result',\n    ],\n    'metrics': [\n        r'\\d+%',\n        r'before.*after',\n        r'improvement',\n        r'reduction',\n        r'benchmark',\n        r'\\|\\s*\\d+\\s*\\|',  # Table with numbers\n    ],\n    'migration': [\n        r'migration',\n        r'step\\s*\\d',\n        r'how\\s+to',\n        r'procedure',\n        r'```bash',  # Code blocks with commands\n    ],\n    'api_changes': [\n        r'api',\n        r'breaking\\s+change',\n        r'deprecat',\n        r'endpoint',\n        r'interface',\n    ],\n}\n```\n\n## Output Format\n\n```markdown\n## Content Analysis: API_REVIEW_REPORT.md\n\n### Extracted Chunks\n\n| # | Section | Category | Value | Size |\n|---|---------|----------|-------|------|\n| 1 | Executive Summary | findings | low | 234 chars |\n| 2 | API Surface Inventory | findings | high | 1,847 chars |\n| 3 | Consistency Audit Findings | findings | high | 2,103 chars |\n| 4 | Action Items | actionable | high | 1,456 chars |\n| 5 | Recommendations | actionable | medium | 892 chars |\n| 6 | Conclusion | findings | low | 312 chars |\n\n### High-Value Content (4 chunks)\n- API Surface Inventory: Detailed plugin API table\n- Consistency Audit Findings: Specific issues with examples\n- Action Items: Concrete tasks with priorities\n- (included in routing)\n\n### Excluded (Low Value)\n- Executive Summary: Generic overview\n- Conclusion: Status statement only\n```\n\n## Special Handling\n\n### Tables\nPreserve markdown tables intact - they often contain valuable structured data:\n```python\ndef is_table(lines: list[str]) -> bool:\n    return any('|' in line and line.count('|') >= 2 for line in lines)\n```\n\n### Code Blocks\nPreserve code blocks, especially those showing:\n- Configuration examples\n- Command sequences\n- Before/after code comparisons\n\n### Checklists\nPreserve checkbox lists - they indicate actionable items:\n```markdown\n- [x] Completed item (historical record)\n- [ ] Pending item (action needed)\n```\n\n### Cross-references\nNote internal references for destination routing:\n```python\n# Links like \"See also: docs/security.md\" suggest destinations\nREFERENCE_PATTERN = r'see\\s+(?:also:?\\s*)?([^\\s,]+\\.md)'\n```\n\nFile v1.9.14:modules/destination-routing.md\n\n# Destination Routing Module\n\nMaps extracted content chunks to appropriate destinations in the documentation.\n\n## Routing Strategy\n\n### Priority Order\n\n1. **Semantic match** - Find existing doc that covers the topic\n2. **Default mapping**\n - Use category-based default destinations\n3. **Create new** - Only when no suitable destination exists\n\n### Preference: Existing Over New\n\nAlways prefer merging into existing documentation:\n- Keeps documentation consolidated\n- Avoids duplicate coverage\n- Maintains established structure\n\nCreate new files only when:\n- Content is substantial (>500 chars of high-value)\n- No existing doc covers the topic\n- Content warrants standalone treatment\n\n## Semantic Matching\n\n### Algorithm\n\n```python\ndef find_semantic_match(chunk: ContentChunk, existing_docs: list[str]) -> str | None:\n    \"\"\"Find best-matching existing document for a content chunk.\"\"\"\n\n    best_match = None\n    best_score = 0\n\n    for doc_path in existing_docs:\n        doc_content = read_file(doc_path)\n        score = compute_relevance(chunk, doc_content)\n\n        if score > best_score and score >= MATCH_THRESHOLD:\n            best_match = doc_path\n            best_score = score\n\n    return best_match\n\ndef compute_relevance(chunk: ContentChunk, doc_content: str) -> float:\n    \"\"\"Score relevance of chunk to document.\"\"\"\n    score = 0.0\n\n    # Header matching (highest weight)\n    doc_headers = extract_headers(doc_content)\n    if any(similar(chunk.header, h) for h in doc_headers):\n        score += 0.4\n\n    # Keyword overlap\n    chunk_keywords = extract_keywords(chunk.content)\n    doc_keywords = extract_keywords(doc_content)\n    overlap = len(chunk_keywords & doc_keywords) / len(chunk_keywords)\n    score += overlap * 0.3\n\n    # Category alignment\n    if doc_likely_category(doc_content) == chunk.category:\n        score += 0.2\n\n    # Reference mentions\n    if chunk mentions doc_path or doc mentions chunk source:\n        score += 0.1\n\n    return s\n\nArchive v1.9.13: 7 files, 16472 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), skill-card.md (2061b), SKILL.md (9098b), _meta.json (148b)\n\nArchive v1.9.12: 7 files, 16500 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), skill-card.md (2177b), SKILL.md (9098b), _meta.json (148b)\n\nArchive v1.0.3: 7 files, 16516 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), skill-card.md (2217b), SKILL.md (9098b), _meta.json (147b)\n\nArchive v1.0.2: 7 files, 16457 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), skill-card.md (2077b), SKILL.md (9035b), _meta.json (147b)\n\nArchive v1.0.1: 6 files, 15358 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), SKILL.md (9035b), _meta.json (147b)\n\nArchive v1.0.0: 6 files, 15358 bytes\n\nFiles: modules/candidate-detection.md (4439b), modules/content-analysis.md (6241b), modules/destination-routing.md (6883b), modules/merge-execution.md (10354b), SKILL.md (9035b), _meta.json (147b)","readmeExcerpt":"Skill: doc-consolidation Owner: athola Summary: Merges ephemeral report and analysis artifacts into permanent documentation Tags: latest:1.9.19 Version history: v1.9.19 | 2026-08-26T13:20:09.156Z | user Release v1.9.19 v1.9.17 | 2026-07-30T05:40:14.609Z | user Release v1.9.17 v1.9.16 | 2026-07-14T19:57:07.565Z | user Release v1.9.16 v1.9.14 | 2026-06-30T18:05:05.720Z | user Release v1.9.14 v1.9.13 | 2026-06-27T16:22:","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"/consolidate-docs"},{"language":"text","snippet":"I have some report files that need consolidating into permanent docs."},{"language":"markdown","snippet":"# Consolidation Plan\n\n## Source: API_REVIEW_REPORT.md\n\n| Content | Category | Value | Destination | Action |\n|---------|----------|-------|-------------|--------|\n| API inventory | Findings | High | docs/api-overview.md | Create |\n| Action items | Actionable | High | docs/plans/2025-12-06-api.md | Create |\n\n### Post-Consolidation\n- Delete: API_REVIEW_REPORT.md\n\nProceed with consolidation? [Y/n]"},{"language":"python","snippet":"# plugins/sanctum/scripts/consolidation_planner.py handles:\n- scan_for_candidates()\n- extract_content_chunks()\n- categorize_chunks()\n- score_value()\n- find_semantic_matches()"},{"language":"text","snippet":"User: I have API_REVIEW_REPORT.md from our review session. Can you consolidate it?\n\nClaude: I'll consolidate the report into permanent documentation.\n\n**Phase 1: Triage**\n\nScanning for candidates...\n- Found: API_REVIEW_REPORT.md (untracked, LLM markers detected)\n\nAnalyzing content...\n- Extracted 6 content chunks\n- High value: API inventory, action items, CLI recommendation\n- Medium value: security assessment\n- Low value: executive summary (generic)\n\nRouting destinations...\n- API inventory → docs/api-overview.md (CREATE)\n- Action items → docs/plans/2025-12-06-api-consistency.md (CREATE)\n- CLI naming → docs/adr/0002-cli-naming.md (CREATE ADR)\n- Security → Already covered in docs/security.md (SKIP)\n\n**Consolidation Plan**\n[table shown]\n\nProceed? [Y/n]\n\nUser: Y\n\nClaude: **Phase 2: Executing**\n\nCreated: docs/api-overview.md (1,247 bytes)\nCreated: docs/plans/2025-12-06-api-consistency.md (892 bytes)\nCreated: docs/adr/0002-2025-12-06-cli-naming.md (634 bytes)\nDeleted: API_REVIEW_REPORT.md\n\nConsolidation complete. Review the created files and commit when ready."},{"language":"bash","snippet":"# Find untracked .md files\ngit status --porcelain | grep '^??' | grep '\\.md$'"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: doc-consolidation\ndescription: Merges ephemeral report and analysis artifacts into permanent documentation\nversion: 1.9.8\ntriggers:\n  - docs\n  - consolidation\n  - cleanup\n  - git-hygiene\n  - knowledge-management\n  - LLM-generated markdown files have accumulated and need consolidation\nmetadata: {\"openclaw\": {\"homepage\": \"https://github.com/athola/claude-night-market/tree/master/plugins/sanctum\", \"emoji\": \"\\ud83d\\udcdd\"}}\nsource: claude-night-market\nsource_plugin: sanctum\n---\n\n> **Night Market Skill** — ported from [claude-night-market/sanctum](https://github.com/athola/claude-night-market/tree/master/plugins/sanctum). For the full experience with agents, hooks, and commands, install the Claude Code plugin.\n\n\n## Table of Contents\n\n- [When to Use](#when-to-use)\n- [Quick Start](#quick-start)\n- [Two-Phase Workflow](#two-phase-workflow)\n- [Phase 1: Triage (Fast Model)](#phase-1:-triage-(fast-model))\n- [Phase 2: Execute (Main Model)](#phase-2:-execute-(main-model))\n- [Workflow Details](#workflow-details)\n- [Step 1: Candidate Detection](#step-1:-candidate-detection)\n- [Step 2: Content Analysis](#step-2:-content-analysis)\n- [Step 3: Destination Routing](#step-3:-destination-routing)\n- [Step 4: Generate Plan](#step-4:-generate-plan)\n- [Source: API_REVIEW_REPORT.md](#source:-api_review_reportmd)\n- [Post-Consolidation](#post-consolidation)\n- [Step 5: Execute Merges](#step-5:-execute-merges)\n- [Fast Model Delegation](#fast-model-delegation)\n- [Content Categories](#content-categories)\n- [Merge Strategies](#merge-strategies)\n- [Intelligent Weave](#intelligent-weave)\n- [Replace Section](#replace-section)\n- [Append with Context](#append-with-context)\n- [Create New File](#create-new-file)\n- [Integration](#integration)\n- [Example Session](#example-session)\n- [Troubleshooting](#troubleshooting)\n- [No candidates found](#no-candidates-found)\n- [Low-quality extractions](#low-quality-extractions)\n- [Merge conflicts](#merge-conflicts)\n- [Related Skills](#related-skills)\n\n\n# Doc Consolidation\n\nExtracts valuable knowledge from ephemeral LLM outputs and merges it into permanent documentation.\n\n## When To Use\n\nUse this skill when:\n- You have untracked `*_REPORT.md` or `*_ANALYSIS.md` files from Claude sessions\n- Git status shows markdown files that shouldn't be committed but contain useful content\n- You want to preserve insights from code reviews, refactoring reports, or API audits\n- Preparing a PR and need to clean up working artifacts\n\nDo NOT use when:\n- Files are already in proper documentation locations\n  (`docs/`, `skills/`)\n- Files are intentionally temporary scratch notes\n- User explicitly wants to preserve the original report format\n- Source files have no extractable value (pure log output)\n\n## Formatting\n\nWhen merging content into permanent documentation, follow\n`Skill(leyline:markdown-formatting)` conventions: wrap prose\nat 80 chars (prefer sentence/clause boundaries), blank lines\naround headings, ATX headings only, blank line before lists,\nand reference-"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7d107jg9jv602h9ytsegydq184a42s\",\n  \"slug\": \"nm-sanctum-doc-consolidation\",\n  \"version\": \"1.9.19\",\n  \"publishedAt\": 1787750409156\n}"},{"path":"modules/candidate-detection.md","content":"# Candidate Detection Module\n\nIdentifies markdown files that are candidates for consolidation.\n\n## Detection Signals\n\nApply signals in priority order. A file is a candidate if it matches **any** signal.\n\n### Signal 1: Git-Untracked Location (Highest Priority)\n\n```bash\n# Find untracked .md files\ngit status --porcelain | grep '^??' | grep '\\.md$'\n```\n\n**Exclude standard locations:**\n- `docs/` - Already permanent documentation\n- `skills/` - Skill definitions\n- `modules/` - Skill modules\n- `commands/` - Slash commands\n- `agents/` - Agent definitions\n- `.github/` - GitHub templates\n\n**Exclude standard names:**\n- `README.md`, `README`\n- `LICENSE.md`, `LICENSE`\n- `CONTRIBUTING.md`\n- `CHANGELOG.md`, `HISTORY.md`\n- `SECURITY.md`\n- `CODE_OF_CONDUCT.md`\n\n### Signal 2: ALL_CAPS Naming Pattern\n\nFiles with ALL_CAPS names that aren't standard conventions:\n\n```\nMATCHES (candidates):\n- API_REVIEW_REPORT.md\n- REFACTORING_REPORT.md\n- MIGRATION_ANALYSIS.md\n- AUDIT_FINDINGS.md\n- *_REPORT.md\n- *_ANALYSIS.md\n- *_REVIEW.md\n- *_FINDINGS.md\n\nEXCLUDES (not candidates):\n- README.md\n- LICENSE.md\n- CONTRIBUTING.md\n- CHANGELOG.md\n- SECURITY.md\n- CODE_OF_CONDUCT.md\n```\n\n### Signal 3: Content Markers\n\nScan first 100 lines for LLM output markers:\n\n**Strong markers (any one = candidate):**\n- `**Date**:` or `Date:` at start of line\n- `## Executive Summary`\n- `## Summary` (at document start)\n- `## Findings`\n- `## Action Items`\n- `## Recommendations`\n- `## Conclusion`\n\n**Supporting markers (need 2+ to qualify):**\n- Markdown tables with `|` columns\n- `### High Priority` / `### Medium Priority` / `### Low Priority`\n- `- [ ]` checkbox lists\n- `## 1.` numbered top-level sections\n- `**Scope**:` or `**Status**:`\n- Lines starting with status markers\n\n## Detection Algorithm\n\n```python\ndef detect_candidates(repo_path: str) -> list[CandidateFile]:\n    candidates = []\n\n    # Get untracked markdown files\n    untracked = git_untracked_md_files(repo_path)\n\n    for file_path in untracked:\n        # Skip standard locations\n        if is_standard_location(file_path):\n            continue\n\n        # Skip standard names\n        if is_standard_name(file_path):\n            continue\n\n        score = 0\n        reasons = []\n\n        # Check naming pattern\n        if is_allcaps_nonstandard(file_path):\n            score += 3\n            reasons.append(\"ALL_CAPS non-standard name\")\n\n        # Check content markers\n        content = read_first_n_lines(file_path, 100)\n        strong, supporting = count_content_markers(content)\n\n        if strong > 0:\n            score += 3\n            reasons.append(f\"Strong markers: {strong}\")\n\n        if supporting >= 2:\n            score += 2\n            reasons.append(f\"Supporting markers: {supporting}\")\n\n        # Threshold: score >= 2\n        if score >= 2:\n            candidates.append(CandidateFile(\n                path=file_path,\n                score=score,\n                reasons=reasons\n            ))\n\n    return sorted(candidates, key=lambda c: c.score, reverse=True"},{"path":"modules/content-analysis.md","content":"# Content Analysis Module\n\nExtracts and categorizes valuable content from candidate files.\n\n## Content Categories\n\n### Category Definitions\n\n| Category | Description | Indicators |\n|----------|-------------|------------|\n| **Actionable Items** | Tasks, TODOs, next steps that require action | `Action Items`, `Next Steps`, `TODO`, `- [ ]` checkboxes |\n| **Decisions Made** | Architecture choices, tradeoffs, rationale | `Decision`, `Chose`, `Tradeoff`, `Rationale`, `Why we` |\n| **Findings/Insights** | Audit results, analysis conclusions, observations | `Findings`, `Observations`, `Analysis`, `Discovered`, `Noted` |\n| **Metrics/Baselines** | Quantitative data, before/after, benchmarks | Tables with numbers, `Before`, `After`, percentages, `Improvement` |\n| **Migration Guides** | Step-by-step procedures, how-to instructions | `Steps`, `How to`, `Migration`, numbered lists with commands |\n| **API Changes** | Interface modifications, breaking changes, deprecations | `API`, `Breaking`, `Deprecated`, `New endpoint`, `Removed` |\n\n### Extraction Process\n\nFor each candidate file:\n\n1. **Parse structure** - Identify sections by headers (`##`, `###`)\n2. **Extract chunks** - Each section becomes a content chunk\n3. **Categorize** - Match chunk to best-fit category\n4. **Score value** - Assess high/medium/low\n\n## Value Scoring\n\n### High Value\nContent that is:\n- **Specific**: Contains concrete names, paths, numbers\n- **Actionable**: Reader can act on it directly\n- **Unique**: Not already documented elsewhere\n\nExamples:\n- Specific action items with owners\n- Concrete metrics (before: 287 lines, after: 255 lines)\n- Explicit decisions with rationale\n- Step-by-step procedures that worked\n\n### Medium Value\nContent that is:\n- **Somewhat specific**: General guidance with some detail\n- **Reference-worthy**: Useful for future lookups\n- **Partially covered**: Extends existing documentation\n\nExamples:\n- General recommendations without specifics\n- Findings that align with existing docs\n- Metrics without clear baseline comparison\n\n### Low Value\nContent that is:\n- **Generic**: Could apply to any project\n- **Redundant**: Already well-documented elsewhere\n- **Ephemeral**: Only relevant to the moment\n\nExamples:\n- Executive summaries (usually boilerplate)\n- Generic best practice reminders\n- Status statements (\"The review is complete\")\n\n## Chunk Extraction Algorithm\n\n```python\ndef extract_chunks(content: str) -> list[ContentChunk]:\n    chunks = []\n    current_section = None\n    current_content = []\n\n    for line in content.split('\\n'):\n        # New section header\n        if line.startswith('## '):\n            if current_section:\n                chunks.append(make_chunk(current_section, current_content))\n            current_section = line[3:].strip()\n            current_content = []\n        elif line.startswith('### '):\n            # Subsection - append to current or create new\n            if current_section:\n                current_content.append(line)\n            else:\n               "},{"path":"modules/destination-routing.md","content":"# Destination Routing Module\n\nMaps extracted content chunks to appropriate destinations in the documentation.\n\n## Routing Strategy\n\n### Priority Order\n\n1. **Semantic match** - Find existing doc that covers the topic\n2. **Default mapping**\n - Use category-based default destinations\n3. **Create new** - Only when no suitable destination exists\n\n### Preference: Existing Over New\n\nAlways prefer merging into existing documentation:\n- Keeps documentation consolidated\n- Avoids duplicate coverage\n- Maintains established structure\n\nCreate new files only when:\n- Content is substantial (>500 chars of high-value)\n- No existing doc covers the topic\n- Content warrants standalone treatment\n\n## Semantic Matching\n\n### Algorithm\n\n```python\ndef find_semantic_match(chunk: ContentChunk, existing_docs: list[str]) -> str | None:\n    \"\"\"Find best-matching existing document for a content chunk.\"\"\"\n\n    best_match = None\n    best_score = 0\n\n    for doc_path in existing_docs:\n        doc_content = read_file(doc_path)\n        score = compute_relevance(chunk, doc_content)\n\n        if score > best_score and score >= MATCH_THRESHOLD:\n            best_match = doc_path\n            best_score = score\n\n    return best_match\n\ndef compute_relevance(chunk: ContentChunk, doc_content: str) -> float:\n    \"\"\"Score relevance of chunk to document.\"\"\"\n    score = 0.0\n\n    # Header matching (highest weight)\n    doc_headers = extract_headers(doc_content)\n    if any(similar(chunk.header, h) for h in doc_headers):\n        score += 0.4\n\n    # Keyword overlap\n    chunk_keywords = extract_keywords(chunk.content)\n    doc_keywords = extract_keywords(doc_content)\n    overlap = len(chunk_keywords & doc_keywords) / len(chunk_keywords)\n    score += overlap * 0.3\n\n    # Category alignment\n    if doc_likely_category(doc_content) == chunk.category:\n        score += 0.2\n\n    # Reference mentions\n    if chunk mentions doc_path or doc mentions chunk source:\n        score += 0.1\n\n    return score\n\nMATCH_THRESHOLD = 0.5  # Minimum score to consider a match\n```\n\n### Existing Doc Discovery\n\nScan these locations for potential destinations:\n\n```python\nDOC_LOCATIONS = [\n    'docs/',\n    'docs/plans/',\n    'docs/adr/',\n    'README.md',\n    'CHANGELOG.md',\n]\n\n# Plugin-specific locations\nPLUGIN_DOC_LOCATIONS = [\n    '{plugin}/docs/',\n    '{plugin}/README.md',\n]\n```\n\n## Default Mappings\n\nWhen semantic matching finds no suitable destination:\n\n| Category | Default Destination | Notes |\n|----------|---------------------|-------|\n| Actionable Items | `docs/plans/YYYY-MM-DD-{topic}.md` | New plan file |\n| Decisions Made | `docs/adr/NNNN-YYYY-MM-DD-{topic}.md` | New ADR |\n| Findings/Insights | `docs/{topic}.md` | New doc or best-effort match |\n| Metrics/Baselines | `docs/benchmarks.md` or inline | Append if exists |\n| Migration Guides | `docs/migration-guide.md` | Append section |\n| API Changes | `CHANGELOG.md` or `docs/api.md` | Prefer CHANGELOG |\n\n### Topic Extraction\n\nDerive topic slug from content:\n\n```python\ndef extract_t"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Merges ephemeral report and analysis artifacts into permanent documentation Skill: doc-consolidation Owner: athola Summary: Merges ephemeral report and analysis artifacts into permanent documentation Tags: latest:1.9.19 Version history: v1.9.19 | 2026-08-26T13:20:09.156Z | user Release v1.9.19 v1.9.17 | 2026-07-30T05:40:14.609Z | user Release v1.9.17 v1.9.16 | 2026-07-14T19:57:07.565Z | user Release v1.9.16 v1.9.14 | 2026-06-30T18:05:05.720Z | user Release v1.9.14 v1.9.13 | 2026-06-27T16:22:","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1123,"uniquenessScore":54,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T05:42:52.159Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T05:42:52.159Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T10:59:46.475Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}