diff --git a/.clean-docs.yml b/.clean-docs.yml index a279ddd..aa8e114 100644 --- a/.clean-docs.yml +++ b/.clean-docs.yml @@ -88,6 +88,51 @@ bindings: doc: docs/SECURITY_MODEL.md anchor: process-controls source: {path: src/clean_docs/isolation.py, symbol: run_isolated_process} + - id: tutorial-outcomes + type: region + doc: docs/learn/tutorial-catch-a-lying-doc.md + region: tutorial-outcomes + extractor: json + source: + path: .clean-docs/learning/tutorial-evidence.json + pointer: /steps + renderer: markdown-table + columns: [moment, command, exit, meaning] + - id: postmortem-measurements + type: region + doc: docs/learn/postmortem-the-readme-that-lied.md + region: postmortem-measurements + extractor: json + source: + path: .clean-docs/learning/ultra-csm-hygiene.json + pointer: /measurements + renderer: markdown-table + columns: [measure, before, after] + - id: postmortem-examples + type: region + doc: docs/learn/postmortem-the-readme-that-lied.md + region: postmortem-examples + extractor: json + source: + path: .clean-docs/learning/ultra-csm-hygiene.json + pointer: /examples + renderer: markdown-table + columns: [case, before, after] + - id: deterministic-seam-evidence + type: symbol + doc: docs/learn/deep-dive-the-deterministic-seam.md + anchor: evidence-authority + source: {path: src/clean_docs/engine.py, symbol: evaluate} + - id: deterministic-seam-phrasing + type: symbol + doc: docs/learn/deep-dive-the-deterministic-seam.md + anchor: phrasing-boundary + source: {path: src/clean_docs/phrasing.py, symbol: build_model_record} + - id: deterministic-seam-gate + type: symbol + doc: docs/learn/deep-dive-the-deterministic-seam.md + anchor: gate-authority + source: {path: src/clean_docs/outcomes.py, symbol: build_outcome_receipt} projections: llms_txt: output: llms.txt @@ -105,6 +150,10 @@ projections: - docs/SECURITY_MODEL.md - docs/SUPPORT.md - docs/SURFACE.md + - docs/learn/index.md + - docs/learn/tutorial-catch-a-lying-doc.md + - docs/learn/postmortem-the-readme-that-lied.md + - docs/learn/deep-dive-the-deterministic-seam.md - skill/SKILL.md bundles: - id: contributor diff --git a/.clean-docs/context/contributor.md b/.clean-docs/context/contributor.md index 74649b3..32c910d 100644 --- a/.clean-docs/context/contributor.md +++ b/.clean-docs/context/contributor.md @@ -1,13 +1,13 @@ # Context bundle: contributor - Source ref: `WORKTREE` -- Corpus sha256: `74f0d42c8d758125d6fbe9878054b8bd5abeb7b32477a26274e99f776128be01` +- Corpus sha256: `4de14e204a537a326c3881119c5d39f9efea575cf1c7355f9a573d1a8c85c279` - Content: exact canonical document bytes ## Canonical document: README.md - Source: [README.md](../../README.md) -- Content sha256: `4289b0409955e0ce025e67cdf718814f0c0d77971663fe779809a64aa8e39d05` +- Content sha256: `b3d947ea15bd363b589a256b91affbd9d954b8b78667aa34432e8d0478cc5358` # clean-docs @@ -17,6 +17,13 @@ clean-docs is a source-bound documentation engine and CLI for maintainers whose +[](https://github.com/owieschon/clean-docs/actions/workflows/ci.yml) +[](https://github.com/owieschon/clean-docs/releases/latest) +[](LICENSE) + +**[Start here](docs/learn/index.md)** for the product map, runnable drift tutorial, real-repository +postmortem, and deterministic-boundary explanation. + ## Why clean-docs exists diff --git a/.clean-docs/learning/tutorial-evidence.json b/.clean-docs/learning/tutorial-evidence.json new file mode 100644 index 0000000..595dc37 --- /dev/null +++ b/.clean-docs/learning/tutorial-evidence.json @@ -0,0 +1,35 @@ +{ + "schema": "clean-docs.tutorial-evidence.v1", + "steps": [ + { + "command": "clean-docs check", + "exit": 0, + "meaning": "The bound page matches source.", + "moment": "Protected baseline" + }, + { + "command": "clean-docs check", + "exit": 1, + "meaning": "The status-actions binding is stale.", + "moment": "Source changed alone" + }, + { + "command": "clean-docs drive", + "exit": 0, + "meaning": "Only the bound region changes.", + "moment": "Declared region repaired" + }, + { + "command": "clean-docs project", + "exit": 0, + "meaning": "llms.txt receives the repaired page digest.", + "moment": "Projection refreshed" + }, + { + "command": "clean-docs verify", + "exit": 0, + "meaning": "Bindings and projections are current.", + "moment": "Repository verified" + } + ] +} diff --git a/.clean-docs/learning/ultra-csm-hygiene.json b/.clean-docs/learning/ultra-csm-hygiene.json new file mode 100644 index 0000000..f165565 --- /dev/null +++ b/.clean-docs/learning/ultra-csm-hygiene.json @@ -0,0 +1,63 @@ +{ + "examples": [ + { + "after": "relocated into the archive convention the repo already used, history intact", + "before": "an agent-to-agent handoff sat in the tracked doc tree a reader browses", + "case": "process exhaust off the reader surface" + }, + { + "after": "only the version `src/ultra_csm/agent1/slot_b.py:38` loads remains on the surface", + "before": "four versions of one prompt tracked side by side, each a near-duplicate of the last", + "case": "one canonical home for a versioned prompt" + }, + { + "after": "each passage is equally true, with the authoring history removed", + "before": "a worldbuilding reference tagged its content with the program that authored it", + "case": "provenance stripped from a reader-facing reference" + } + ], + "measurements": [ + { + "after": "73 (all justified in `NOTES.md`)", + "before": "280", + "measure": "Total findings" + }, + { + "after": "10 (cited evidence, per `docs/README.md`)", + "before": "91", + "measure": "surface (process artifact on reader surface)" + }, + { + "after": "33 (genuine long references)", + "before": "93", + "measure": "section-length" + }, + { + "after": "7 (shared concepts across separate docs)", + "before": "54", + "measure": "near-dup" + }, + { + "after": "21 (genuine long references)", + "before": "22", + "measure": "doc-length" + }, + { + "after": "1 (a run-ledger table cell)", + "before": "11", + "measure": "provenance" + }, + { + "after": "0", + "before": "7", + "measure": "audience" + }, + { + "after": "1", + "before": "2", + "measure": "restatement" + } + ], + "schema": "clean-docs.learning-evidence.v1", + "source": "docs/archive/v0/ultra-csm-before-after.md" +} diff --git a/.clean-docs/reader-trial-rubric-v1.1.yml b/.clean-docs/reader-trial-rubric-v1.1.yml new file mode 100644 index 0000000..28d69c1 --- /dev/null +++ b/.clean-docs/reader-trial-rubric-v1.1.yml @@ -0,0 +1,30 @@ +version: 1 +context: + - README.md + - docs/learn/index.md + - docs/learn/tutorial-catch-a-lying-doc.md + - docs/learn/postmortem-the-readme-that-lied.md + - docs/learn/deep-dive-the-deterministic-seam.md +profiles: + - id: anthropic-opus-4-8 + provider: anthropic + model: Opus 4.8 + - id: codex-gpt-5-6-sol-high + provider: codex + model: GPT 5.6 Sol High +tasks: + - id: route-orientation + instruction: Starting from the README first screen and learning index, choose the page that explains what clean-docs is and why source-bound documentation matters. + passes_when: The reader chooses the README overview for product orientation, then identifies the learning index thesis that documentation drift is a source-derivation problem. + - id: route-hands-on + instruction: Starting from the learning index, choose the page for learning the repair loop by doing it. + passes_when: The reader chooses the catch-a-lying-doc tutorial and states that it establishes a baseline, creates source drift, observes failure, repairs the declared region, refreshes projections, and verifies the result. + - id: route-mechanism + instruction: Starting from the learning index, choose the page that explains who owns facts, phrasing, and the gate result. + passes_when: The reader chooses the deterministic-seam deep dive and assigns evidence selection and pass-or-fail authority to deterministic code while limiting a model to phrasing supplied evidence. + - id: run-tutorial + instruction: Run the published tutorial against the candidate in a fresh repository and record each command exit and declared file change. + passes_when: The baseline passes, deliberate source drift fails first with the named binding, drive changes the declared documentation region, project refreshes llms.txt, and final check and verify exit zero. + - id: explain-authority-boundary + instruction: Explain why a model cannot select facts, widen scope, or decide whether clean-docs passes, using only the published learning pages. + passes_when: The answer preserves the evidence-to-phrasing-to-gate sequence, names deterministic code as the authority on inputs and outcomes, and does not grant the model merge-gate authority. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25a24d9..7f3ee9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,14 @@ jobs: - run: clean-docs --root . release --from v0.4.0 --to HEAD --format json - run: python scripts/record_demo.py --out /tmp/clean-docs-demo-evidence.json - run: cmp .clean-docs/demo/evidence.json /tmp/clean-docs-demo-evidence.json + - run: python scripts/build_learning_evidence.py --out /tmp/clean-docs-learning-evidence.json + - run: cmp .clean-docs/learning/ultra-csm-hygiene.json /tmp/clean-docs-learning-evidence.json + - run: >- + python scripts/record_learning_tutorial.py + --clean-docs "$(command -v clean-docs)" + --out /tmp/clean-docs-tutorial-evidence.json + - run: cmp .clean-docs/learning/tutorial-evidence.json /tmp/clean-docs-tutorial-evidence.json + - run: python scripts/render_social_preview.py --check - run: python scripts/trusted_self_check.py public-dogfood: @@ -224,6 +232,32 @@ jobs: path: clean-docs-v10-acceptance.json if-no-files-found: error + acceptance-v1-1: + needs: test + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: "3.12" + cache: pip + - run: python -m pip install ".[dev]" + - run: >- + python scripts/run_acceptance.py + --registry tests/v11-acceptance.yml + --out clean-docs-v11-acceptance.json + - if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: clean-docs-v1.1-acceptance-${{ matrix.os }}-${{ github.sha }} + path: clean-docs-v11-acceptance.json + if-no-files-found: error + performance-v1-0: needs: test runs-on: ${{ matrix.os }} diff --git a/README.md b/README.md index 902e36c..b7836ec 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,13 @@ clean-docs is a source-bound documentation engine and CLI for maintainers whose +[](https://github.com/owieschon/clean-docs/actions/workflows/ci.yml) +[](https://github.com/owieschon/clean-docs/releases/latest) +[](LICENSE) + +**[Start here](docs/learn/index.md)** for the product map, runnable drift tutorial, real-repository +postmortem, and deterministic-boundary explanation. + ## Why clean-docs exists diff --git a/docs/RELEASES.md b/docs/RELEASES.md index 48493f1..86d61ec 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -33,4 +33,11 @@ The factual Markdown and JSON remain unchanged when narrative validation fails. Release extraction is static and snapshot-bound. First-party and configured discoverer plugins run once per ref in disposable copies. The active worktree, its installed dependencies, and narrative output cannot change the typed delta. -Release-candidate builds rehearse the published reader tasks in CI. A stable release additionally requires one content-addressed trial from each model profile declared in the rubric: Anthropic Opus 4.8, Anthropic Sonnet 5, Codex GPT 5.5 High, and Codex GPT 5.6 Sol High. Every profile runs in a fresh session with only the rubric's published context, completes every task, and binds its evidence by SHA-256. Profiles cannot share conversation history or prior trial output. The receipt also names the exact candidate commit and wheel digest. `scripts/verify_reader_trial.py` checks `.clean-docs/reader-trial.json`; `scripts/build_release.py` reconstructs the candidate wheel and permits only the version and reader receipts to differ in the stable release. It refuses absent, duplicate, substituted, incomplete, stale, tampered, or untried evidence. +Release-candidate builds rehearse the published reader tasks in CI. A stable release additionally requires one content-addressed trial from each profile in that release line's rubric: + +| Release line | Independent reader set | Evidence contract | +| --- | --- | --- | +| `1.0` | Anthropic Opus 4.8, Anthropic Sonnet 5, Codex GPT 5.5 High, and Codex GPT 5.6 Sol High | Product identification, installation, protection, repair, and limitation tasks | +| `1.1` | Anthropic Opus 4.8 and Codex GPT 5.6 Sol High | Learning-path routing, tutorial execution, and deterministic-authority tasks | + +Every profile runs in a fresh session with only the rubric's published context, completes every task, and binds its evidence by SHA-256. Profiles cannot share conversation history or prior trial output. The receipt also names the exact candidate commit and wheel digest. `scripts/verify_reader_trial.py` selects the versioned rubric and evidence namespace; `scripts/build_release.py` reconstructs the candidate wheel and permits only the version and matching reader receipts to differ in the stable release. It refuses absent, duplicate, substituted, incomplete, stale, tampered, or untried evidence. diff --git a/docs/SURFACE.md b/docs/SURFACE.md index c434821..4433ec5 100644 --- a/docs/SURFACE.md +++ b/docs/SURFACE.md @@ -9,12 +9,12 @@ The catalog binding catches additions, removals, and replacements across the det | surface | discovered | examples | | --- | ---: | --- | -| api-symbol | 175 | `AcceptanceCase`, `Assertion`, `AuditFinding`, and 172 more | +| api-symbol | 184 | `AcceptanceCase`, `Assertion`, `AuditFinding`, and 181 more | | cli-command | 19 | `audit`, `benchmark`, `check`, and 16 more | -| cli-option | 50 | `--accept-hygiene-baseline`, `--base`, `--binding`, and 47 more | +| cli-option | 55 | `--accept-hygiene-baseline`, `--base`, `--binding`, and 52 more | | package | 1 | `clean-docs` | | runtime-constraint | 1 | `Python >=3.10` | -| test-suite | 47 | `scripts/test_release_lifecycle.py`, `tests/test_audit.py`, `tests/test_changed_check.py`, and 44 more | +| test-suite | 49 | `scripts/test_release_lifecycle.py`, `tests/test_audit.py`, `tests/test_changed_check.py`, and 46 more | - + diff --git a/docs/assets/clean-docs-social.png b/docs/assets/clean-docs-social.png new file mode 100644 index 0000000..5d8d369 Binary files /dev/null and b/docs/assets/clean-docs-social.png differ diff --git a/docs/assets/clean-docs-social.svg b/docs/assets/clean-docs-social.svg new file mode 100644 index 0000000..b3a6cd5 --- /dev/null +++ b/docs/assets/clean-docs-social.svg @@ -0,0 +1,73 @@ + diff --git a/docs/learn/deep-dive-the-deterministic-seam.md b/docs/learn/deep-dive-the-deterministic-seam.md new file mode 100644 index 0000000..f35015e --- /dev/null +++ b/docs/learn/deep-dive-the-deterministic-seam.md @@ -0,0 +1,66 @@ +# Deep dive: the deterministic seam + + +This explanation is for maintainers deciding where model judgment may enter a documentation pipeline without weakening its gate. It separates evidence selection, prose phrasing, repository writes, and pass or fail authority so you can inspect the boundary in both the architecture and the implementation. + + +The short version is memorable because it is mechanical: a model may hold the pen, but it never +gets the keys. Facts enter from declared evidence, writes stay bounded to planned documents, and a +deterministic result decides whether the repository is current. + +The [product system map](../../README.md#how-the-pieces-fit) shows the complete path and its text +equivalent. This page follows the three authority handoffs inside it: evidence enters, phrasing may +change its form, and deterministic checks retain the verdict. + +## Evidence authority + +[`evaluate`](../../src/clean_docs/engine.py) owns the source-to-binding comparison. It loads the +manifest, creates a repository snapshot, dispatches only the declared extractor, renders the typed +value, and compares the result with the marked document region. Claim and symbol bindings take the +same route through typed results. + +This is the first side of the seam: source configuration selects which facts exist. A model does not +discover an extra capability, widen the selected repository scope, or turn an unsupported surface +into an accepted claim. + +## Phrasing boundary + +[`build_model_record`](../../src/clean_docs/phrasing.py) receives inventory facts that already +exist. Its prompt exposes an allowlist of prose templates and asks for fact identifiers plus template +names, not open-ended product copy. The parser rejects unknown facts, duplicate facts, unsupported +templates, malformed responses, and more than five drafts. + +The returned sentence is therefore a presentation of supplied evidence. It is not evidence. If the +provider fails, the operation stops before a repository write. The [security model](../SECURITY_MODEL.md) +defines the surrounding process and host boundaries without duplicating them here. + +## Gate authority + +[`build_outcome_receipt`](../../src/clean_docs/outcomes.py) combines deterministic audit, inventory, +binding, projection, and changed-file results. Its `ok` value follows those counts. It does not ask a +provider whether the prose sounds supported. + +That distinction keeps failure actionable. A stale region names its binding and source. A missing +symbol names its locator. A model score could express judgment about clarity, but it cannot replace +the repeatable evidence that made the check fail. + +## Why the seam is useful + +Pure generation can produce fluent documentation whose factual scope is impossible to audit. Pure +templates can keep facts current while flattening every explanation into reference prose. The seam +keeps both tools in their honest roles: + +| Responsibility | Owner | Why | +| --- | --- | --- | +| Select facts and scope | Manifest plus extractor | The input must be reproducible at a repository ref | +| Phrase supplied facts | Deterministic renderer or bounded model path | Presentation may vary without changing authority | +| Write documentation | Explicit derive or drive path | Unrelated prose must remain untouched | +| Decide pass or fail | Deterministic checks | CI needs the same answer without credentials or judgment drift | + +Use a human or model to judge motivation, pedagogy, and whether the chosen facts are enough. Use the +gate to prove that declared facts, locators, and projections still match. The seam is not a claim +that all prose is true forever. It is the line that prevents the checkable spine from drifting +silently. + +Run [the tutorial](tutorial-catch-a-lying-doc.md) to exercise that boundary, or use the +[extension guide](../EXTENSIONS.md) when a source needs a new deterministic adapter. diff --git a/docs/learn/index.md b/docs/learn/index.md new file mode 100644 index 0000000..d2eb375 --- /dev/null +++ b/docs/learn/index.md @@ -0,0 +1,30 @@ +# Learn clean-docs + + +This learning path is for maintainers deciding whether source-bound documentation fits their repository. It replaces vague confidence in prose with a mental model, a runnable drift repair, and a clear authority boundary, so you can choose the shortest page for the question in front of you. + + +Documentation drift is a source-derivation problem before it is a discipline problem. Review can +catch a stale sentence, but it cannot make the relationship between that sentence and its defining +source repeatable. clean-docs makes that relationship explicit and checks it again after the code +moves. + +The useful question is not "who forgot the docs?" It is "which source should have made this claim +fail?" Follow the path that matches your current job: + +| If you need to... | Start with | You will leave with... | +| --- | --- | --- | +| Decide what the product is | [Product overview](../../README.md) | The category, fit, architecture, and installation route | +| Protect one fact yourself | [Catch a lying doc](tutorial-catch-a-lying-doc.md) | A working binding, a failed drift check, and a repaired page | +| See the problem at repository scale | [The README that lied](postmortem-the-readme-that-lied.md) | A concrete cleanup case and the limits of its evidence | +| Understand the model boundary | [The deterministic seam](deep-dive-the-deterministic-seam.md) | A precise account of who owns facts, prose, and gate results | + +The tutorial is the fastest proof. The postmortem explains why the proof matters beyond a toy +fixture. The deep dive names the mechanism after you have seen it move. + +## What this path does not replace + +These pages teach a sequence and a mental model. Use the existing [CLI reference](../CLI.md), +[support guide](../SUPPORT.md), and [security model](../SECURITY_MODEL.md) when you need exact current +commands, installation variants, or trust boundaries. The lessons link to those canonical homes +instead of growing stale copies. diff --git a/docs/learn/postmortem-the-readme-that-lied.md b/docs/learn/postmortem-the-readme-that-lied.md new file mode 100644 index 0000000..ed85fc9 --- /dev/null +++ b/docs/learn/postmortem-the-readme-that-lied.md @@ -0,0 +1,78 @@ +# Postmortem: the README that lied + + +This case study is for maintainers whose documentation corpus feels polished but has lost a trustworthy reader path. It shows how one real repository separated current guidance from process exhaust, measured the result, and exposed which improvements still required human judgment. + + +The repository did not have one spectacularly false page. It had a quieter failure: current +guidance, superseded prompts, handoff notes, and authoring history all looked equally official. +Nothing in the prose announced which layer a reader had entered. + +This is a recorded historical cleanup of `ultra-csm`, not a claim about that repository's current +state. The typed record is derived from the archived before-and-after account at +[`docs/archive/v0/ultra-csm-before-after.md`](../archive/v0/ultra-csm-before-after.md). clean-docs +renders the tables below from that record. + +## What the scan found + +The first pass reported 280 findings across structure, duplication, length, provenance, and +audience signals: + + +| measure | before | after | +| --- | --- | --- | +| Total findings | 280 | 73 (all justified in `NOTES.md`) | +| surface (process artifact on reader surface) | 91 | 10 (cited evidence, per `docs/README.md`) | +| section-length | 93 | 33 (genuine long references) | +| near-dup | 54 | 7 (shared concepts across separate docs) | +| doc-length | 22 | 21 (genuine long references) | +| provenance | 11 | 1 (a run-ledger table cell) | +| audience | 7 | 0 | +| restatement | 2 | 1 | + + +The count fell because the reader surface got smaller and the checker lost false positives. It did +not fall because thresholds were loosened. The remaining findings were reviewed and justified in +the source repository. + +## Three changes did most of the work + +The cleanup made each document answer a harder question than "is this Markdown useful somewhere?" +It asked whether the page belonged on the current reader surface: + + +| case | before | after | +| --- | --- | --- | +| process exhaust off the reader surface | an agent-to-agent handoff sat in the tracked doc tree a reader browses | relocated into the archive convention the repo already used, history intact | +| one canonical home for a versioned prompt | four versions of one prompt tracked side by side, each a near-duplicate of the last | only the version `src/ultra_csm/agent1/slot_b.py:38` loads remains on the surface | +| provenance stripped from a reader-facing reference | a worldbuilding reference tagged its content with the program that authored it | each passage is equally true, with the authoring history removed | + + +The first change moved handoffs and status notes into the repository's archive convention. The +second left one loaded prompt version on the current surface and preserved older versions as +history. The third removed authoring-program labels from reference prose that stayed true without +them. + +The result was not tidier wording. It was a corpus whose paths had different meanings. + +## What the numbers do not prove + +A lower hygiene count does not prove that every sentence is accurate, helpful, or well taught. +Several remaining findings were legitimate long references or shared concepts, and three reductions +came from correcting the linter itself. The evidence supports a narrower claim: structural noise +became measurable, most of it was removed from the current reader path, and the exceptions became +explicit. + +That boundary matters. Corpus checks can find likely process exhaust, duplicate language, and +misplaced provenance. A source binding is still required when a specific product claim must fail +after its defining code changes. [Catch a lying doc](tutorial-catch-a-lying-doc.md) demonstrates that +second mechanism on one fact. + +## The reusable decision + +For every page, ask: if this vanished from the current documentation surface, would a reader lose +current product truth or only authoring history? Keep the first in the reader path. Preserve the +second in version history or an archive whose name makes its status clear. + +Then bind the factual spine that remains. Structure makes the right page findable; source evidence +makes its checkable claims answerable. diff --git a/docs/learn/tutorial-catch-a-lying-doc.md b/docs/learn/tutorial-catch-a-lying-doc.md new file mode 100644 index 0000000..fdd33b9 --- /dev/null +++ b/docs/learn/tutorial-catch-a-lying-doc.md @@ -0,0 +1,118 @@ +# Catch a lying doc + + +This tutorial is for maintainers who want to see one source-bound fact fail and recover in a disposable repository. It turns an easy-to-miss command rename into a named check failure, a region-only repair, and a verified projection using an installed clean-docs release. + + +A stale README keeps a straight face. This exercise gives it a tripwire. + +You will build Moonbase Status, a tiny operator guide whose public action table comes from a Python literal. No repository code is imported or executed. The full binding surface and installation variants remain in the [manifest reference](../../README.md#manifest-reference) and [support guide](../SUPPORT.md). + +## Before you begin + +Install a stable clean-docs artifact in an isolated environment and confirm `clean-docs --version` works. The [local artifact procedure](../SUPPORT.md#install-upgrade-roll-back-and-uninstall) is the offline path used to test this tutorial. You also need Git and Python 3.10 or newer. + +## 1. Build the fixture + +Create an empty repository, its source-owned action, the page, and the binding manifest: + +```bash +mkdir moonbase-status +cd moonbase-status +git init +mkdir src +cat > src/actions.py <<'PY' +ACTIONS = [ + {"command": "report", "job": "Send the current habitat status"}, +] +PY +cat > README.md <<'MD' +# Moonbase Status + + +Use this guide when a habitat operator needs the current public actions. It keeps the action table tied to source so a renamed command cannot remain plausible in the guide. + + +## Operator actions + + + +MD +cat > .clean-docs.yml <<'YAML' +version: 1 +bindings: + - id: status-actions + type: region + doc: README.md + region: status-actions + extractor: python-literal + source: {path: src/actions.py, symbol: ACTIONS} + renderer: markdown-table + columns: [command, job] +projections: + llms_txt: + output: llms.txt + title: Moonbase Status documentation + summary: Source-bound operator documentation. + include: [README.md] +YAML +``` + +## 2. Protect the baseline + +Render the declared region, generate the projection, and verify the repository: + +```bash +clean-docs drive +clean-docs project +clean-docs check +clean-docs verify +git add .clean-docs.yml README.md llms.txt src/actions.py +git commit -m "Protect operator actions" +``` + +The check exits `0`. The receipt reports `"ok": true`, and the README now contains the `report` row derived from `ACTIONS`. + +## 3. Create honest drift + +Rename the source command without touching the README: + +```bash +python3 - <<'PY' +from pathlib import Path + +path = Path("src/actions.py") +path.write_text(path.read_text().replace('"report"', '"publish"')) +PY +clean-docs check +``` + +The final command exits `1` and names `status-actions`. That failure is the lesson: the old prose still reads well, but it no longer gets to pass as current. + +## 4. Repair and verify + +Repair the bound region, inspect its exact diff, refresh the projection, and run the final gate: + +```bash +clean-docs drive +git diff -- README.md +clean-docs project +clean-docs check +clean-docs verify +``` + +`drive` changes the table row from `report` to `publish` without rewriting the surrounding prose. `project` updates the page digest in `llms.txt`. The last two commands exit `0`. + +The release-tested fixture records the same state changes: + + +| moment | command | exit | meaning | +| --- | --- | --- | --- | +| Protected baseline | clean-docs check | 0 | The bound page matches source. | +| Source changed alone | clean-docs check | 1 | The status-actions binding is stale. | +| Declared region repaired | clean-docs drive | 0 | Only the bound region changes. | +| Projection refreshed | clean-docs project | 0 | llms.txt receives the repaired page digest. | +| Repository verified | clean-docs verify | 0 | Bindings and projections are current. | + + +You have now seen the full loop. Read [the deterministic seam](deep-dive-the-deterministic-seam.md) to understand why a model can help with wording without gaining authority over this result. diff --git a/llms.txt b/llms.txt index f1fb27a..3f2a687 100644 --- a/llms.txt +++ b/llms.txt @@ -5,14 +5,18 @@ ## Canonical documentation - [CLEAN_DOCS_SPEC.md](CLEAN_DOCS_SPEC.md): declared canonical context; sha256: b0df161645ff6f5356913658789990083c09a7d041f385541df97e9e259e99d1 -- [README.md](README.md): bindings: manifest-reference, product-overview, supported-bindings; sha256: 4289b0409955e0ce025e67cdf718814f0c0d77971663fe779809a64aa8e39d05 +- [README.md](README.md): bindings: manifest-reference, product-overview, supported-bindings; sha256: b3d947ea15bd363b589a256b91affbd9d954b8b78667aa34432e8d0478cc5358 - [SECURITY.md](SECURITY.md): declared canonical context; sha256: 9b73fcc18efa2ff39d64d8271a6c33b278de5dc625d7ac7c319587edf0c31621 - [STANDARD.md](STANDARD.md): declared canonical context; sha256: 2af7b40f7ac3a91b4d3877e98b1d33420f47b36e30202eb8b493f9c1ee4d6792 - [docs/CLI.md](docs/CLI.md): bindings: cli-reference; sha256: dae82eebf8e8712dce8b4c17e480f13912155078b3e34fb0159ff2f2c45510bc - [docs/EVALUATION.md](docs/EVALUATION.md): bindings: evaluation-scorers; sha256: efbcce90691ec594da793977b33ba62add79dd47c0ac9452443e826d28e1bb2f - [docs/EXTENSIONS.md](docs/EXTENSIONS.md): bindings: extension-guide, migration-guide; sha256: a1da5683da8247c11fb9bb2d6f04c3aa30961cabe7aab5ee5791b70244835d2d -- [docs/RELEASES.md](docs/RELEASES.md): bindings: release-guide; sha256: 0c9024977bca84b77106d85b91a756bf97124638ed65601cdc79cc90bf007161 +- [docs/RELEASES.md](docs/RELEASES.md): bindings: release-guide; sha256: 44a95749c55a3989777a9c2516a1b6d7d2da02954a6565f76019f8183b574583 - [docs/SECURITY_MODEL.md](docs/SECURITY_MODEL.md): bindings: security-model; sha256: 29d9f2582186f19d72823e163512fa12f6d7459f4da273ca3d8be56d5befd2bf - [docs/SUPPORT.md](docs/SUPPORT.md): bindings: support-guide; sha256: bc0055417eb4be09a5e8722aaa0bfa7454877b3909e38a72b7d03f9fbd7576ab -- [docs/SURFACE.md](docs/SURFACE.md): bindings: repository-surface; sha256: a4dfbe4cc61e7368aedc05c4c30422fc0194fe21e8e7c0477705495c16e688bf +- [docs/SURFACE.md](docs/SURFACE.md): bindings: repository-surface; sha256: 7cc29bb2ccdf1a92a0740a114181a97ffdca8b5eb4744c64000aa6119b415c26 +- [docs/learn/deep-dive-the-deterministic-seam.md](docs/learn/deep-dive-the-deterministic-seam.md): bindings: deterministic-seam-evidence, deterministic-seam-gate, deterministic-seam-phrasing; sha256: d4dcc2aaf7e6fa917d5474bb8e265eb60c0dca7332e099c5342c5bd79fb83371 +- [docs/learn/index.md](docs/learn/index.md): declared canonical context; sha256: 54ca471c870ac4b295b0bfe72d51561891084c7483a649f3586517204ac974fb +- [docs/learn/postmortem-the-readme-that-lied.md](docs/learn/postmortem-the-readme-that-lied.md): bindings: postmortem-examples, postmortem-measurements; sha256: 2616fe1ad837581461ccbd07bcfd7e0c6aa799f762e64f40ed34a637f3460cb2 +- [docs/learn/tutorial-catch-a-lying-doc.md](docs/learn/tutorial-catch-a-lying-doc.md): bindings: tutorial-outcomes; sha256: b5aeabb02e76c64e328d637b28ee7ca8458964ca402f3ce5b915b6d10ff0c51a - [skill/SKILL.md](skill/SKILL.md): declared canonical context; sha256: 77f0b257236a184c5b202e8327c27785f3ab4abe9c1ae9c3c082f8fa06428da9 diff --git a/pyproject.toml b/pyproject.toml index 622f848..a697126 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "clean-docs" -version = "1.0.0" +version = "1.1.0rc1" description = "Keep repository documentation bound to its sources." readme = "README.md" requires-python = ">=3.10" diff --git a/scripts/build_learning_evidence.py b/scripts/build_learning_evidence.py new file mode 100644 index 0000000..0595d7c --- /dev/null +++ b/scripts/build_learning_evidence.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""Build the typed historical record used by the learning postmortem.""" + +from __future__ import annotations + +import argparse +import json +import re +from pathlib import Path + +from clean_docs.regions import atomic_write + + +ROOT = Path(__file__).resolve().parents[1] +SOURCE = ROOT / "docs/archive/v0/ultra-csm-before-after.md" +OUTPUT = ROOT / ".clean-docs/learning/ultra-csm-hygiene.json" + + +def _table_rows(text: str) -> list[dict[str, str]]: + match = re.search( + r"## The numbers\n\n\| Measure \| Before \| After \|\n" + r"\| --- \| --- \| --- \|\n(?P
(?:\|.*\|\n)+)", + text, + ) + if match is None: + raise RuntimeError("archived measurement table not found") + rows: list[dict[str, str]] = [] + for line in match.group("body").splitlines(): + measure, before, after = (cell.strip() for cell in line.strip("|").split("|")) + rows.append({"measure": measure, "before": before, "after": after}) + return rows + + +def _examples(text: str) -> list[dict[str, str]]: + sections = re.findall( + r"## Example \d+: (?P