Skip to content

docs: specs 009-013 — documentation restructure in response to #67 - #72

Merged
iancooper merged 8 commits into
masterfrom
docs-restructure-specs
Aug 4, 2026
Merged

docs: specs 009-013 — documentation restructure in response to #67#72
iancooper merged 8 commits into
masterfrom
docs-restructure-specs

Conversation

@iancooper

@iancooper iancooper commented Aug 3, 2026

Copy link
Copy Markdown
Member

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

  • No tutorial exists. The Overview section is a showcase (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.
  • Transports are filed under a delivery guarantee — RabbitMQ, Kafka, SNS/SQS and Azure Service Bus live under "Guaranteed At Least Once".
  • Explanatory material is scattered across five separate sections.
  • "Reference" contains one page, and we ship two glossaries: BasicConcepts.md (24 terms) is a subset of Glossary.md (100).
  • Pages mix modes internallyRabbitMQConfiguration.md interleaves reference, explanation, how-to, and guidance over 565 lines.
  • Machine-readability: 0 of 110 pages carry page metadata; ## Configuration is a heading on 22 pages, ## Best Practices on 26; ~230 of ~1,050 C# blocks include using directives.

The specs

Spec Scope
009 Getting Started Tutorials Four-rung ladder: in-process command → RabbitMQ → durable Postgres Outbox → Kafka
010 Information Architecture SUMMARY.md restructure, GitBook redirects, BasicConcepts/Glossary merge, llms.txt
011 Authoring Conventions Page-type banners, subject-qualified headings, version markers, splitting mixed-mode pages, linter
012 Configuration Reference Consistent option tables, verified against source by an automated checker
013 How-To Guides Task-phrased recipes, including the PostgreSQL-for-both case from #67

Execution 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.

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
@iancooper iancooper self-assigned this Aug 3, 2026
@iancooper
iancooper marked this pull request as draft August 3, 2026 14:16
iancooper and others added 3 commits August 3, 2026 19:41
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
@iancooper

Copy link
Copy Markdown
Member Author

Scope change: this PR now touches contents/ and tools/

The description above says "No documentation pages change in this PR." That is no longer
true, and the change is small but worth signposting rather than leaving to be found in
the file list.

contents/VersionBegin.md and contents/VersionEnd.md deleted (93cd9b7)

Both were a hack predating GitBook's support for multiple versions. Neither was
documentation: each held an H1 and a single line of body ("Beginning of Version" / "End of
Version"), nothing linked to either, and neither appeared in SUMMARY.md.

tools/linkcheck.py's NON_CONTENT set existed solely to keep these two 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. linkcheck.py is
clean before and after.

This also removes an edge case from spec 011's banner rule, which would otherwise have
needed its own notion of an exempt page — "does this file count as a page?" is exactly the
kind of ambiguity a linter should not have to carry.

The corpus is 105 pages, not 107 (3b783a3)

Spec 011's requirements and design are updated throughout, with the reconciliation recorded
in requirements.md § Measurements. The heading figures are unaffected — neither deleted
file had an ## heading, so neither contributed to the 53 colliding heading texts or the
297 instances. The expected linter output on the untouched tree is now 105 banner
errors, 256 cross-page heading errors and 34 within-page errors; those counts are
the test that the rules match the corpus, so getting them right before the sweep matters.

Spec 011 now has a task list

spec/011-authoring_conventions/tasks.md — 43 tasks across 7 phases, where the phases are
the design's own sequencing rather than an order invented for the task list. Two findings
in it postdate the approved design and are recorded at the top of the file: the page count
above, and the fact that .gitbook.yaml contains two U+200B zero-width spaces — one
immediately before structure:, so the key is literally ​structure:. Since malformed
indentation disables GitBook redirects silently rather than erroring, and redirects are
what preserve existing links through the restructure (#67), that one is worth knowing
before anyone edits the file.

Note on the numbers in the description above

Two figures in the audit list were superseded when spec 011 re-measured by script:
"0 of 110 pages carry page metadata" is 0 of 105, and "~230 of ~1,050 C# blocks include
using directives" is 133 of 796 (16%) — the earlier count included every fenced
block, not just C#-tagged ones. The direction is the same and compliance is worse than
first stated. Full tables in spec/011-authoring_conventions/requirements.md.

Still no page content has changed, and #67 stays open until the work lands.

iancooper and others added 4 commits August 3, 2026 21:36
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
@iancooper
iancooper marked this pull request as ready for review August 4, 2026 09:19
@iancooper
iancooper merged commit ee8c0d5 into master Aug 4, 2026
4 checks passed
iancooper added a commit that referenced this pull request Aug 8, 2026
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant