Skip to content

How the docs are written

The docstrings are the source of truth. Part three of this book is compiled from them, and help(ps.Perforate) and ps.help(ps.Perforate) read them, so a docstring is not a comment about the surface: it is the surface’s documentation, shipped, and the first thing an agent reads when it meets a name no lesson taught.

ps.help(ps.Perforate) # the fields, the card it writes, where it is held
assert "Parameters" in (ps.Perforate.__doc__ or "")

Style: NumPy. Parameters, Returns, Raises, Notes, Examples, in that order. Prose whose truth is dated, a measured number or a “today”, lives in Notes, where a reader expects to check the date. NumPy style suits an argument-dense scientific surface, aligns with the ecosystem, and its rules are mechanical enough for a test to hold.

Written for a reader with no other context. The summary line says what the thing is in reservoir terms. The body names the deck card it writes or loads and where the model holds it (its address, as m.get and m.set read it). Each parameter has its type and, for a number, its unit kind in the model’s unit system; Returns says what comes back; Raises names the refusal codes.

House rules on top of the style. One term per concept: the table in part four is committed as data and a law holds this book’s own prose to it. Short sentences, active voice, present tense. Our own vocabulary throughout: this documentation describes this surface and this product’s measured behaviour, and paraphrases no simulator manual; where it points at one, it points by section number.

Enforcement is a law over the public surface. tests/test_docstring_law.py walks every public class, function, method and property PetroScript has — a method only where its class defines it, so a method a private base defines is checked nowhere — and validates each with the format checks [tool.numpydoc_validation] in pyproject.toml lists: parameter and returns quality (PR02–PR10, RT02–RT05), the summary rules (SS02–SS06), and section naming and order (GL05–GL07). On functions and methods it adds three presence checks: PR01 (every parameter documented), RT01 (a Returns section where the body returns a value) and RS01 (a Raises section naming each refusal code, the catalog’s or the bundle door’s bundle.*, that the body’s own raise statements spell as a literal; a code a helper raises is not seen). RS01 is the house’s own: the reference tool has no Raises check. A class’s fields are the meanings law’s, not this one’s.

What fails today is a ledger that may only shrink. tests/docstring_ledger.txt holds one name CODE row per failing check, a line each, sorted under its namespace’s [header]. A failure with no row is red: a new name lands passing. A row whose name now passes is red until it is deleted, so the ledger stays exact. The format list may only grow; the law fails by name if an entry leaves it.

An example is a claim, so it runs. tests/test_docstring_examples.py runs the Examples section of every name the law walks, each docstring’s >>> lines in a fresh python, from a folder that holds what they read: tests/results_fixtures/QUICKSTART, the quickstart’s deck and the run the pinned image wrote of it (a stand-in hands that run to deck.run()). An example reads that deck by its path from the repository’s root and states the model’s units beside its numbers. The entry points agents use most each carry one, and the test names any that loses it.

The checker is ours, and the check codes are not. A law in this repository keeps documentation build tooling out of the dependency list, and the off-the-shelf validator wants some. So the checks are PetroScript’s own, in its test suite, and each records the standard code it mirrors. The codes in force are listed under [tool.numpydoc_validation] in pyproject.toml: section naming and order (GL05–GL07), the parameter and returns quality family (PR02–PR10, RT02–RT05) and the summary rules (SS02–SS06). Six codes are not implemented (GL03, GL09, GL10, SA02–SA04): they need the parts of the reference grammar whose faithful reimplementation costs more than they catch, and that is recorded at the checker, so adding one later is a decision rather than a discovery.

Two checks are excluded by decision. GL01 (the summary starts on the line after the opening quotes) and GL02 (closing quotes on their own line) contradict this codebase’s docstring shape, which puts the summary on the opening-quote line and may close the quotes on the last text line. A check that fights the house style is disabled by name with its reason recorded, never dropped quietly.

The ledger is a burn-down, and this is its opening count. Measured 2026-10-16 over the 535 public names, once every format failure was fixed: 474 rows, all presence — 240 RT01, 229 PR01 and 5 RS01. 396 of them are 198 classes’ replace, whose **fields has no Parameters entry and which has no Returns section.

Two consequences for anyone adding to the product:

  • a new public name lands with a docstring that passes every check the law enforces, and rebuilding this book (python tools/build_book.py) is part of landing it: the book’s laws fail by name otherwise;
  • to clear rows, fix the docstrings, run pytest tests/test_docstring_law.py, delete exactly the rows it names as passing and keep the headers (so work on different namespaces never edits adjacent lines), then rebuild this book;
  • a docstring that says something the code does not do is worse than none, because every agent that reads it learns the wrong thing: the docstring follows the code.

PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt