Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 300 additions & 1 deletion .github/workflows/docs.yml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ endfunction()

if (BOOST_CAPY_MRDOCS_BUILD)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/mrdocs.cpp"
"#include <boost/capy.hpp>\n")
"#include <boost/capy.hpp>\n#include <boost/capy/test.hpp>\n")
add_library(boost_capy_mrdocs "${CMAKE_CURRENT_BINARY_DIR}/mrdocs.cpp")
boost_capy_setup_properties(boost_capy_mrdocs)
target_compile_definitions(boost_capy_mrdocs PUBLIC BOOST_CAPY_MRDOCS)
Expand Down
402 changes: 402 additions & 0 deletions DOC_AUDIT_PHASE4_EXIT.md

Large diffs are not rendered by default.

271 changes: 271 additions & 0 deletions DOC_AUDIT_REFERENCE.md

Large diffs are not rendered by default.

452 changes: 452 additions & 0 deletions DOC_IMPROVEMENT_PLAN.md

Large diffs are not rendered by default.

370 changes: 370 additions & 0 deletions DOC_REVIEW_FEEDBACK.md

Large diffs are not rendered by default.

348 changes: 348 additions & 0 deletions DOC_STYLE_GUIDE.md

Large diffs are not rendered by default.

106 changes: 106 additions & 0 deletions doc-prompts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Documentation Prompt Collection

A collection of structured prompts (in the `tools-public` house style) that generate,
repair, and audit Capy/Corosio documentation. The prompts do the work a linter cannot:
they own the **judgment rules** — Diátaxis mode purity, duplication, objectiveness,
pedagogy, and code↔doc drift — while deterministic tooling owns the mechanical rules.

**Two surfaces.** Documentation lives as exposition `.adoc` pages *and* as reference
docstrings in the headers (MrDocs generates the reference from them). Every tool applies the
five axes to both: `doc-audit` gains a reference mode (fixed Diátaxis mode = `reference`,
axes remapped to the docstring contract); `doc-write`/`doc-fix` accept a header `target_file`
and write/repair the docstring in the `.hpp`; `doc-sync` is the natural home for reference
drift — a changed symbol and its docstring share one diff, so the co-located docstring is
always its first drift hit. **A reference edit lands in the `.hpp`, never a generated page.**
Docstring conventions follow the `boost-docs` skill.

## The two ends of the pipeline

```
GENERATE ─────────────────────────────► DETECT
doc-write doc-sync doc-audit
(new page to spec) (repair code-change (score existing
drift) pages on 5 axes)
│ │
└──────► doc-fix ◄──────┘
(apply grounded edits
from findings)
```

| Tool | End | Trigger | Output |
|---|---|---|---|
| **doc-write** | generate | a symbol/feature/topic to document | a new `.adoc` page + compiled snippet(s) + nav entry |
| **doc-sync** | detect + fix | a code change (diff / commit range) | edits that repair docs the change made stale |
| **doc-audit** | detect | existing or new pages | ranked findings on the five axes |
| **doc-fix** | fix | findings (from `doc-audit` or `doc-sync`) | a minimal, grounded patch set |

`doc-sync` and `doc-audit` both hand their findings to `doc-fix` for repair, so the edit
contract lives in one place.

## Shared rubric

Every tool references one source of truth: the project **documentation style guide**
(`DOC_STYLE_GUIDE.md`). The style guide defines:

- the **five axes** — **St**ructure, **Ac**curacy, **Wo**rding, **Co**mpleteness & Pedagogy,
**Pr**esentation & Tooling;
- the **Diátaxis** mode taxonomy (tutorial / how-to / reference / explanation);
- the **single-source-of-truth** rules (link the reference via `cpp:`; include compiled
snippets, never paste code);
- the **terminology table** (one term per concept).

The tools cite the guide by section (e.g. "style guide C.1" for terminology) rather than
restating it, so the rubric never drifts from the guide.

## Division of labor — what these tools do NOT do

Deterministic tooling owns the mechanical rules and is a separate CI gate:

- **Vale** — banned words, sentence length, terminology substitutions.
- **snippet-compile job** — every documentation code block compiles against the real API.
- **structural lint script** — mode-attribute presence, nav position, "no raw `[source]`
blocks", "every concept page has an `include::example$`".

These prompts assume those gates exist and target only what they cannot check.

## Shared invariants (every tool obeys)

1. **Raw code and raw page prose never enter the main context.** Sub-agents read from disk;
the main context orchestrates over structured JSON records only.
2. **Every claim is grounded.** A statement about the API is backed by a verbatim quote of
the real declaration or reference; no claim is invented.
3. **Single source.** Code blocks are `include::example$…[tag=…]` of compiled sources, never
hand-typed. Signatures are `cpp:` links, never restated in prose.
4. **Noise floor.** A finding or edit without a verbatim span is discarded. Subjective
preference is not a finding. Doing nothing is a valid outcome.

## Sub-agent dispatch contract

Invariant 1 above is a promise; this section is the mechanism that keeps it, stated fully
inside this collection (no external tool or file is required to understand or run it).

1. **Step 0 is deterministic and runs in the orchestrator** (what each tool calls "the main
context") — no LLM call. It only computes paths, an inventory, a change set, or a brief
from arguments and file **listings** (names, diff stats) — never file **contents**.
2. **One sub-agent per unit** — one page, one symbol, one doc hit, one finding, one edit. The
orchestrator's dispatch to that sub-agent carries only identifiers already produced by a
prior step: a `path`/`symbol`, and the prior step's typed JSON record. It never carries
file contents, because the orchestrator never held any to begin with.
3. **The sub-agent is the only actor that reads raw content.** It opens the file(s) itself,
from disk, using its own tools. Whatever it reads exists only inside that sub-agent's own
context — the orchestrator has no channel into it.
4. **The sub-agent's only return value is its step's typed JSON record**, validated against
that step's schema before the orchestrator accepts it. A record's only raw-text fields are
the short, capped verbatim spans the schema itself demands (`span`, `source_span`,
`evidence` — ≤ 200–300 chars, with the single stated C1/C2 exception) — never the
surrounding page, docstring, or diff hunk.
5. **The adversarial challenge/verify stage is a separate sub-agent dispatch**, not a
continuation of the authoring sub-agent's context. It receives the same kind of
identifier-plus-record input, re-reads the file from disk **independently**, and returns
its own typed verdict record. It never receives the first sub-agent's raw reading — only
its claim.
6. **Consequence:** the orchestrator's own context, across an entire run, contains nothing
but paths, counts, and validated JSON records carrying capped verbatim spans. There is no
step at which an orchestrator instruction says "read this file and show me its contents" —
every read happens inside a sub-agent whose one output channel is the schema. Raw code and
raw prose structurally cannot reach the orchestrator under this contract.
244 changes: 244 additions & 0 deletions doc-prompts/doc-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
---
description: Documentation audit against the five documentation axes
---

# Documentation Audit

Audits Antora documentation pages **and** public-header docstrings against the five axes
defined in the documentation style guide — **Structure, Accuracy, Wording, Completeness &
Pedagogy, Presentation & Tooling**. Takes one or more `.adoc` pages (**exposition mode**,
Diátaxis-classified) or `include/boost/capy/**` headers (**reference mode**, Diátaxis mode
fixed to `reference`), and runs a structured analysis pipeline. Sub-agents read all prose or
docstrings and perform all scoring. The main context orchestrates, filters, and renders the
report. Raw page prose and raw docstring text never enter the main context.

**Noise philosophy:** The tool goes out of its way not to find anything. Every finding must
justify its existence against a style-guide rule with a verbatim quoted span. The default
posture is: this page is fine until proven otherwise. A page that scores clean on all axes
is a valid and desirable outcome, not a failure of the tool. Subjective preference is not a
finding.

\newpage

```mermaid
flowchart TD
Paths --> Inventory
Inventory --> Classify
Classify --> Score
Score --> Challenge
Challenge --> Synthesize
Synthesize --> Report
```

\newpage

---

## Core Rule

Raw page prose or docstring text NEVER enters the main context. All reading and scoring
happen inside sub-agents; the main context receives only structured JSON records. A finding
without a verbatim quoted span is discarded. Non-negotiable.

The five axes are the ONLY axes. The authoritative definitions live in the style guide; the
summaries below are for the sub-agent's convenience. Cite the specific rule (e.g. "C5",
"B1", "D2") in each finding.

- **St — Structure.** Diátaxis mode purity (one mode per page); dependency-correct ordering;
no duplication of another page; no reproduction of reference signatures (style guide A, B).
- **Ac — Accuracy.** Every claim correct and verifiable against code/reference; no drift.
(Example *compilation* is a CI gate, not this tool.)
- **Wo — Wording.** Judgment-level prose rules a linter cannot check: undefined terms,
decorative metaphor/cliché, tone, unnecessary negatives (style guide C5–C7).
- **Co — Completeness & Pedagogy.** Goal-oriented not syntax-first; a concept page shows the
library's own type running; non-obvious choices state rationale; thread-safety/affinity
documented (style guide D).
- **Pr — Presentation & Tooling.** Prose links the reference via `cpp:` rather than restating
it (style guide E1). Nav/ToC/theme/reference-grouping are owned by the build, not here.

**Reference mode remaps every axis to the docstring contract** (fixed Diátaxis mode =
`reference`); see "Reference Mode" below for the concrete per-axis rules.

---

## Step 0 - Inventory

Runs in main context. No LLM. Deterministic.

**Input:** paths — files/directories under `doc/modules/ROOT/pages` (**exposition mode**),
or files/directories under `include/boost/capy/**` (**reference mode**).

**Actions (exposition mode):** expand directories to `.adoc`; exclude partials (`_*.adoc`),
nav, generated reference. Attach `nav_position` and `declared_mode` (the `:page-mode:`
attribute, or null).

**Actions (reference mode):** expand directories to headers; exclude `detail/`, `impl/`,
`experimental/detail/`, and any public declaration with **no** docstring at all (an
undocumented declaration is the MrDocs no-warnings gate's job, not this tool's — see "Not
this tool's job"). One entry per documented public declaration, keyed by its qualified
`symbol` and the verbatim Doxygen block immediately preceding it.

**Output:** `DiscoveryResult` — `page_entries[]` of `{ path, unit_kind, nav_position,
declared_mode }` for `unit_kind=page`, or `{ path, unit_kind, symbol }` for
`unit_kind=docstring`. Inform the user: "[N] pages / [M] docstrings under audit."

---

## Step 1 - Classify

One sub-agent per page. Determine the page's true Diátaxis mode from content.

**Reference mode (`unit_kind=docstring`) skips this step.** A docstring's Diátaxis mode is
fixed to `reference` by definition (style guide Part A) — set `inferred_mode=reference`,
`declared_mode=reference`, `mode_mismatch=false`, and go straight to Step 2.

**Return:** `ClassifyRecord`

- `path`: string
- `inferred_mode`: one of `tutorial`, `how-to`, `reference`, `explanation`, `mixed`
- `declared_mode`: string or null
- `mode_mismatch`: boolean — `true` only when `declared_mode` is **non-null** and disagrees
with `inferred_mode`, or `inferred_mode` is `mixed`. **A `null` `declared_mode` is never a
mismatch** — an undeclared `:page-mode:` is the deterministic lint script's gate (A1; see
README "Division of labor"), not this tool's judgment call, regardless of how many pages in
the target corpus currently declare one. (Capy's own corpus went from 1 of 65 pages declaring
`:page-mode:` to 65 of 65 over the course of this plan; the rule above did not change and must
not be re-tuned to a snapshot count — a corpus in either state defers presence-checking to A1,
never to this tool's judgment.)
- `topic`: string, **one sentence** — the concept the page teaches
- `approx_word_count`: integer

**Validation:** reject invalid JSON, unknown enum, `topic` > 200 chars. If two pages share a
`topic`, flag a duplication candidate for Step 2.

---

## Step 2 - Score

One sub-agent per page. Score the five axes in a single read.

**Input:** `ClassifyRecord` + any duplication-candidate paths. Sub-agent reads from disk.

**Return:** `PageScore`

- `path`: string
- `axes[]`: exactly five, one per `St`,`Ac`,`Wo`,`Co`,`Pr`, each:
- `axis`: one of `St`,`Ac`,`Wo`,`Co`,`Pr`
- `grade`: one of `clean`, `minor`, `major`
- `findings[]`: **at most 4 per axis**, each:
- `span`: **verbatim quote** (≤ 200 chars, **except** a `C1`/`C2` sentence-length finding,
whose `span` is the full offending sentence even past 200 chars — truncating a run-on
sentence deletes the clause that proves the violation). Required.
- `rule`: string — the style-guide rule id (e.g. `C5`, `D2`).
- `problem`: string, **one sentence**.
- `fix`: string, **one sentence** — the concrete edit.
- `confidence`: one of `high`, `medium`, `low`.
- `runnable_example_present`: boolean — feeds the `Co` grade for concept pages. **D2 scope:**
applies hard to tutorial/how-to concept pages (a type must be shown *actually running*, and
a claimed program output must trace to a compiled `main`/test harness — see the dry-run
finding below). For `explanation`/design-essay pages, compiled snippets that illustrate
mechanism (no claimed program output) satisfy the single-source rule without D2's stronger
"shown running" bar — do not force-fail an essay for lacking one.

**Wording exemption:** text inside an attributed `[quote]` block or a `role=external`/
`role=pseudocode` code block is not the page author's prose — exclude it from `Wo`-axis and
terminology checks (rewriting a citation to match house terminology misquotes the source).

**Presentation scope (`Pr`/E1/B1):** flag a hand-typed **signature** (a restated parameter
list or return type) or a restated concept definition, not every backtick-quoted type name in
casual prose. (Confirmed against the corpus: `cpp:` is established house convention — 51 of
65 pages use it, 555 uses across 93 distinct targets. This is no longer an adoption backlog,
so the noise floor tightens: a bare backtick-quoted reference to a type or member that has a
resolvable `cpp:` target is in scope for a finding, same as a hand-typed signature. Casual
prose that names a concept with no corresponding reference target remains out of scope —
still don't flag every backtick-quoted word.)

**Validation:** reject if any finding lacks a `span`; if a `span` is not verbatim in the
file; if > 4 findings per axis; if the axes are not exactly `St,Ac,Wo,Co,Pr`.

### Reference mode — axis remap (docstrings)

Fixed Diátaxis mode = `reference`. The five axes remap to the docstring contract (align with
the `boost-docs` skill's Doxygen conventions):

- **St** — structural completeness: brief (implicit first sentence) present; no stray
`\`-commands mixed with `@`-commands; no paragraph stranded inside or after a `@par` block.
**No section-ordering rule** — retired: the corpus splits 65/26 in favor of `@par` *before*
the first `@param`/`@tparam`/`@return` (house convention is the "violating" form, 71/29),
and MrDocs 0.8.0 normalizes section order in the rendered output regardless of source order
(e.g. `task.hpp`'s `await_resume` writes `@return` before `@par Exception Safety`, but the
rendered page shows Exception Safety before Return Value) — so there is no stable target to
score source order against.
- **Ac** — docstring↔code accuracy: every actual parameter has a matching `@param`, same
name, same order; `@return` present iff the function returns non-`void`; `@throws` matches
what the code can actually throw (a `noexcept` function carries no `@throws`); a
`requires`/concept constraint on a template parameter is reflected in prose or `@par
Requires`.
- **Wo** — same STE-derived prose rules (C1–C10), scoped to the docstring's own sentences
(excluding `@code`/`@endcode`).
- **Co** — completeness & pedagogy remapped to docstring-contract completeness: `@param`/
`@return`/`@throws` coverage; thread-safety documented (`@par Thread Safety`) where not
obviously single-threaded; the template constraint's *purpose* is explained, not only
stated; `@par Example` present where feasible.
- **Pr** — **MrDocs render check**: valid Doxygen command syntax (no unclosed `@code`, no
`@param` naming a parameter that does not exist); the brief describes behavior, not
identity, and does not restate the declaration (style guide B4).

**Known gap — `Ac` has no lettered rule.** Unlike `St`, `Co`, and `Pr` above, reference-mode
`Ac` doesn't map onto any lettered clause in the style guide: Part A/B are written for
exposition-mode structure and briefs, not a per-axis accuracy contract for docstrings. Past
audits have cited `B4` (written for class-brief identity-vs-behavior, not factual accuracy)
to justify a plain factual-error finding under `Ac` — that's a stretch, not a real citation.
Until the style guide settles this (a possible Part B accuracy rule, or an explicit blessing
of the axis id as its own citation — out of scope here, see Task 1b), citing the bare axis id
(`Ac`) as a reference-mode finding's `rule` is acceptable. Do not discard an otherwise-valid
`Ac` finding for lacking a lettered citation the style guide does not currently provide.

A docstring finding's `span` is a verbatim quote from the header (same 200-char rule and the
same C1/C2 exception as exposition mode). Reference-mode findings feed `doc-fix`/`doc-sync`
exactly like exposition findings — the repair still lands in the `.hpp`, never a generated
page (see `doc-fix`/`doc-write`).

---

## Step 3 - Challenge

One adversarial sub-agent per page with any `major` grade. Its job is to REFUTE findings.

**Return:** `ChallengeRecord` — `verdicts[]` of `{ span, survives, reason }`.
**Rule:** default `survives=false` when uncertain. Main drops every non-surviving finding.

---

## Step 4 - Synthesize

Main context, surviving records only. Roll up per-page grades to an axis line
(`St:major Ac:clean Wo:minor Co:major Pr:minor`); rank pages by `3×major + minor`; merge
duplication candidates into one cross-page Structure finding.

**Output — Report** (feeds `doc-fix` directly):

```
## Documentation Audit
| Page / Symbol | St | Ac | Wo | Co | Pr | Mode ok? |
|------|----|----|----|----|----|----------|

### <path> — St:major, Co:major
- **[St · A2]** "<span>" — <problem> -> <fix> (high)
```

Reference-mode rows use `<path>::<symbol>` (e.g. `include/boost/capy/ex/run_async.hpp::
run_async(Ex,H1)`) in the Page/Symbol column; `Mode ok?` is always `yes` (mode is fixed).

End with: "[P] pages audited, [D] docstrings audited, [C] clean, [F] findings across the five
axes."

---

## Not this tool's job

Compilation (CI), mechanical prose lint and structural/nav checks (Vale + lint script), and
**rewriting** (`doc-fix` consumes this report). This tool judges only what a linter cannot.
For the reference surface: a public declaration with **no** docstring at all is the MrDocs
no-warnings gate's job (Task 2), not a finding here — this tool judges whether an *existing*
docstring is complete, accurate, and well-worded, not whether one exists.
Loading
Loading