docs: specs 009-013 — documentation restructure in response to #67 - #72
Conversation
Responds to Docs#67, which argues our documentation mixes Diataxis modes and is organised around Brighter's architecture rather than reader intent. Audit findings that shaped the specs: - No tutorial exists. The Overview section is a showcase, a glossary and a philosophy piece; none of them gets a reader to something running. - Transports (RabbitMQ, Kafka, SNS/SQS, ASB) are filed under a delivery guarantee, "Guaranteed At Least Once". - Explanatory material is scattered across five separate sections. - "Reference" contains one page; two glossaries exist (BasicConcepts.md is a 24-term subset of Glossary.md's 100). - Pages mix modes internally: RabbitMQConfiguration.md interleaves reference, explanation, how-to and guidance across 565 lines. - 0 of 110 pages carry page metadata; "## Configuration" is a heading on 22 pages, "## Best Practices" on 26. Five specs: - 009 Getting started tutorials: four-rung ladder ending in Kafka, which showcases the single-threaded pump's fit with consumer-group partition assignment. - 010 Information architecture: SUMMARY.md restructure, GitBook redirects, BasicConcepts/Glossary merge, llms.txt generation. - 011 Authoring conventions: page-type banners, subject-qualified headings, version markers, splitting mixed-mode pages, linter enforcement. - 012 Configuration reference: consistent option tables, verified against source by an automated checker rather than by hand. - 013 How-to guides: task-phrased recipes, including the PostgreSQL-for-both case the issue author asked for. Execution order is 011 -> 010 -> 012 -> 013, with 009 in parallel; spec numbers are identifiers, not a sequence. Also records a narrow, approved exception to the Brighter read-only rule: tutorial sample code may be added to ../Brighter/samples/ via pull request, so tutorial code is compiled by the source repo's CI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
Spec 009 (Getting Started Tutorials) and 011 (Authoring Conventions) now
have approved requirements and approved designs. No documentation pages
change here — this is specification only.
009 — a four-rung tutorial ladder, each rung backed by a CI-built sample:
1. First Command (in-process) 2. First Message (RabbitMQ)
3. Durable Outbox (Postgres) 4. Streaming with Kafka
All six technical-verification items are closed against Brighter source.
Notably, one pump is created per *performer*, not per partition
(Subscription.cs:201, Dispatcher.cs:589), so at the default noOfPerformers
of 1 a single process is assigned every partition and drains them on one
thread — which is what preserves per-key ordering.
Design review added two deliverables:
- D12: five glossary terms the tutorials link but that do not exist
(at-least-once, Box Provisioning, partition, consumer group, offset).
Absent, not merely thin, so acceptance criterion 8 fails without them.
- D9's version authority moves from ../Brighter/release_notes.md to the
NuGet API. CI has no sibling Brighter checkout, so the original source
made the gate unrunnable anywhere but a maintainer's laptop.
011 — page banners, a heading-uniqueness rule and tools/pagelint.py.
Two findings from its review shape the programme: the Docs repo has no CI
at all, and CLAUDE.md's File Organization Pattern contradicts the heading
convention it would introduce. Page splitting moved to spec 010, which is
already re-filing the same pages.
Measurements behind both specs are reproducible via modemix.py.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
These two files were a hack predating GitBook's support for multiple
versions. Neither is documentation: each held an H1 and a single line of
body ("Beginning of Version" / "End of Version"), nothing linked to
them, and they never appeared in SUMMARY.md.
linkcheck.py's NON_CONTENT set existed solely to keep them out of the
orphan check, so it goes with them. The orphan check now has no
exemptions at all: every page under contents/ must be reachable from
SUMMARY.md, which means a page that is not navigable has to be either
linked or deleted rather than quietly excused.
This also removes an edge case from spec 011's banner rule, which would
otherwise have needed its own notion of an exempt page. The corpus is
now 105 pages; linkcheck reports 107 files (105 + SUMMARY.md +
README.md) and is clean before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
tasks.md: 43 tasks across 7 phases, where the phases are design section 12's ten sequencing steps rather than an order invented here. The order is load-bearing, so the file says so and records the dependency graph, the parallelisable groups and traceability back to D1-D9. Two things the task list adds that the approved design predates: - The corpus is 105 pages, not 107, following the VersionBegin/VersionEnd deletion. requirements.md and design.md are updated throughout, with the reconciliation recorded in requirements section Measurements. The heading figures are untouched: neither file had an H2, so neither contributed to the 53 colliding texts or the 297 instances. Design step 3 now expects 105 banner errors, 256 cross-page, 34 within-page. - .gitbook.yaml contains two U+200B zero-width spaces, one immediately before "structure:" and one after "SUMMARY.md", so the key is literally "structure:". Given that malformed indentation disables GitBook redirects silently rather than erroring, Task 6.12 strips them rather than editing around them. Three tasks carry judgements rather than transcription: 5.3 shapes docs.yml for spec 009's versioncheck.py, including the daily schedule trigger D9 requires; 5.2 provokes the --changed step with a throwaway PR, since a step that finds no changed ranges passes vacuously; and 6.12 establishes whether the splits move any page URL at all before adding a redirects block, because both splits keep the Reference core under its original filename. A stop line after Phase 2 is written into the file: the first execution session runs Phases 1 and 2 only, so the linter's counts are compared against prediction before any sweep depends on them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
Scope change: this PR now touches
|
Six findings from the tasks-phase review, applied across all three documents: 1. Rules 3a/3b compare slug(), not raw heading text. GitBook strips emphasis when building anchors, so the four bold H2s on the outbox pages really do collide in the published URL. Two of them merge with plain twins (Configuration 22->26, NuGet Packages 5->9), moving the rule 3a text count from 50 to 48. Instances are 256 either way. Without this recorded, Phase 2's reconciliation would trip its own stop-rule over an unwritten normalisation choice. 2. Rule 3b's twelve pages enumerated in full. The design named ten and closed with "+ 2 more", making Task 2.8's page-by-page check unrunnable. The missing two are AWSSQSConfiguration.md and BrighterSchedulerSupport.md; InMemoryOptions.md also has a fourth repeat (Example Usage x3), so it needs 12 qualifications, not 10. 3. Task 4.5 (the three content merges) added to the traceability table. 4. Acceptance criteria renumbered 1-8; two were both numbered 6. 5. Tasks 2.4 and 2.6 are not parallel - both are branches of check_code_blocks, and 2.7 changes its dispatch. 6. md_files() is repo-wide, not contents/, so pagelint.py must filter itself. Confirmed linkcheck.py has a __main__ guard, so the "import, do not duplicate" plan works as designed. Tasks phase approved: .tasks-approved added. Next is Phase 1 - the repo's first CI workflow, then the CLAUDE.md amendment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
The Docs repository has had no CI. linkcheck.py has only ever run when someone remembered to type it, which is the explanation for the audit findings behind spec 011: every CLAUDE.md rule that is not machine- checked has decayed. This is the repository's first workflow, and it deliberately ships running linkcheck.py alone. Spec 011 adds a banner to all 105 pages and qualifies 290 headings; pagelint.py will fail loudly until those sweeps land, so wiring it in now would mean a red build with no baseline to compare against. A green run on an untouched tree is what makes every later failure attributable (spec 011 AC6). fetch-depth: 0 is present from the start even though nothing needs the merge-base until Task 5.1. pagelint.py --changed derives its strict ranges from git history, and a shallow clone fails there by finding no changed ranges - which passes vacuously rather than erroring. No third-party actions beyond checkout and setup-python, and both tools are standard-library only, so the build cannot go red for reasons unrelated to the documentation. Spec 011, Task 1.1 (design D5, step 1 of 10). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
Two edits, one commit. Shipping either alone leaves CLAUDE.md contradicting itself. The File Organization Pattern prescribed the page skeleton with unqualified headings - Configuration, Best Practices, Common Pitfalls, Sample Code. Four of those are among the worst collisions spec 011 exists to fix: Configuration and Best Practices each appear on 26 pages. An author following the documented standard produced a page the linter will reject. The skeleton and its ordering are unchanged; the banner takes position 2, and every section heading except the navigation allowlist now carries its subject. The new Page Conventions section covers all six subsections from design 2b: page banner, heading qualification, version markers on code, complete code blocks, llms.txt format, enforcement. BANNER_RE and the five-item navigation allowlist are quoted verbatim from the design, so this file and tools/pagelint.py cannot drift - the regex was checked against all five worked banners and against two malformed ones. The Enforcement subsection carries the ledger AC5 demands: every convention mapped to the rule that checks it, with both strictness levels stated. It records one deliberate gap rather than hiding it - version markers have no rule, because whether two code blocks differ by version is a judgement about meaning, and a regex guessing at it would fire on every before/after pair in the repo. That one is checked in review, and the ledger says so. The version-marker example is a real V9->V10 pair taken from V10MigrationGuide.md:144-167 (UseExternalBus/AddServiceActivator -> AddProducers/AddConsumers), not an invented one. Spec 011, Tasks 1.2, 1.3 and 1.4 (design D1, step 2 of 10). Phase 1 is now complete; next is Phase 2, tools/pagelint.py, run locally only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
Spec 011 Phase 2, Tasks 2.1-2.9 (design D4, step 3 of 10). Six rules: banner presence and grammar, heading uniqueness across pages (H2) and within a page (H2-H4), language tags, ServiceActivator in prose, using directives. Imports md_files, slug and HEADING_RE from linkcheck.py rather than duplicating them, and filters to contents/ itself, since md_files() is repo-wide and would otherwise report a missing banner on SUMMARY.md. Deliberately NOT added to docs.yml. It reports 412 errors on the untouched tree, which is the point; Task 5.1 wires it in once the sweeps have made it pass. Phase 2 exists to test the predicted counts before sweeping 105 pages, and it earned its keep. Rules 1, 2 and 3b reproduced their predictions exactly - 105 banners, 0 malformed, 34 within-page repeats verified page by page across the same 12 pages. Three figures did not, and all three were wrong in the prediction rather than the rule: 1. 143 C# fences are written ``` csharp, with a space. CommonMark trims the info string so they are C# blocks and render as such, but a script matching ^```csharp does not see them. This single fact explains two discrepancies at once: there are 940 C# blocks, not 796, and only 36 fenced blocks genuinely lack a language, not 185 - 36 + 149 space-separated reproduces the old 185 exactly. Consequences run both ways: P1 Task 7.2 shrinks from 185 fences to 36, while the using-directive debt grows to 804 blocks across 89 pages. Compliance is 14%, not 16%. 2. Rule 3a finds 262 instances across 50 texts, not 256 across 48. The raw comparison matched its prediction exactly (50/256), which is what made the gap diagnosable. The tasks-review finding was right that slug() merges the emphasised outbox headings into their plain twins, but missed that slug() also lowercases: Common pitfalls, NuGet packages, How It Works/How it works and **Configuring The Dispatcher**/Configuring the Dispatcher account for exactly +6. The last two were not collisions raw at all. 50 texts under both comparisons is a coincidence; the sets differ. 3. Rule 5 fires 11 times, none of them the error it describes. Eight are API identifiers written in bold where CLAUDE.md already asks for backticks; three are HowServiceActivatorWorks.md, the page about the name. Both fixes are recorded and must land before Task 5.1. No rule needed revising, so Phase 2's stop-rule is satisfied. tasks.md gains a Measured baseline section with the full reconciliation; design.md and requirements.md are corrected where the linter disproved them. Also verified rather than assumed: --changed strictness is genuinely per block (an edit between two blocks makes neither strict), changed_ranges parses multi-hunk diffs and raises rather than returning empty on an unresolvable ref, so the vacuous pass Task 5.2 warns about cannot happen silently. Fence tracking runs before heading extraction, so a "# Install packages" comment in a bash block is not read as an H1. .gitignore gains __pycache__, which pagelint.py now creates on every run by importing linkcheck.py. Phases 1 and 2 complete, 13 of 43 tasks. Stopping at the session boundary in tasks.md; next is Task 3.1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
Task 1.2 done. #67 is OPEN, last updated 2026-08-03T11:43:25Z, two comments, both the maintainer's: the acknowledgement and the follow-up pointing at #72. No reply from the issue author. Neither thing flagged there for pushback — Diátaxis-as-authoring-discipline, and the prose-vs-generated reference distinction — has drawn one, so nothing external constrains the design. Re-check at Task 2.8 rather than treating this as settled: PR 2 is the merge that moves 74 public URLs, and it is where a late objection would cost the most. The issue stays open until the work lands. Task 1.1 was ratified 2026-08-08 and is ticked here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
Adds five documentation specifications responding to #67, which argues that the
documentation mixes Diátaxis modes and is organised around Brighter's architecture
rather than around what a reader is trying to do.
No documentation pages change in this PR — these are the specs that will govern that
work, opened early so the plan can be reviewed (and commented on by the issue author)
before writing starts.
Audit findings behind the specs
ShowMeTheCode.md, which opens by saying "It's not about how... It's not about why"), a glossary, and a philosophy piece. None gets a reader to something running.BasicConcepts.md(24 terms) is a subset ofGlossary.md(100).RabbitMQConfiguration.mdinterleaves reference, explanation, how-to, and guidance over 565 lines.## Configurationis a heading on 22 pages,## Best Practiceson 26; ~230 of ~1,050 C# blocks includeusingdirectives.The specs
SUMMARY.mdrestructure, GitBook redirects,BasicConcepts/Glossarymerge,llms.txtExecution order is 011 → 010 → 012 → 013, with 009 in parallel throughout. Spec
numbers are identifiers, not a sequence.
Decisions
The issue recommended using Diátaxis. We apply it here as page-level discipline, not as literal top-level navigation.
A four-bucket split would shred each technology into four pages across 7 transports, 8 outboxes, 6 inboxes, 7 schedulers and 7 distributed locks. Readers navigate that material by technology, so those families stay grouped and get within-page mode discipline instead.
Front matter is ruled out. GitBook's documentation states that
.gitbook.yaml, not front matter, is the supported route, and GitbookIO/gitbook#1079 reports front matter rendering literally into the page body. Spec 011 uses a visible banner below the H1 instead — which readers can actually see, and which survives the chunking that retrieval systems apply.Also, tutorial sample code may be added to
../Brighter/samples/via pull request, so tutorial code is compiled and kept honest by the source repository's CI.src/,tests/, ADRs, and release notes remain strictly read-only.Closes nothing yet — #67 stays open until the work lands.